Fall back to a second taskbar lane when the Widgets→Start lane can't fit the widget - #281
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe 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. ChangesNative taskbar placement
Estimated code review effort: 3 (Moderate) | ~25 minutes Mergeability Score: ⚪ Minimal · up to 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
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
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>
ba3097b to
ffbdf1e
Compare
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>
Fixes the silent-hide from #261, per the plan in that thread: fix
autofirst, 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: theTrayNotifyWndrect (already discovered viaFindWindowExWand used as an obstacle) is now also exposed aslandmarks.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 inplacement_outcomeis factored into abest_gaphelper (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
widgets.right >= start.leftguard reads that as mid-animation and returnsTransientLandmarkson 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.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 inuia_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
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:

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

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":

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:

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.):

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

Summary by CodeRabbit