Repository navigation
feat(tray): quick-access panel on tray icon left click - #146
Conversation
A left click on the tray icon now opens a borderless acrylic panel with connect/disconnect, the current node, smart/global routing, and the system proxy / TUN switches. A double-click still restores the main window. The panel takes activation on the first click, so Windows can miss the second click of a double-click. The double-click is therefore recognised in-app: a second click near the same point within the double-click time closes the panel and restores the window. RestoreFromTray now also calls SetForegroundWindow, because Activate alone leaves a background tray app behind the taskbar. Dialogs raised while the window is hidden or under the panel bring the full window back first, so they are not left unseen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 51de2d52a3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| panel.LightDismissed += (_, _) => | ||
| { | ||
| if (IsSecondTrayClick()) | ||
| CloseTrayPanelAndRestore(); |
There was a problem hiding this comment.
Gate restoration on an actual second left-click
When the panel loses activation within the double-click interval while the pointer remains over the tray icon, this handler treats any light dismiss as a second click because IsSecondTrayClick checks only elapsed time and cursor position. For example, right-clicking the icon to open its context menu or quickly pressing Alt+Tab after opening the panel restores the full main window and steals focus even though no second left-click occurred. Track an actual tray-button event rather than inferring one from every deactivation.
Useful? React with 👍 / 👎.
| var restingY = work.Y + work.Height - margin - visibleBottom; | ||
| StartSlide(restingY, SlideInMs, decelerates: true, OnShowCompleted); |
There was a problem hiding this comment.
Anchor the panel to the actual taskbar edge
The project supports Windows 10, where users can dock the taskbar to the top, left, or right, but this always computes the resting position from the bottom of the primary work area (and the adjacent code always anchors it to the right). In those supported layouts the panel opens in an unrelated bottom-right corner and slides from the bottom of the display rather than from behind/above the notification area. Derive both the edge and coordinates from the taskbar or tray-icon rectangle.
Useful? React with 👍 / 👎.
What changed
Views/TrayPanelWindow,Views/TrayPanelControl,ViewModels/TrayPanelViewModel): left-clicking the tray icon opens a borderless acrylic panel above the icon. It has start/stop, the current node with protocol and status, a smart/global routing switch, system proxy and TUN toggles, and buttons for the main window and exit. Clicking the icon again closes it, and so does a light dismiss.ActiveAcrylicBackdrop: a customSystemBackdrop. The stockDesktopAcrylicBackdropdrops to a flat fallback colour as soon as the window loses activation, and that is exactly when the panel's slide-out starts. The stock backdrop can also get light/dark wrong when the app theme is forced against the system theme.IsSecondTrayClicktherefore detects a second click within the double-click time, near the same point, on both the shell's click report and the panel's light dismiss.RestoreFromTraynow callsSetForegroundWindow:Activate()does not bring a background tray app to the front, so the window sometimes only flashed on the taskbar.DialogServicenow resolves its XamlRoot throughGetDialogXamlRoot. If the window is in the tray or under the panel, it closes the panel and shows the full window before the dialog opens. Example: the TUN elevation prompt started from the panel.MainViewModel.DisplayedServer, a publicControlPanelViewModel.IsCustomConfigActivewith change notification, andShowForJumpListrenamed toShowFullWindow. New strings are in both zh-CN and en-US.resw.Why
Right now any tray click restores the full window, even for quick actions like toggling the proxy or switching routing mode. The panel handles those without leaving the tray.
Reviewer notes
build-x64CI job covers the publish, and a real run of the AOT build is worth doing.🤖 Generated with Claude Code