Skip to content

Fall back to a second taskbar lane when the Widgets→Start lane can't fit the widget - #281

Merged
tsouth89 merged 5 commits into
tsouth89:mainfrom
diogochaves:fix/taskbar-widget-auto-lane
Aug 12, 2026
Merged

Fall back to a second taskbar lane when the Widgets→Start lane can't fit the widget#281
tsouth89 merged 5 commits into
tsouth89:mainfrom
diogochaves:fix/taskbar-widget-auto-lane

Conversation

@diogochaves

@diogochaves diogochaves commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Fixes the silent-hide from #261, per the plan in that thread: fix auto first, its own PR, no new setting.

Opening this a bit ahead of #264 settling: manual verification turned up a worse shipping manifestation of the same bug (the Widgets freeze below) that seemed worth showing sooner. Once #264 lands I'll rebase and generalize its status-row string ("between Widgets and Start" → wording that covers both lanes) — one small hunk; there's no other coupling between the two PRs.

On Windows 11 with Taskbar alignment = Left (a stock setting), Start sits at x≈0 and the Widgets→Start lane's right edge goes negative, so the native widget hides silently. Windhawk's "Start button always on left" triggers the same thing. This PR keeps that lane as the always-preferred first choice and adds a fallback: when it has no verified gap that clears the minimum width, try a second lane between Start and the tray, with exactly the same obstacle verification — pinned/running icons are obstacles, so the widget lands centered in the empty stretch after the last icon, never covering a button.

What changed

  • floatbar/taskbar.rs: the TrayNotifyWnd rect (already discovered via FindWindowExW and used as an obstacle) is now also exposed as landmarks.tray. It bounds the fallback lane's right edge; when a taskbar doesn't expose it (secondary taskbars can omit it) or its rect fails the taskbar-band sanity check, the lane falls back to the taskbar's own right edge.
  • taskbar_widget.rs: the inline gap-scan in placement_outcome is factored into a best_gap helper (same obstacle filter, same scan, same largest-gap tie-break — a pure refactor), and lane 2 is tried only when lane 1 returns no qualifying gap. Placement in either lane is the existing policy: centered in the largest fully-empty gap, desired.min(available) width.

What deliberately didn't change

  • Lane 1 wins whenever it fits at all, so behavior on centered-alignment taskbars is byte-identical — all 37 pre-existing placement policy tests pass untouched, and a new test pins lane 1 winning even when lane 2 also has room.
  • No settings, no UI. (The Surface the taskbar widget's hidden state in Settings #264 status row's "between Widgets and Start" wording will be generalized in a follow-up hunk once that PR is in main.)
  • One guard that predates this PR did have to change, because manual verification proved it breaks the headline scenario: with alignment = Left and Windows Widgets enabled (the stock default), Windows renders the Widgets entry by the tray, so UIA reports it right of Start permanently. The existing widgets.right >= start.left guard reads that as mid-animation and returns TransientLandmarks on every pass — and since transient misses deliberately preserve the existing window, a widget placed before flipping alignment stays frozen on top of the app icons indefinitely. This isn't introduced by the branch: the guard has shipped since v0.43 (a01eaab) and the preservation policy is similarly long-standing. Reproduced on the installed 1.5.27 release: with Widgets off, flipping to left alignment makes the strip vanish (the known [Feature]: Anchored placement for the taskbar usage strip (tray-adjacent), tolerant of taskbar mods #261 silent hide); with Widgets on, it instead stays frozen on top of the app icons (screenshot below). Now that geometry means "lane 1 doesn't exist": lane 2 places the widget and the Widgets rect joins the obstacle set, so the verified gap can never cover it. Genuinely stale (off-band) Widgets rects keep the transient policy, pinned by its own test.
  • The obstacle model is unchanged: XAML controls are harvested via UIA with the existing Button-type filter, same as lane 1 has always used. Lane 2 traverses new territory (right of Start), so anything there that UIA doesn't expose as a button — worth watching: the wide search box, and the clock on secondary taskbars without TrayNotifyWnd — wouldn't be avoided. Verified on real hardware (Windows 11 Pro 25H2, build 26200.9168): with the search box enabled under left alignment, UIA exposes the pill as a button and the widget avoids it like any other control. If a configuration surfaces where UIA under-reports, extending the harvest filter is a contained follow-up in uia_buttons.

One behavior in lane 2 is deliberately stricter than "missing tray": a tray rect that fails the taskbar-band sanity check is treated as transient (stale rect mid layout/DPI change — retry next watchdog pass) rather than widening the lane to the taskbar's right edge over the tray's true position. Same staleness policy as an off-band Start or Widgets rect.

New tests

Ten: the left-aligned-Start fallback itself (the test that closes #261), missing-tray right-edge fallback, off-band tray reporting transient, the tray-side Widgets entry becoming a lane-2 obstacle, off-band Widgets staying transient, lane 1 preferred when both lanes fit, crowded-but-non-negative lane 1 falling through, fully-obstructed lane 2 still reporting VerifiedNoFit, the vertical-taskbar early return, and multi-monitor with per-taskbar tray rects. Placement tests assert hand-computed pixel positions, not just presence.

Commands run

cargo fmt --all
cargo test --manifest-path rust\Cargo.toml
cargo test --manifest-path apps\desktop-tauri\src-tauri\Cargo.toml
cargo clippy --manifest-path rust\Cargo.toml --all-targets -- -D warnings
cargo clippy --manifest-path apps\desktop-tauri\src-tauri\Cargo.toml --all-targets -- -D warnings
pnpm --dir apps/desktop-tauri test

All green: 872 + 504 Rust tests, 500 frontend tests, clippy clean with -D warnings.

Manual verification on Windows 11 Pro 25H2 (build 26200.9168), single monitor, with and without Windhawk mods.

Screenshots

Alignment = Center (default) — placement unchanged:
normal

Taskbar alignment = Left (stock Windows 11, no mods) — previously hidden silently, now centered in the free stretch between the icons and the tray:
taskbar-aligment-left

Windhawk "Start button always on left" (icons stay centered) — same fallback lane, but here the largest verified gap is between Start and the centered icons, so the widget lands there and nearly recreates the default look. The fallback isn't "move right", it's "find the room":
windhawk-start-button-always-on-the-left

Transition honesty: re-placement is watchdog-driven (5s), so a violent relayout like flipping alignment can overlap icons until the next pass. Pre-existing cadence, unchanged by this PR — the same staleness exists on main whenever the icon row grows:
5-seconds-window

The pre-existing Widgets-guard freeze (before): alignment = Left with Windows Widgets on — the guard reports transient forever and the preserved widget stays frozen over the icons. Ships in every release since v0.43; fixed by this PR. (The weather entry itself is also misplaced in this shot — that's an Explorer relayout quirk when flipping alignment center→left with Widgets on; it reproduces with Ceiling closed and is unrelated to this change.):
windows-widget-bug

After the fix: same stock-default combo, widget placed in the fallback lane with the Widgets entry treated as an obstacle:
windows-widget-reactivated

Summary by CodeRabbit

  • Bug Fixes
    • Improved widget placement around the Windows taskbar and notification area.
    • Prevented widgets from overlapping taskbar controls, tray icons, or other obstructions.
    • Improved placement on left-aligned and vertical taskbars.
    • Added more reliable fallback positioning when taskbar information is unavailable or outdated.
    • Improved placement across multiple monitors, including monitor-specific notification areas.
    • Simplified the taskbar status message when no suitable space is available.

@diogochaves
diogochaves requested a review from tsouth89 as a code owner August 12, 2026 18:44
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ed522e2-ad70-4253-a108-e67845a9ed61

📥 Commits

Reviewing files that changed from the base of the PR and between ba3097b and ffbdf1e.

📒 Files selected for processing (3)
  • apps/desktop-tauri/src-tauri/src/taskbar_widget.rs
  • apps/desktop-tauri/src/floatbar/SettingsSection.test.tsx
  • apps/desktop-tauri/src/floatbar/SettingsSection.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop-tauri/src-tauri/src/taskbar_widget.rs

📝 Walkthrough

Walkthrough

The change records notification-area geometry and refines native taskbar widget placement. Placement now shares gap scanning, handles Widgets and stale landmarks, and falls back to tray or taskbar edges. The no-fit status message is location-independent.

Changes

Native taskbar placement

Layer / File(s) Summary
Tray landmark discovery
apps/desktop-tauri/src-tauri/src/floatbar/taskbar.rs
TaskbarLandmarks stores the TrayNotifyWnd rectangle. Discovery records the tray as both a landmark and an obstacle.
Placement lane selection
apps/desktop-tauri/src-tauri/src/taskbar_widget.rs
Placement uses shared gap scanning, treats Widgets at or after Start as unavailable for lane one, and falls back to a Start-to-tray or taskbar-edge lane.
Placement validation coverage
apps/desktop-tauri/src-tauri/src/taskbar_widget.rs
Fixtures and tests cover stale landmarks, lane obstruction, vertical taskbars, blocked lanes, left-aligned taskbars, and independent multi-monitor tray coordinates.
No-fit status wording
apps/desktop-tauri/src/floatbar/SettingsSection.tsx, apps/desktop-tauri/src/floatbar/SettingsSection.test.tsx
The no-fit status now says “Hidden: no free space on the taskbar.” Tests cover current and stale placement results.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Mergeability Score: ⚪ Minimal · up to ffbdf

The PR adds a bounded taskbar placement fallback without introducing a known correctness or production-readiness issue; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant TaskbarLayoutDiscovery
  participant TrayNotifyWnd
  participant placement_outcome
  participant NativeTaskbarWidget
  TaskbarLayoutDiscovery->>TrayNotifyWnd: discover tray bounds
  TrayNotifyWnd-->>TaskbarLayoutDiscovery: return notification-area rectangle
  TaskbarLayoutDiscovery->>placement_outcome: provide landmarks and obstacles
  placement_outcome->>placement_outcome: scan gaps and select fallback lane
  placement_outcome->>NativeTaskbarWidget: apply widget placement
Loading

Possibly related PRs

Suggested reviewers: tsouth89, finesssee

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary change: adding a fallback taskbar lane when the Widgets-to-Start lane cannot fit the widget.
Linked Issues check ✅ Passed The changes satisfy issue [#261] by adding verified fallback placement, tray landmarks, obstacle checks, and hidden-state feedback for automatic placement.
Out of Scope Changes check ✅ Passed The code, tests, and status-message updates directly support the taskbar placement and diagnostic objectives in issue [#261].
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

diogochaves and others added 4 commits August 12, 2026 17:53
Stock Windows 11's "Taskbar alignment = Left" (and Windhawk's "Start
button always on left") pin Start at the taskbar's left edge, which
starves the only lane placement_outcome knew about (Widgets->Start)
and silently hides the widget. Factor the gap scan into a shared
best_gap helper and try a second lane, Start->tray, whenever lane 1
has no qualifying gap. Lane 1 still wins whenever it fits, so default
placement is unchanged.

Promote the already-discovered TrayNotifyWnd rect to
TaskbarLandmarks.tray as the new lane's right-edge landmark, with a
bounds.right - 8 fallback for secondary taskbars that omit it or when
the tray rect fails the taskbar-band check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An off-band tray rect is stale (mid layout or DPI change); letting it
widen lane 2 to the taskbar's right edge could place the widget over the
tray's true position, so report TransientLandmarks and retry on the next
watchdog pass — the same policy as an off-band Start or Widgets rect.
Also add a characterization test pinning the pre-existing sanity guard
(Widgets at or right of Start stays transient, no lane-2 fallback) so a
future change to it is a conscious one, and fold the three left-aligned
lane-2 fixtures into one helper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With "Taskbar alignment = Left" and Windows Widgets enabled (the stock
default), Windows renders the Widgets entry by the tray, so UIA reports
it at or right of Start permanently. The sanity guard read that as a
mid-animation state and returned TransientLandmarks on every watchdog
pass, which froze a stale widget in place (transient misses preserve the
existing window) and never consulted the fallback lane — reproduced on
real hardware during tsouth89#261 verification. Lane 1 is now simply absent in
that geometry, lane 2 places the widget, and the Widgets rect joins the
obstacle set so the verified gap can never cover it. Off-band Widgets
rects keep the transient policy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@tsouth89
tsouth89 force-pushed the fix/taskbar-widget-auto-lane branch from ba3097b to ffbdf1e Compare August 12, 2026 21:56
@tsouth89
tsouth89 merged commit dfdd24c into tsouth89:main Aug 12, 2026
5 checks passed
@diogochaves
diogochaves deleted the fix/taskbar-widget-auto-lane branch August 12, 2026 22:56
@tsouth89 tsouth89 mentioned this pull request Aug 13, 2026
10 tasks
tsouth89 added a commit that referenced this pull request Aug 13, 2026
Cuts 1.5.30. Version bumped in all five locations from `VERSIONING.md`,
`BUILD_NUMBER=132`, `Cargo.lock` regenerated, and the `Unreleased`
entries moved into a dated section.

## What's in it

**A second click on the tray icon hides the dashboard** (#283, closes
#280). Left-click always reopened the stats window. First click shows
it, second click hides it. A Windows double-click is ignored so the
window does not flash.

**Grok banked resets show up** (#282). Codex already had the chip.
Grok's redeemable reset tokens now appear on Overview, provider detail,
and the taskbar flyout. Redeem stays on grok.com.

**Settings says why the taskbar widget is hidden** (#264). The strip
could vanish with no explanation. The Taskbar Usage group now reports
shown, no free space, waiting for landmarks, or no enabled providers.

**The taskbar widget takes a second lane when the usual one does not
fit** (#281, closes #261). Left-aligned taskbars (and Windhawk "Start
always on left") made the Widgets-to-Start gap go negative, so the strip
hid. If that lane has no room, it now uses the stretch between Start and
the tray.

**Charts no longer mix one account's usage into another** (#277). A
removed account could inherit the machine-wide cache. Each account now
has its own tab and cache identity.

**Provider detail no longer applies a stale refresh** (#278). A slower
request from the previous seat could paint over the one you just
switched to.

**Credential status is scoped to the selected provider** (#279). Opening
provider B could show a protected store and revoke action that belonged
to provider A.

## Validation

`scripts\local-check.ps1 -All -Version 1.5.30` passed except the known
local PTY failure:

- `cli::tty_runner::tests::test_run_sends_script_through_pty` fails
locally because `%APPDATA%\npm` has a `cmd` shim that is not a valid
Win32 application. Same environment issue noted on #263. 877 other
shared Rust tests passed.

Tauri Rust tests (517), frontend tests (525), frontend build, clippy,
fmt, store-submission prep, and release-doctor all passed.

Three release-doctor warnings are expected pre-tag: no local `v1.5.30`
tag, no GitHub release yet, no local assets directory.

## Affected areas

- [x] Tray panel
- [x] Settings UI
- [ ] Config file / settings persistence
- [ ] CLI
- [x] Provider-specific behavior
- [x] Installer / release packaging
- [ ] Startup / background behavior
- [x] Documentation
- [ ] Other:

## UI / tray proof

- [x] Visual proof was not practical; manual validation and explanation
attached

The tray toggle was click-tested on a local debug build of `main` before
this version bump. Release notes are the changelog section in this PR.

## Notes for reviewers

This PR is the version bump and changelog only. The signed installer is
built by tagging `v1.5.30` after merge.

Co-authored-by: tsouth89 <tsouth89@users.noreply.github.com>
@tsouth89 tsouth89 mentioned this pull request Aug 14, 2026
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Anchored placement for the taskbar usage strip (tray-adjacent), tolerant of taskbar mods

2 participants