Win11: reserve space in the system tray for the taskbar window - #2387
Open
mackid1993 wants to merge 2 commits into
Open
Win11: reserve space in the system tray for the taskbar window#2387mackid1993 wants to merge 2 commits into
mackid1993 wants to merge 2 commits into
Conversation
Adds an option that stops taskbar icons from overlapping the taskbar window by reserving room for it. This matters most when the taskbar is completely full, where the window previously ended up sitting on top of task buttons. The Win11 taskbar is a two-column layout in which the task button strip's right edge is the notification area's left edge. Widening the tray makes the strip repack to stay clear of it, and the strip never encroaches on the tray. Space is therefore reserved with fully transparent placeholder icons registered through Shell_NotifyIcon. Placeholder task buttons cannot do this: once the taskbar fills up Windows demotes the newest buttons into the overflow flyout, so they yield no space at all. Newly registered tray icons default into the overflow flyout, where they reserve nothing, so each placeholder is marked always-visible. That is the same value behind Settings > Personalisation > Taskbar > "Other system tray icons". The entries are removed again when the option is switched off or the app exits, and a sweep at startup clears any left behind by a previous run that did not exit cleanly. The placeholders' on-screen positions are read with UI Automation on a background thread, because its cross-process calls pump the message queue and would re-enter the UI thread. A WINEVENT_OUTOFCONTEXT hook wakes that thread on layout changes. Nothing is injected into any other process. Tray icons can be reordered by dragging, so another program's icon can land between two placeholders, inside the reserved region. Placing the window there would cover that icon, leaving it neither visible nor clickable. The query therefore enumerates every taskbar button rather than only our own, and hides the window while anything foreign overlaps the region; it reappears by itself once the icon is dragged away. The reserved width is driven by the window's measured width, so it scales with however many display items are configured. The per-icon slot width is seeded from a DPI-scaled guess and then corrected from the icons actually on screen, narrowing only, which converges instead of oscillating. The window holds its position while the reservation is still filling in, so that it does not skitter across the taskbar as each placeholder widens the tray. That hold only applies once the window has been placed normally at least once: otherwise a freshly built dialog would hold its initial rect, pinning the window to the far left of the taskbar on top of the Start button. An Explorer restart destroys and rebuilds the dialog and took exactly that path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Author
|
I also built binaries for testing: https://github.com/mackid1993/TrafficMonitor/releases/tag/v1.86-reserve-taskbar-space |
|
Thank you so much! You’ve resolved the issue that has been troubling me for years. |
Author
|
I'm glad this helped someone. That made my day! Hopefully the maintainer will consider merging this one day! It's a nice little workaround for Windows 11. |
The reserved region is sized from an estimate of how wide one tray icon slot is. That estimate is seeded from the DPI and then corrected from the icons actually on screen, narrowing only, which converges instead of oscillating. It was never reset when the DPI changed, and it was seeded from the system DPI rather than the DPI of the monitor the taskbar is on. Attaching a differently scaled display - a docking station, typically - therefore kept the pitch measured on the previous screen. Because the estimate can only narrow, it could not climb back: docking to a lower DPI left it believing icons were still wide, so too few placeholders were added and the region never became wide enough to hold the window, and the reverse over-reserved a wide strip of the tray. Either way the window no longer sat where the region actually was. The current monitor's DPI is now supplied by the caller, which already tracks it through CTaskBarDlg::SetDPI, and a change clears the measured pitch and invalidates the region so both are established again at the new scale. DPI is also part of the set of values that force the window to be re-placed, since attaching a monitor does not necessarily change the taskbar geometry that was otherwise the only trigger. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Author
|
Fixed an issue with dpi changes such as when docking a laptop |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reserving taskbar space so icons stop covering the readout
The problem
I run TrafficMonitor docked in the Windows 11 taskbar and my taskbar is usually full.
Once it fills up, the task buttons expand all the way over to the notification area and
end up sitting underneath the readout. You can't read the numbers and you can't click
the buttons that are stuck under it.
There are already offset options in the settings, but those are a fixed number of pixels
you have to keep retuning by hand, and they don't help at all once the bar is actually
full. The buttons just take the space anyway.
What I tried first, and why it didn't work
My first idea was placeholder task buttons. Register hidden windows with their own
AppUserModelIDs so Windows draws blank buttons where I want the readout to go. That does not
work. As soon as the taskbar is full Windows takes the newest buttons and demotes them into
the overflow flyout. I created four and got zero visible buttons and zero space back.
Second idea was shrinking the XAML island the taskbar draws itself into. That reserved the
space, but it dragged the whole notification area left along with it, so the clock and all
the tray icons moved too. That was worse than what I started with.
What actually works
The Windows 11 taskbar is a two column layout. The task button strip is one column, the
notification area is the other, and the right edge of the strip is the left edge of the
tray. If you make the tray wider, the strip repacks itself to stay clear of it. It never
pushes into the tray. That's the whole trick.
So instead of fighting the button strip, I add a few completely transparent placeholder
icons to the notification area with Shell_NotifyIcon. The tray gets wider, the buttons
move over on their own, and the readout sits in the gap that opens up. Nothing overlaps,
because Windows is doing the layout itself. I'm not overriding anything.
To find out where those placeholders actually landed I use UI Automation on a background
thread. It has to be a background thread, because those cross process calls pump the
message queue and would reenter the UI thread if you did it inline. A WinEvent hook wakes
that thread when the taskbar layout changes, so the window follows along right away.
How much space it reserves comes from the window's own measured width, so it scales with
however many items you have configured. It isn't hardcoded for four.
The part I got wrong twice, and what it taught me
New tray icons don't show up in the tray. Windows files them into the overflow flyout, where
they take up no width at all and reserve nothing. To be useful, each placeholder has to be
marked always visible. That's the same value behind Settings, Personalization, Taskbar,
"Other system tray icons", so it isn't a private hack, but it does mean this feature writes
to the registry. I tried hard to avoid that and it simply cannot be done.
The subtler part took me most of a night to understand. Explorer keeps its tray settings in
memory and treats the registry as a write back copy. If you delete one of those keys from
outside Explorer, the two go out of sync: Explorer still recognizes the icon's identity but
never writes its key again, so that placeholder can never be shown, ever, until Explorer
restarts. My first version deleted its keys on exit to be tidy, which meant run one worked,
exit wiped the keys, and run two was dead on arrival. It looked like a random intermittent
bug for hours.
So now the keys are deliberately left alone. They're a fixed set reused every run, so they
can't pile up. They're only removed when you actually switch the feature off, and if you
switch it back on it rotates to a fresh block of GUIDs, because the batch you just purged is
poisoned by that same desync.
The other things that took real time
Tray icons can be dragged around by the user. If you drop one of your icons into the middle
of my placeholder block, the readout would sit right on top of it. So the query looks at
every button in the taskbar, not just my own, and if anything foreign ends up inside the
reserved region the readout hides until you move it away. An overlap has to survive two
samples before it counts, because opening the overflow chevron makes the tray shuffle for a
frame and that was enough to make the window blink.
The taskbar's own right click menu makes UI Automation report an empty taskbar. Not a smaller
one, an empty one. Treating that literally meant the readout vanished whenever you right
clicked the taskbar. A completely empty result is now treated as a blackout and the previous
one is kept.
On startup the window stays hidden for the second or two the region takes to build, instead
of appearing at its normal spot and then jumping. And there's a deadline, so if the region
never comes together it falls back to ordinary placement rather than hiding forever or
parking itself on top of the task buttons.
What it deliberately doesn't do
No injecting anything into Explorer. No Windhawk style hooks into the shell. It's
Shell_NotifyIcon, UI Automation, one registry value that a Settings toggle already writes,
and an out of context WinEvent hook.
Vertical taskbars aren't supported. That tray is a grid several icons wide, so making it
wider frees up no horizontal space at all and the placeholders accomplish nothing. The
checkbox greys out when your taskbar is on the left or right edge, and it says why.
What I'd want a reviewer to push on
While the feature is on, the placeholders show up as entries in that Settings tray icon list.
That's unavoidable: each reserved slot is a real tray icon and Windows lists every tray icon.
If someone toggles one of them off, the reservation quietly gets smaller.
There's no timeout on shutdown when it waits for the UI Automation thread, so a wedged
Explorer could stall the app on exit.
Two copies installed in different folders would fight over the same placeholder identities.
And the honest one: the app can't tell when it's reserving nothing. If the shell stops
persisting those keys for a reason I haven't seen, it will add icons, report success, and
silently do nothing. The GUID rotation covers the case I actually hit, but that's a class of
problem rather than a single bug.
Why I think it's worth having
It's off by default, so nobody gets surprised by it. If you turn it on, the placeholders are
invisible, and they're removed again when you turn it off.
For anyone who keeps a full taskbar, this is the difference between the readout being usable
and being buried. It fixes it the way the shell already wants to lay things out rather than
fighting it, which is why it holds up when the taskbar changes size, when icons come and go,
and across an Explorer restart.
It has been tested on one machine. It should go in as opt in and be treated as beta until
other people have run it.