Skip to content

fix(toolbar): anchor each centre switcher to the capsule that opens it - #2678

Merged
datlechin merged 1 commit into
mainfrom
fix/toolbar-switcher-anchor
Sep 8, 2026
Merged

fix(toolbar): anchor each centre switcher to the capsule that opens it#2678
datlechin merged 1 commit into
mainfrom
fix/toolbar-switcher-anchor

Conversation

@datlechin

Copy link
Copy Markdown
Member

The connection chooser and the container chooser both anchored to MainWindowToolbar.connectionGroup, which is the whole centred group: two capsules wide. So both popovers opened on the seam between Connection and Database rather than under the one the user pressed.

Measured

A compiled NSToolbar harness, group of two bordered subitems, centeredItemIdentifiers = [group], window 1200pt:

anchor popover midX capsule midX
group 600.0 -
Connection subitem 543.0 543.2
Database subitem 671.0 671.8

A subitem does resolve as a popover anchor and lands on its own capsule to within a point. The code did not do that because of a claim in its own comment: NSToolbar.items holds groups only and a native group's subitems carry no view, so the subitem "cannot" anchor. The first half is true, the conclusion is not; NSPopover.show(relativeTo:) does not go through toolbar.items.

There is a real trap, though. Once AppKit clips the group into the overflow menu its subitems have no view, and the same call raises NSInvalidArgumentException ("view has no window"), which Swift cannot catch. Sweeping the window width: toolbar.visibleItems names the group down to 640pt and stops at 600pt, and the throw begins at exactly the same width. The group itself keeps working there, because AppKit presents a clipped item from another affordance in the window.

Change

ToolbarSwitcherPresenter.anchor now resolves an identifier that may name a subitem: the toolbar's own item first, then the subitem while its group is in visibleItems, then the group once it is not, then nil so a removed group still falls back to the floating panel. Switch Connection anchors to MainWindowToolbar.connection, the container and schema choosers to MainWindowToolbar.database.

CompareEndpointToolbarController shares the same resolver but its identifiers are top-level, so its behaviour is unchanged.

Tests

Five cases in ToolbarSwitcherAnchorTests, over a resolver seam that takes items and visible items rather than a live toolbar: subitem of a visible group, fallback to an overflowed group, group absent from the toolbar (what Customize Toolbar leaves behind, since neither subitem is an allowed identifier of its own), a clipped top-level item, and the window lookup end to end. visibleItems names a group even for a window never ordered front, measured, so the visible branch is reachable from a unit test.

10/10 pass, swiftlint --strict clean on the touched files, app builds.

https://claude.ai/code/session_01DT51WAbwXF3AxSReQxNhnD

@datlechin
datlechin merged commit 77c7b91 into main Sep 8, 2026
4 checks passed
@datlechin
datlechin deleted the fix/toolbar-switcher-anchor branch September 8, 2026 17:02
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.

1 participant