feat: add tray layout controls

This commit is contained in:
Steve Cliff
2026-08-20 09:31:15 +01:00
parent 45ee82692d
commit cb00340c55
10 changed files with 523 additions and 31 deletions
+33 -6
View File
@@ -18,6 +18,8 @@ The project uses pure Go and sets `CGO_ENABLED=0`. No C compiler, Windows SDK,
```text
cmd/fancywin/ CLI entry point
assets/fancywin.png Full-resolution generated icon source
internal/assets/ Embedded runtime image assets
internal/config/ YAML model, loading, and validation
internal/layout/ Platform-neutral zone geometry
internal/platform/platform_windows.go Win32 backend
@@ -56,8 +58,10 @@ The script:
5. Writes `dist\fancywin.exe` and copies the example to
`dist\fancywin.yaml`.
The generated executable is a console subsystem application so users can see
configuration and Win32 errors directly.
The generated executable remains a console-subsystem application so diagnostics
work without a second binary. Normal execution calls `FreeConsole` and operates
through the notification-area icon. `--debug` retains the console, while
startup failures in detached mode are shown with `MessageBoxW`.
## Cross-compiling
@@ -138,6 +142,13 @@ Test both x64 and ARM64 where hardware is available:
same-integrity elevated run can do so.
11. Disconnect and reconnect a monitor and confirm current work-area information
is used for subsequent moves.
12. Confirm the custom icon is crisp in the normal and overflow notification
areas at 100%, 150%, and 200% scaling.
13. Select each named layout from the tray, confirm the check mark and overlay
change immediately, and verify `active_layout` changes without disturbing
YAML comments or layout formatting.
14. Right-click the tray icon, select `Exit`, and confirm the process and icon
both disappear cleanly.
## Architecture
@@ -153,13 +164,29 @@ At startup the backend:
1. Requests per-monitor-v2 DPI awareness.
2. Creates a named mutex for single-instance enforcement.
3. Installs a `SetWinEventHook` covering `EVENT_SYSTEM_MOVESIZESTART` through
3. Creates a hidden window and registers a notification-area icon with
`Shell_NotifyIconW`.
4. Installs a `SetWinEventHook` covering `EVENT_SYSTEM_MOVESIZESTART` through
`EVENT_SYSTEM_MOVESIZEEND` with `WINEVENT_OUTOFCONTEXT` and
`WINEVENT_SKIPOWNPROCESS`.
4. Registers configured system-wide hotkeys against the current thread.
5. Creates a two-second timer for configuration reload checks and a 25 ms timer
5. Registers configured system-wide hotkeys against the current thread.
6. Creates a two-second timer for configuration reload checks and a 25 ms timer
for sampling the mouse button, Shift, and active drag state.
6. Enters a standard Win32 `GetMessage` loop.
7. Enters a standard Win32 `GetMessage` loop.
The tray icon is a generated FancyWin mark embedded as a 64×64 RGBA PNG. At
startup the standard-library PNG decoder supplies premultiplied BGRA pixels to a
32-bit GDI DIB section, which `CreateIconIndirect` converts to an alpha-aware
native `HICON`; it never needs to be extracted to disk. A stock Windows icon is
used only if conversion unexpectedly fails.
The tooltip contains the active layout. The context menu deduplicates configured
layouts by case-insensitive name, checks the active one, and provides `Exit`.
Selecting a layout updates runtime state immediately and rewrites only the
top-level `active_layout` YAML line so the choice persists. `Exit` posts
`WM_QUIT` to the existing message loop for orderly hook, hotkey, overlay, icon,
and tray cleanup. A registered `TaskbarCreated` message restores the icon after
Windows Explorer restarts.
### Mouse snapping