Skip to content

Fix menubar popover across macOS spaces#12

Merged
handlecusion merged 2 commits into
handlecusion:mainfrom
Nanako0129:fix-menubar-space-switch
Jun 4, 2026
Merged

Fix menubar popover across macOS spaces#12
handlecusion merged 2 commits into
handlecusion:mainfrom
Nanako0129:fix-menubar-space-switch

Conversation

@Nanako0129

@Nanako0129 Nanako0129 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Background

Tokcat is a macOS menubar app, so the main window behaves like a popover rather than a regular desktop window. Before this change, the first Space that opened the popover could effectively become the window's remembered Space. When the user clicked the menu bar icon from another Space, macOS could switch back to the original Space instead of opening Tokcat where the click happened.

Full-screen app Spaces had a second failure mode: after preventing the Space switch, the popover could still fail to appear because it was not ordered above the full-screen app window.

Expected behavior: when the user clicks the Tokcat menu bar icon, the popover should open in the current macOS Space, including regular desktop Spaces and full-screen app Spaces.

Problem Matrix

Scenario Previous behavior Expected behavior
First open in Space A Popover appears normally Popover appears normally
Switch to Space B and click the menu bar icon macOS may jump back to Space A Stay in Space B and show the popover
Click from a full-screen app Space The app may stay in place, but the popover may not appear Stay in the full-screen Space and show the popover in front
Multi-monitor or hidden-window reuse Positioning may follow the hidden window's last monitor Position from the clicked tray icon's monitor

Root Cause

This is not a React state issue. It comes from AppKit's native management of NSWindow, Spaces, and full-screen Spaces.

flowchart TD
    CLICK[User clicks menu bar icon] --> SHOW[Tauri shows main WebviewWindow]
    SHOW --> SPACE{Which Space owns the window?}
    SPACE -->|Reusable hidden window| OLD[macOS may return to the original Space]
    SPACE -->|Only joins all regular Spaces| FS[Full-screen Space may not order the window in front]
    OLD --> BAD[User is moved away from the current Space]
    FS --> HIDDEN[Popover exists but is not visible above the full-screen app]
Loading
Cause Details
Reused hidden window Tokcat reuses the same main WebviewWindow. After hide(), a later show() can preserve the original Space relationship.
set_focus() is not enough for full-screen Spaces Full-screen apps use dedicated Spaces, and normal focus/order behavior may not bring an accessory app window forward there.
Monitor lookup used window state current_monitor() can reflect the hidden window's previous location instead of the menu bar icon that was clicked.

Implementation

File Change Purpose
src-tauri/tauri.conf.json Add visibleOnAllWorkspaces: true Set the cross-Space intent when the Tauri window is created.
src-tauri/src/tray.rs Call prepare_popover_window() before showing the popover Reapply native macOS window behavior every time the popover opens.
src-tauri/src/tray.rs Add CanJoinAllSpaces, CanJoinAllApplications, FullScreenAuxiliary, IgnoresCycle, and Transient Allow the popover to participate in regular Spaces and full-screen app Spaces while keeping popover-like behavior.
src-tauri/src/tray.rs Set NSPopUpMenuWindowLevel Use a window level appropriate for menu/popover UI so it appears above full-screen app content.
src-tauri/src/tray.rs Call orderFrontRegardless() after show() and set_focus() Force the popover to the foreground in full-screen Spaces.
src-tauri/src/tray.rs Use monitor_from_point(tray_x, tray_y) instead of current_monitor() Position relative to the clicked menu bar icon, not the hidden window's previous monitor.

Open Flow

flowchart TD
    CLICK[Menu bar icon click] --> GETWIN[Get main WebviewWindow]
    GETWIN --> VISIBLE{Is it visible?}
    VISIBLE -->|Yes| HIDE[Hide popover]
    VISIBLE -->|No| PREP[prepare_popover_window]
    PREP --> POSITION[Position from tray rect and tray monitor]
    POSITION --> SHOW[window.show]
    SHOW --> FRONT1[orderFrontRegardless]
    FRONT1 --> FOCUS[set_focus]
    FOCUS --> FRONT2[orderFrontRegardless]
    FRONT2 --> EVENT[emit popover-shown]
Loading

Verification

Check Result
Rust formatting Passed
Tauri debug build Passed with existing vendored/native warnings
macOS app bundle Passed and produced a debug Tokcat.app
Manual regular Space test Passed; macOS no longer jumps back to Space A
Manual full-screen Space test Passed; the popover appears in the current full-screen Space

Commands run locally:

rustfmt --check src/tray.rs
npx tauri build --debug --no-bundle
npx tauri build --debug --bundles app --no-sign --config '{"bundle":{"createUpdaterArtifacts":false}}'

Manual test coverage:

Scenario Result
Open in Space A, switch to Space B, click the menu bar icon Passed; the current Space is preserved
Enter a macOS full-screen app Space and click the menu bar icon Passed; the popover appears in that full-screen Space

Note: The app-bundle command disables signing and updater artifacts only for local verification. The normal release pipeline should keep using the existing signing and updater artifact flow.

Risk And Tradeoffs

Area Assessment
Platform scope Native behavior is guarded by #[cfg(target_os = "macos")]; non-macOS keeps the existing set_visible_on_all_workspaces(true) path.
Window level NSPopUpMenuWindowLevel matches menu/popover UI and fixes foreground ordering in full-screen Spaces.
AppKit surface area The change uses Tauri's ns_window() handle and objc2 AppKit APIs without modifying Tauri runtime or vendored tray-icon code.
UX behavior IgnoresCycle and Transient keep the popover out of normal window cycling, matching expected menubar-popover behavior.

Implementation Summary

src-tauri/src/tray.rs
├── prepare_popover_window()
│   ├── set_visible_on_all_workspaces(true)
│   ├── setCollectionBehavior(...FullScreenAuxiliary...)
│   └── setLevel(NSPopUpMenuWindowLevel)
├── bring_popover_to_front()
│   └── orderFrontRegardless()
└── position_window_under_tray()
    └── monitor_from_point(tray_x, tray_y)

Issue

Fixes #13

@Nanako0129 Nanako0129 force-pushed the fix-menubar-space-switch branch from 509e163 to 4dee08e Compare June 3, 2026 06:07
@handlecusion

Copy link
Copy Markdown
Owner

Thanks for the detailed fix and write-up, @Nanako0129 .

I reproduced the full-screen Space issue locally against main and confirmed this PR makes the popover appear in the current full-screen Space.

I pushed one small follow-up commit (9b61b96) to apply the same native window preparation and foreground ordering to the global shortcut path (Ctrl+Cmd+T) as well, so tray clicks, tray menu actions, and the shortcut now share the same macOS Space behavior.

@handlecusion handlecusion merged commit 1b514ab into handlecusion:main Jun 4, 2026
2 checks passed
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.

Menubar popover should open in the current macOS Space

2 participants