Skip to content

PowerDisplay: Draw the tray icon's hover UI instead of the Shell tooltip - #49555

Closed
moooyo wants to merge 1 commit into
mainfrom
yuleng/powerdisplay/tray-hover-ui/1
Closed

PowerDisplay: Draw the tray icon's hover UI instead of the Shell tooltip#49555
moooyo wants to merge 1 commit into
mainfrom
yuleng/powerdisplay/tray-hover-ui/1

Conversation

@moooyo

@moooyo moooyo commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary of the Pull Request

Carved out of #49446 so the tray-icon plumbing can be reviewed on its own, ahead of the mouse-wheel feature that motivated it.

This moves the PowerDisplay notification-area icon onto NOTIFYICON_VERSION_4 and gives it its own hover presentation, replacing the standard Shell tooltip. The reason for doing that at all is that the Shell tooltip cannot be shown on demand — so a feature that wants to attach a transient readout to the icon (in #49446: the brightness percentage as you scroll) has nowhere to put it.

UpdateAdjustmentFeedback(string?) is the seam #49446 then builds on. Formatting the text is deliberately the caller's job, so wording and its localized resources stay with whatever gesture produced them rather than leaking into the tray plumbing.

Dependency direction: #49446 will be rebased onto this branch. Nothing here depends on #49446.

PR Checklist

  • Closes: #xxx
  • Communication: I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected
  • Tests: Added/updated and all pass
  • Localization: All end-user-facing strings can be localized
  • Dev docs: Added/updated
  • New binaries: Added on the required places
  • Documentation updated: If checked, please file a pull request on our docs repo and link it here: #xxx

Communication is unchecked on purpose — see Open question below. No new binaries or projects: everything lands in existing assemblies.

Detailed Description of the Pull Request / Additional comments

Registration and recovery. Nothing polls a healthy registration. A lost icon is reported by the TaskbarCreated broadcast, by a failing Shell_NotifyIconGetRect on the next hover, or by the settings-update path, and each schedules a check through TrayIconRegistrationBackoff (250 ms → 5 s, capped) that keeps retrying while NIM_ADD fails. The health check uses Shell_NotifyIconGetRect, which also succeeds for an icon parked in the notification overflow, so a hidden icon is not mistaken for a lost one.

NOTIFYICON_VERSION_4. The callback packing differs between versions — the id moves to HIWORD(lParam), the event to LOWORD(lParam), and the Shell supplies an anchor point in wParam — so DispatchTrayNotification decodes according to whether NIM_SETVERSION actually succeeded, and the legacy path is kept working. Version 4 adds NIN_SELECT, NIN_KEYSELECT and WM_CONTEXTMENU while still forwarding the raw button messages, so the legacy cases are suppressed while it is active; handling both would open the context menu twice and toggle the window twice.

Hover UI. NIF_TIP without NIF_SHOWTIP stops the Shell drawing its tooltip and lets TrayWheelFeedbackWindow present instead — a no-activate (WS_EX_NOACTIVATE), click-through (WS_EX_TRANSPARENT + HTTRANSPARENT) TransparentWindow.

Moment Overlay shows
500 ms after the pointer enters the icon The app name
UpdateAdjustmentFeedback(text) That text, immediately
2 s after the last readout Back to the app name

Open question for reviewers

Replacing the Shell tooltip has two consequences that are deliberate, documented in design.md and in EnsureTrayIconIdentity, and that I think need a maintainer's call before this ships:

  1. Keyboard and touch lose the visible tooltip. The overlay is only presented when GetCursorPos lands inside the icon rectangle, so focusing the icon from the keyboard shows nothing. szTip is still supplied, so the UI Automation name and the overflow-flyout label are unaffected — but there is no visible hover text for those input methods. This is a real capability regression, not an implementation gap.
  2. Cost for everyone. The overlay is created lazily on first hover and kept for the process lifetime, so the icon now costs a XAML window and a 250 ms hover watchdog it did not cost before.

An alternative considered and rejected: keep NIF_SHOWTIP and only use the overlay for the transient readout. That preserves the native tooltip for all input methods, but means two hover presentations to maintain and keep from diverging.

Also worth a reviewer opinion: the TrayWheelFeedback* type names come from the motivating consumer in #49446. Happy to rename them to TrayHoverFeedback* if preferred.

Validation Steps Performed

  • Build: PowerDisplay, x64 Debug — clean, no warnings.
  • Unit tests: PowerDisplay.Lib.UnitTests 219 passed. Placement, hover timing, rectangle arithmetic and the backoff sequence are pure logic in PowerDisplay.Lib and are covered there, including negative-origin secondary monitors, the notification overflow, taskbar-edge selection, and work-area clamping.
  • The Win32 and WinUI glue in TrayIconService and TrayWheelFeedbackWindow is not unit tested.

Not yet verified on hardware, and called out so a reviewer can weigh it:

  • Whether the Shell emits NIN_POPUPOPEN on keyboard focus. If it does, the overlay could be anchored to the icon rectangle without the cursor check, which would close consequence (1) above while keeping a single presentation.
  • Non-bottom taskbar edges (Windows 11 no longer allows moving the taskbar, so this needs Windows 10).
  • Mixed-DPI overlay placement, which needs two displays at different scale factors.

Moves the notification-area icon onto NOTIFYICON_VERSION_4 and gives it its
own hover presentation, so a caller can attach a transient readout to the
icon - something the standard Shell tooltip cannot do, because it cannot be
shown on demand.

Registration and recovery. Nothing polls a healthy registration. A lost icon
is reported by the TaskbarCreated broadcast, by a failing
Shell_NotifyIconGetRect on the next hover, or by the settings-update path,
and each schedules a check through TrayIconRegistrationBackoff
(250 ms to 5 s, capped) that keeps retrying while NIM_ADD fails. The health
check uses Shell_NotifyIconGetRect, which also succeeds for an icon parked in
the notification overflow, so a hidden icon is not mistaken for a lost one.

NOTIFYICON_VERSION_4. The callback packing differs between versions - the id
moves to the high word of lParam, the event to the low word, and the Shell
supplies an anchor point in wParam - so DispatchTrayNotification decodes
according to whether NIM_SETVERSION actually succeeded. Version 4 adds
NIN_SELECT, NIN_KEYSELECT and WM_CONTEXTMENU while still forwarding the raw
button messages, so the legacy cases are suppressed while it is active;
handling both would open the context menu twice.

Hover UI. NIF_TIP without NIF_SHOWTIP stops the Shell drawing its tooltip and
lets TrayWheelFeedbackWindow present instead: a no-activate, click-through
TransparentWindow showing the app name after a 500 ms hover, and whatever
UpdateAdjustmentFeedback is given for two seconds after that. Formatting that
text is deliberately the caller's job, so wording and its localized resources
stay with whatever gesture produced them rather than leaking into the tray
plumbing.

Two consequences of replacing the tooltip are deliberate, and are called out
in the module design doc and in EnsureTrayIconIdentity:

- The overlay is created lazily on first hover and kept for the process
  lifetime, so the icon costs a XAML window and a 250 ms hover watchdog it
  did not cost before.
- A hover that never involves the cursor - keyboard or touch focus in the
  notification area - shows no text at all, because the overlay is only
  presented when GetCursorPos lands inside the icon rectangle. szTip is still
  supplied, so the UI Automation name and the overflow flyout label are
  unaffected, but there is no visible hover text for those input methods.

Placement, hover timing, rectangle arithmetic and the backoff sequence are
pure logic in PowerDisplay.Lib with unit tests. The Win32 and WinUI glue is
not unit tested.
@github-actions github-actions Bot added Area-Localization issues regarding to Localization the application Product-PowerDisplay labels Jul 29, 2026
@moooyo

moooyo commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Closed due to no enough motivation to push this impl for a very tiny feature.

@moooyo moooyo closed this Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Localization issues regarding to Localization the application Product-PowerDisplay

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant