Skip to content

fix(menubar): stop double-counting NSStatusItemSpacing in notch overflow budget#573

Merged
stonerl merged 2 commits into
developmentfrom
fix/notch-overflow-spacing-bug
May 13, 2026
Merged

fix(menubar): stop double-counting NSStatusItemSpacing in notch overflow budget#573
stonerl merged 2 commits into
developmentfrom
fix/notch-overflow-spacing-bug

Conversation

@nightah
Copy link
Copy Markdown
Collaborator

@nightah nightah commented May 12, 2026

What does this PR do?

Removes a double-count of NSStatusItemSpacing in the notch overflow budget so that profile applies at the macOS default spacing (offset=0) no longer push items into the hidden section while the menu bar visibly has room.

PR Type

  • Bugfix
  • CI/CD related changes
  • Code style update (formatting, renaming)
  • Documentation
  • Feature
  • Refactor
  • Performance improvement
  • Test addition or update
  • Other (please describe):

Does this PR introduce a breaking change?

  • Yes
  • No

If yes, please describe the impact and migration path:

N/A

What is the current behavior?

When applyProfileLayout runs on a notched display with the overflow toggle enabled, it computes availableWidth = rightBoundary - notchGap - nonProfileFootprint - (count - 1) * userSpacing and compares against a profileBaseline summed from each item's bounds.width. macOS bakes NSStatusItemSpacing into each status item's frame, so bounds.width already includes the spacing padding, and rightBoundary (Control Center's bounds.minX) already shifts to absorb CC's own spacing growth. The explicit (count - 1) * userSpacing subtraction therefore deducts the spacing a second time, under-counting availableWidth by (count - 1) * userSpacing pixels. At offset=-12 (spacing 4) the error is small (around 48px on the typical layout) and absorbed by historical slack; at offset=0 (spacing 16) the error grows to roughly 192px and forces six profile items into the hidden section despite a visibly empty bar between the rightmost visible item and Control Center.

Issue Number: N/A

What is the new behavior?

The (count - 1) * userSpacing deduction is removed. The budget now relies on item.bounds.width and ccItem.bounds.minX to carry the spacing footprint, which the diagnostic log shows they already do. With the fix the Built-in Retina apply at offset=0 reports availableWidth=424 against profileBaseline=419, well within budget, and the previously-ejected items remain in the visible section. At offset=-12 and offset=-6 the budget gains additional slack but profiles that already fit continue to fit, so existing layouts are unchanged.

PR Checklist

  • I've built and run the app locally
  • I've checked for console errors or crashes
  • I've run relevant tests and they pass
  • I've added or updated tests (if applicable)
  • I've updated documentation as needed

Other information

Empirical width measurements collected from the Notch overflow budget: diagnostic log, same 11 profile items each run:

offset spacing rightBoundary availableWidth (pre-fix) profileBaseline avg item width
-12 4 1301 404 287 24.8
-6 10 1285 316 357 31.2
0 16 1273 232 419 36.8

Average item.bounds.width rises linearly at 1:1 with userSpacing, implying an icon-only width of about 20.8 for this profile plus the current spacing. With the fix, availableWidth at offset=0 becomes 232 + 12 * 16 = 424, exceeding profileBaseline=419 by 5px and matching what the screen actually has room for.

Notes for reviewers

Focus areas:

  • The diff is intentionally surgical: one deduction removed, one comment block rewritten. userSpacing is retained so the existing Notch overflow budget: log line continues to report the system spacing for diagnostics.
  • No unit tests cover the notch overflow branch of applyProfileLayout today; verification was manual at offset = -12, -6, and 0 on the Built-in Retina display, with the Notch overflow budget: diagnostic line captured at each step (values above).
  • Manual repro steps before and after the fix:
    1. Settings, Displays, set Built-in Retina spacing offset to 0.
    2. Settings, Profiles, click Apply on a profile that has roughly 11 items in the visible section.
    3. Before the fix: several items overflow into hidden while the bar shows a visible empty gap between the last visible item and Control Center; Notch overflow budget: logs availableWidth=232.
    4. After the fix: items remain in visible filling the bar; Notch overflow budget: logs availableWidth=424.

Summary by CodeRabbit

  • Bug Fixes

    • Resolved menu bar item spacing calculation issue on notched displays affecting overflow decisions and proper menu layout
  • Chores

    • Enhanced build process reliability by explicitly configuring Xcode version selection in DMG creation workflow

Review Change Stack

…low budget

The notch overflow check in `applyProfileLayout` was subtracting `(count - 1) * userSpacing` from `availableWidth` while the per-item widths it summed into `profileBaseline` already had the spacing baked in by macOS. The deduction therefore over-counted layout cost by `(count - 1) * userSpacing` pixels. The error scaled linearly with the spacing value, so it was invisible at `offset=-12` (spacing `4`) but ejected several profile items into the hidden section at `offset=0` (the macOS default of `16`) even when the menu bar visibly had room. Applying the Built-in Retina Display profile at default spacing pushed six items into hidden while leaving roughly 200px of empty bar between the rightmost visible item and Control Center.

Empirical measurements taken from the `Notch overflow budget:` diagnostic log with the same 11 profile items at three spacing values (`-12`, `-6`, `0`) showed the average reported `item.bounds.width` rising linearly from `24.8` to `31.2` to `36.8`, an implied icon-only width of about `20.8` plus exactly the current spacing value. The Control Center item's `bounds.minX` (used as `rightBoundary`) also shifted left by about `2px` per `1px` of spacing for the same reason. Both inputs to the budget already reflect the spacing, so the explicit `(count - 1) * userSpacing` deduction was pure double-counting.

This change removes that deduction and rewrites the comment block above `userSpacing` to record the empirical relationship instead of the inverted assumption that was there before. `userSpacing` itself is retained because the `Notch overflow budget:` diagnostic line continues to log it. With the fix in place the Built-in Retina apply at `offset=0` reports `availableWidth=424` against `profileBaseline=419`; the profile fits, no overflow is triggered, and the visible section matches what the bar actually has room for.

Signed-off-by: Amir Zarrinkafsh <3339418+nightah@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 889e2b2c-ad2a-4a8b-b4b6-611d533ae66a

📥 Commits

Reviewing files that changed from the base of the PR and between fd1b2e5 and 8751e99.

📒 Files selected for processing (1)
  • .github/workflows/build-dmg.yml

📝 Walkthrough

Walkthrough

Fix double-counting of NSStatusItemSpacing in notch-overflow budgeting by treating status item bounds as already including inter-item spacing and removing the extra spacing subtraction; also add an explicit xcode-select step to the DMG build workflow to choose Xcode 26.5.

Changes

Notch-Overflow Budgeting

Layer / File(s) Summary
Spacing double-counting fix and comments
Thaw/MenuBar/MenuBarItems/MenuBarItemManager.swift
Updated comments to state that NSStatusItemSpacing is already baked into each status item's bounds; removed the explicit (totalItemCount - 1) * userSpacing subtraction from the notch overflow available-width calculation, so overflow decisions no longer double-count inter-item spacing.

DMG Build Workflow

Layer / File(s) Summary
Xcode selection for DMG build
.github/workflows/build-dmg.yml
Added a workflow step that runs sudo xcode-select --switch /Applications/Xcode_26.5.app/Contents/Developer with a comment noting compatibility reasons, ensuring the DMG build uses Xcode 26.5.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

  • stonerl/Thaw#522: Related — both PRs modify applyProfileLayout in MenuBarItemManager.swift.

Poem

🐰 A status bar once counting twice,
Bounds held spacing, not device.
Now math is neat, the icons stay,
And builds pick Xcode on their way.
Hoppity hop—fixes made today.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main fix: removing double-counting of NSStatusItemSpacing in the notch overflow budget calculation.
Description check ✅ Passed The PR description is comprehensive and follows the template well, including all major sections with detailed technical explanation, testing confirmation, and diagnostic evidence.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/notch-overflow-spacing-bug

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 and usage tips.

Specify Xcode version to resolve compatibility issues.
@sonarqubecloud
Copy link
Copy Markdown

@stonerl stonerl merged commit 121a9c9 into development May 13, 2026
8 checks passed
@stonerl stonerl deleted the fix/notch-overflow-spacing-bug branch May 13, 2026 07:20
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.

3 participants