Skip to content

fix(tabs): open files as tabs in full screen and Split View - #36

Merged
b451c merged 1 commit into
b451c:mainfrom
onemannoplan:fix/fullscreen-tabbing
Oct 22, 2026
Merged

b451c merged 1 commit into
b451c:mainfrom
onemannoplan:fix/fullscreen-tabbing

Conversation

@onemannoplan

Copy link
Copy Markdown
Contributor

Problem

With QuickMD in full screen or Split View (e.g. QuickMD on the left half, Finder on the right), opening another file did not add a tab in place. The tab group was moved into a new full-screen Space of its own: the Split View broke, the window kept its half width with the other half of the screen black, and the menu bar could not be revealed.

Cause

Two things together:

  1. WindowConfigurator's callback runs only after the new window is already on screen. In full screen, the window has already started getting its own Space by then. The late addTabbedWindow moved it into the existing tab group mid-transition.
  2. The callback renamed every window's tabbingIdentifier, also after the window appeared. SwiftUI already gives all DocumentGroup windows one shared identifier, so AppKit's own tabbing would have handled this (the system default, "Prefer tabs: In Full Screen", tabs new windows in place). But the renamed host never matched the new window, so AppKit gave the new window a Space of its own instead.

Fix

  • Keep SwiftUI's shared tabbingIdentifier, and set AppleWindowTabbingMode to always in the argument domain at launch (WindowTabbing.preferTabsAlways()). This applies to QuickMD only and is never written to disk, so the user's system setting is untouched. AppKit then tabs each new window as it is ordered in, whether or not the app is in full screen. This matches the app's stated intent of tabs regardless of the system setting.
  • The manual merge is now only a fallback for ordinary windows, and never runs in full screen. A window AppKit already tabbed is treated as merged, so WindowSizeMemory no longer resizes the tab group.
  • WindowSizeMemory ignores full-screen windows. Before this, a Split View half (1534 × 1920 here) was saved as the size for later standalone windows.

Testing

  • New WindowTabbingTests (14 tests): AppKit reports .always after preferTabsAlways(), other launch arguments are kept and nothing is persisted, the merge decisions, and size memory skipping full-screen windows.
  • Full suite: 701 passed, 0 failed.
  • Manual, on an Intel MacBook Pro with macOS 26.6.2: new files open as tabs in full screen and in Split View with Finder, and the Split View stays intact. Before the fix, temporary logging showed the new window already visible and on its own full-screen Space when the callback ran. After the fix, it was already a tab in the existing group by then.

Opening a file while QuickMD was in full screen or Split View gave the
tab group a full-screen Space of its own: the Split View broke, half the
screen was black and the menu bar could not be revealed.

WindowConfigurator's callback runs only after the new window is on
screen (and, in full screen, already in a Space of its own), so its late
addTabbedWindow moved a window mid-transition. It also renamed every
window's tabbingIdentifier, so AppKit's own tabbing (on by default in
full screen) never matched the new window to the existing one.

- Keep SwiftUI's shared tabbingIdentifier and set AppleWindowTabbingMode
  to "always" in the argument domain at launch (QuickMD only, not
  persisted): AppKit tabs each new window as it is ordered in.
- Manual merge is a fallback for ordinary windows only; a window AppKit
  already tabbed is not resized.
- WindowSizeMemory ignores full-screen windows, so a Split View half is
  no longer remembered as the size for new windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@b451c
b451c merged commit 02a3c39 into b451c:main Oct 22, 2026
3 checks passed
@b451c

b451c commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Thank you, @onemannoplan - great diagnosis, and the write-up made it easy to verify.

Merged into main, together with one follow-up commit (26fa99b):

  • The manual-merge fallback now picks its host from the document windows that WindowConfigurator registers, instead of matching tabbingIdentifier. SwiftUI's shared identifier is what makes AppKit's own tabbing work (that part is yours and unchanged), but it is not documented and I could only check it on macOS 26/27. With the registry, a second file still opens as a tab outside full screen even if an older macOS gives each window its own identifier, and the Settings / Tip Jar window can never become a document's host.
  • FakeWindow records addTabbedWindow instead of performing it, so the hidden-host test now checks that no merge happened (the old assertion read an override that was never set). One new test merges into a host with a different identifier, and it fails on the identifier match.
  • CHANGELOG entry with credit.

Checked on a separate build on macOS 27: on the old main, opening a second file in full screen created a second full-screen Space; with your fix it lands as a tab in the same Space. A Settings window opened next to the documents stays a separate window. I could not test Split View on this Mac, because "Displays have separate Spaces" is off here and macOS does not offer Split View without it. It goes out in the next release.

@b451c

b451c commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Shipped in v1.13.0: https://github.com/b451c/quickmd/releases/tag/v1.13.0 (also on Homebrew). Thanks again, @onemannoplan!

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.

2 participants