Add minimize to system tray#789
Conversation
|
The issue with all the systray solutions LLM's come up with is that they don't understand Gremlin's constraints well. As a result they always want to use QtWidget's based solutions which will work right now, but once the input viewer is reworked nothing in Gremlin requires QtWidgets and they will be dropped. As such adding new components that requires that won't work. There is a QML system tray icon but that falls back onto QtWidgets, making it not suitable. This effectively leaves a solution that directly uses Window's systray interface via pywin32 which Gremlin requires as a dependency anyway. |
76e91ef to
d508e36
Compare
|
Reworked to use the Win32 A hidden helper window handles the icon's mouse/menu messages (its wndproc is driven by Qt's running event loop), and window show/hide still goes through the QML |
|
Ran it through my usual merge steps, the LLM comes up with these things it thinks may be troublesome: 0. remove() double-deletes the tray icon, and _on_destroy is a confused fallbackremove() calls NIM_DELETE, then DestroyWindow. DestroyWindow sends WM_DESTROY synchronously, which dispatches to _on_destroy, which calls NIM_DELETE again. The second call silently fails (the icon is already gone), but the two methods now share responsibility for the same side-effect. The intent of _on_destroy is clearly a defensive fallback for abnormal window destruction — it should be the only place NIM_DELETE lives, and remove() should just call DestroyWindow. 1. Upgrade to NOTIFYICON_VERSION_4The current code uses the pre-Vista notification protocol. Calling NIM_SETVERSION after NIM_ADD unlocks: Cursor position packed into wparam (eliminates the GetCursorPos call) NIN_SELECT for keyboard-driven activation (accessibility) 2. Make the helper window invisible to the shellThe current CreateWindow uses WS_OVERLAPPED (= 0) at zero size, which works but relies on the window being invisible by accident. A zero-size overlapped window can briefly flicker in the taskbar on some DWM configurations. Use reateWindowEx with WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE to explicitly exclude it from the taskbar and Alt+Tab. 3. Add NIF_SHOWTIP to the notification dataNIF_SHOWTIP (added in Vista) opts into the modern rich tooltip and ensures the tip text shows even when the user has balloon tips suppressed by group policy. Probably giving these files as context to an LLM will make it understand these inputs: |
Restores the pre-R14 minimize-to-tray behaviour as an opt-in option. When 'minimize-to-tray' is enabled, minimizing the window hides it to a tray icon instead of the taskbar; clicking the icon restores it. The tray menu offers profile activate/deactivate and quit (via the window's close path so unsaved changes are still flagged), and the icon reflects the active state. Implemented directly against the Win32 Shell_NotifyIcon API via pywin32 (already a Gremlin dependency) rather than QtWidgets' QSystemTrayIcon, so it adds no QtWidgets dependency. Window show/hide goes through the QML QWindow (QtGui). Self-contained in gremlin/ui/tray.py plus the option registration and tray creation in joystick_gremlin.py.
- remove() no longer double-deletes the icon: DestroyWindow triggers the WM_DESTROY handler, which is now the sole owner of NIM_DELETE - opt into NOTIFYICON_VERSION_4 (NIM_SETVERSION after NIM_ADD) and mask the low word of lparam in the event handler, since v4 packs the icon id into the high word; also handle NIN_SELECT / NIN_KEYSELECT / WM_CONTEXTMENU for keyboard-driven activation and menu - create the helper window with WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE so the zero-size window can't flicker in the taskbar or steal focus - add NIF_SHOWTIP so the tooltip shows even when balloon tips are suppressed
The top tray-menu entry now reads Hide/Show depending on whether the window is currently visible, mirroring the Activate/Deactivate entry, so the menu can both stash the window into the tray and bring it back.
d508e36 to
17e5174
Compare
|
Thanks for the detailed review — addressed all four in a follow-up commit, rebased on current
pywin32 doesn't expose There's also one small enhancement in a separate commit: the top menu entry now toggles Hide/Show based on window visibility (mirroring the Activate/Deactivate entry) so the menu can both stash the window into the tray and bring it back. Happy to drop that commit if you'd rather keep this PR to just the fixes. |
Restores the pre-R14 minimize-to-tray behaviour as an opt-in option.
What
minimize-to-trayoption (off by default). When enabled, minimizing the main window hides it to a tray icon instead of the taskbar; clicking the icon restores and focuses it.QSystemTrayIcon.isSystemTrayAvailable().Backend.activityChanged) and quit — quit routes through the window's normal close path so the unsaved-changes prompt still fires.Notes
gremlin/ui/tray.py;joystick_gremlin.pyonly registers the option and creates the icon. No behaviour change when the option is off.gfx/icon*.icoassets andBackendsignals; nothing added tosignal.