Skip to content

fix(windows): leave a connection window's first focus to its tab so Cmd+W closes the tab - #2981

Merged
datlechin merged 2 commits into
mainfrom
fix/close-tab-focus-on-hidden-rail
Sep 18, 2026
Merged

datlechin merged 2 commits into
mainfrom
fix/close-tab-focus-on-hidden-rail

Conversation

@datlechin

Copy link
Copy Markdown
Member

The bug

After relaunching with restored tabs, pressing Cmd+W before clicking anything closed the whole connection window instead of the current tab. Clicking the editor or the grid first made Cmd+W close the tab as expected.

It is not specific to restore. Any connection window nobody has clicked into yet does it, including one opened fresh from the welcome window.

Root cause

A connection window never said where its first keyboard focus goes, so AppKit picked it once, as the window was first put on screen, from the views that existed at that moment. The editor, the grid and the object list are SwiftUI and are not built yet at that point. The only view that could take focus was the connections strip's list.

That list qualified even when the strip was "hidden", because collapsing the strip only set its width constraint to 0 and never set isHidden. AppKit treats a zero-width view as visible, so the list stayed a key view. The strip's controller answers performClose: itself and closes its highlighted entry, and for a connection's only entry that means closing the connection. So Cmd+W went to the strip, not to the window.

The editor (SQLEditorCoordinator.installEditorServices) and the object list (SidebarOutlineView.adoptFirstResponderIfVacant) already take focus when nobody holds it. The strip already held it, so both gave way.

Measured in a sandboxed Debug build of main, reading AXFocusedUIElement before pressing Cmd+W:

Case Focus before the first click Cmd+W did
One connection restored, two tabs workspace-rail (zero width, invisible) closed the connection, Welcome window appeared
Fresh connect to the sample database workspace-rail File menu read Close Connection "Chinook (Sample)"
Two connections restored, strip visible workspace-rail closed the whole selected connection

Probes against AppKit on macOS 27 confirmed the rest: the pick happens at makeKeyAndOrderFront whether or not autorecalculatesKeyViewLoop is set; views added later never take it; a visible strip wins too; and hiding a view that holds focus hands it on.

The fix

Two changes, each with its own job:

  1. A collapsed strip is hidden, not only zero width (NavigationSidebarViewController). It is shown before it grows and hidden once it has shrunk, so the animation is unchanged. If the keyboard is in the strip when it starts to collapse, focus moves to the next key view, which is what AppKit does itself when a focused view is hidden. The window keeps focus only if no other key view exists. This takes the invisible list out of the key view loop, the responder chain and the accessibility tree. That covers one connection, Show connections turned off, and View > Hide Connections.
  2. The window names its first focus (EditorWindow, MainSplitViewController+Focus). initialFirstResponder is the container the selected tab's content is shown in. The container takes no focus itself, so the window keeps it when it appears, and the editor or object list adopts it once built, which is the vacancy contract both already follow. This is what covers a strip that is already on screen at first show, which hiding cannot reach.

The strip keeps its documented Cmd+W for when you deliberately put the keyboard in it, per docs/features/keyboard-shortcuts.mdx.

Tests

  • ConnectionWindowInitialFocusTests (unit):

    • A strip that was never shown, or was collapsed, is hidden and has no key view.
    • A shown strip is a key view.
    • Collapsing the strip releases focus immediately.
    • The released focus goes to the next key view rather than to the window.
    • A connection window with the strip on screen at first show leaves the window as first responder.

    Against the old code, four of the first five fail. The fifth is the shown-strip control, which passes either way.

  • CloseTabBeforeFirstClickUITests (UI):

    • Open the sample database and press Cmd+W without clicking: the table tab closes and the connection stays open.
    • Seed a two-connection session with three tabs each, relaunch, and press Cmd+W without clicking: one tab closes and both connections stay in the strip.

Before / After

Two connections restored into one window, nothing clicked, then Cmd+W.

Start: two entries in the strip, the "Query 1" tab selected.

Start: two connections in the strip, Query 1 selected

Before: the "Chinook (Sample)" connection closed, and the strip went with it.

Before: Cmd+W closed the Chinook connection

After: only the "Query 1" tab closed. Both connections are still in the strip.

After: Cmd+W closed the Query 1 tab and both connections stay

Verification

  • verify.sh build: PASS.
  • verify.sh test ConnectionWindowInitialFocusTests ConnectionWindowChromeTests: PASS, 21/21.
  • Negative control: the same suite with the two behaviour files reverted to main fails in four of its first five cases.
  • swiftlint lint --strict on every changed file: 0 violations.
  • Live check in a TABLEPRO_UI_TEST_SANDBOX Debug build, one and two restored connections: focus lands in the SQL editor, Cmd+W closes the tab, and every connection stays open.
  • Not run locally: CloseTabBeforeFirstClickUITests and WindowFocusUITests. The runner stopped at macOS's "Enable UI Automation" password prompt (Timed out while enabling automation mode, zero cases executed), so CI's UI shards are their first run.
  • Review: Codex is out of usage until 2026-09-22, so /code-review read the diff instead. Its one finding was that collapsing a focused strip left the keyboard with the window. That is fixed in the second commit and has its own test.

Not changed

The strip still closes its highlighted entry on Cmd+W when you have put the keyboard in it yourself, as keyboard-shortcuts.mdx documents.

@datlechin
datlechin merged commit d795615 into main Sep 18, 2026
9 of 13 checks passed
@datlechin
datlechin deleted the fix/close-tab-focus-on-hidden-rail branch September 18, 2026 12:26
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