|
3 | 3 | import logging |
4 | 4 | import os |
5 | 5 | import threading |
| 6 | +import time |
6 | 7 | from pathlib import Path |
7 | 8 | from typing import TYPE_CHECKING, Callable, Optional |
8 | 9 |
|
@@ -66,6 +67,14 @@ def __init__( |
66 | 67 | self._icon: Optional[_Icon] = None |
67 | 68 | self._thread: Optional[threading.Thread] = None |
68 | 69 |
|
| 70 | + # Double-click detection: pystray Win32 backend has no native |
| 71 | + # double-click (window class style 0 lacks CS_DBLCLKS, _on_notify |
| 72 | + # only handles WM_LBUTTONUP / WM_RBUTTONUP). Use timing-based |
| 73 | + # detection by wrapping Icon.__call__ (invoked on WM_LBUTTONUP) |
| 74 | + # and measuring interval between activations. |
| 75 | + self._last_click_ts: float = 0.0 |
| 76 | + self._DOUBLE_CLICK_INTERVAL: float = 0.4 # seconds (<400 ms) |
| 77 | + |
69 | 78 | # --- menu actions ------------------------------------------------- |
70 | 79 |
|
71 | 80 | def _toggle_pause(self, icon, item): |
@@ -196,7 +205,30 @@ def _build_menu(self) -> pystray.Menu: |
196 | 205 | return pystray.Menu(*items) |
197 | 206 |
|
198 | 207 | def start(self): |
199 | | - icon = pystray.Icon( |
| 208 | + # Double-click handling: pystray Win32 backend has no native |
| 209 | + # double-click support (window class style=0 lacks CS_DBLCLKS and |
| 210 | + # _on_notify only dispatches WM_LBUTTONUP/WM_RBUTTONUP). Implement |
| 211 | + # timing-based detection by intercepting Icon.__call__ (invoked on |
| 212 | + # WM_LBUTTONUP) — two activations within <400 ms are treated as a |
| 213 | + # double-click that reloads config; single click delegates to the |
| 214 | + # original Icon.__call__ to preserve default menu behavior. |
| 215 | + controller = self |
| 216 | + |
| 217 | + class _SRRIcon(pystray.Icon): # type: ignore[misc] |
| 218 | + def __call__(self): # type: ignore[override] |
| 219 | + now = time.monotonic() |
| 220 | + if now - controller._last_click_ts < controller._DOUBLE_CLICK_INTERVAL: |
| 221 | + logging.info("tray: double-click detected — reloading config") |
| 222 | + controller._last_click_ts = 0.0 |
| 223 | + try: |
| 224 | + controller._on_reload() |
| 225 | + except Exception as e: |
| 226 | + logging.warning(f"tray double-click reload failed: {e}") |
| 227 | + return |
| 228 | + controller._last_click_ts = now |
| 229 | + return super().__call__() |
| 230 | + |
| 231 | + icon = _SRRIcon( |
200 | 232 | self.project_name, |
201 | 233 | self._icon_image, |
202 | 234 | f"SRR — {self.state_text}", |
|
0 commit comments