From ba3471291a3e343f335261d83aa9ab23fe5f699f Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Sun, 24 May 2026 18:52:37 +0800
Subject: [PATCH] Keep secondary windows in fullscreen Space
---
Release/AppStore-7.6.0-20260524.1849/QA_RELEASE_EVIDENCE.md | 8 ++-
Apptag/ApptagApp.swift | 39 ++++++++++++++++---
Scripts/window_logic_qa.sh | 48 ++++++++++++++++++++++++
Release/AppStore-7.6.0-20260524.1849/README.md | 5 +-
Release/AppStore-7.6.0-20260524.1849/APP_STORE_CONNECT_METADATA.md | 0
Release/AppStore-7.6.0-20260524.1849/BUILD_SIGN_UPLOAD.md | 2
Release/AppStore-7.6.0-20260524.1849/FIRST_TIME_MAC_APP_STORE_TODO.md | 0
7 files changed, 89 insertions(+), 13 deletions(-)
diff --git a/Apptag/ApptagApp.swift b/Apptag/ApptagApp.swift
index ea0f0e3..214816c 100644
--- a/Apptag/ApptagApp.swift
+++ b/Apptag/ApptagApp.swift
@@ -67,6 +67,7 @@
private var lastShowDockIcon: Bool?
private var statusMenuScreenForNextOverlay: NSScreen?
private var overlayGeneration = 0
+ private var overlayAvoidsSpaceSwitch = false
private var suppressReopenUntil = Date.distantPast
private struct OverlayPlacementContext {
@@ -269,9 +270,8 @@
let showDock = UserDefaults.standard.bool(forKey: Self.showDockIconKey)
lastShowDockIcon = showDock
- let shouldStayAccessoryForCurrentFullscreenSpace = avoidSpaceSwitch
- && isOverlayVisible
- && !isSettingsVisible
+ let shouldStayAccessoryForCurrentFullscreenSpace = isOverlayVisible
+ && (avoidSpaceSwitch || overlayAvoidsSpaceSwitch)
let desiredPolicy: NSApplication.ActivationPolicy = shouldStayAccessoryForCurrentFullscreenSpace
? .accessory
: (requiresForegroundOwnership
@@ -784,6 +784,7 @@
let placement = overlayPlacementContextForNextOverlay(preferredScreen: preferredScreen)
let shouldAvoidSpaceSwitch = placement.map { hasFullscreenWindowOnScreen($0.screen) } ?? false
if let overlayWindow, overlayWindow.isVisible {
+ overlayAvoidsSpaceSwitch = shouldAvoidSpaceSwitch
moveOverlayToCurrentPlacement(preferredScreen: preferredScreen)
overlayWindow.level = currentOverlayLevel
refreshLauncherChromeState(activate: !shouldAvoidSpaceSwitch, avoidSpaceSwitch: shouldAvoidSpaceSwitch)
@@ -839,6 +840,7 @@
existingWindow.orderOut(nil)
overlayWindow = nil
}
+ overlayAvoidsSpaceSwitch = shouldAvoidSpaceSwitch
let window = makeOverlayWindow(
on: placement.screen,
@@ -1098,6 +1100,7 @@
overlayWindow?.orderOut(nil)
removeOverlayKeyMonitor()
removeQuickSearchExternalMouseMonitor()
+ overlayAvoidsSpaceSwitch = false
refreshLauncherChromeState()
NotificationCenter.default.post(name: .tagLauncherOverlayDidHide, object: nil)
if discardWindow {
@@ -1203,7 +1206,10 @@
private func showQuickSearchFromGlobalHotkey() {
if overlayWindow?.isVisible == true {
- refreshLauncherChromeState(activate: true)
+ refreshLauncherChromeState(
+ activate: !overlayAvoidsSpaceSwitch,
+ avoidSpaceSwitch: overlayAvoidsSpaceSwitch
+ )
requestQuickSearch(source: QuickSearchOpenSource.globalVisible)
return
}
@@ -1299,12 +1305,20 @@
var behavior = window.collectionBehavior
behavior.remove(.canJoinAllSpaces)
- behavior.formUnion([.fullScreenAuxiliary, .moveToActiveSpace])
+ if overlayAvoidsSpaceSwitch {
+ behavior.remove(.moveToActiveSpace)
+ behavior.formUnion([.fullScreenAuxiliary, .stationary, .transient, .ignoresCycle])
+ } else {
+ behavior.formUnion([.fullScreenAuxiliary, .moveToActiveSpace])
+ }
window.collectionBehavior = behavior
window.makeKeyAndOrderFront(nil)
window.orderFrontRegardless()
settingsWindow = window
- refreshLauncherChromeState(activate: true)
+ refreshLauncherChromeState(
+ activate: !overlayAvoidsSpaceSwitch,
+ avoidSpaceSwitch: overlayAvoidsSpaceSwitch
+ )
}
private func attachSettingsWindow(_ window: NSWindow, to overlayWindow: NSWindow) {
@@ -1454,6 +1468,13 @@
}
private func promoteOverlayToForegroundInput() {
+ if overlayAvoidsSpaceSwitch {
+ refreshLauncherChromeState(activate: false, avoidSpaceSwitch: true)
+ guard let overlayWindow else { return }
+ overlayWindow.makeKeyAndOrderFront(nil)
+ overlayWindow.orderFrontRegardless()
+ return
+ }
beginLauncherForegroundOwnership()
guard let overlayWindow else { return }
overlayWindow.makeKeyAndOrderFront(nil)
@@ -1551,7 +1572,11 @@
@objc private func openPreferences(_ sender: Any? = nil) {
TagDatabase.flushPendingCategorySchemeBackupBatch()
- beginLauncherForegroundOwnership()
+ if overlayAvoidsSpaceSwitch {
+ refreshLauncherChromeState(activate: false, avoidSpaceSwitch: true)
+ } else {
+ beginLauncherForegroundOwnership()
+ }
// Don't hide overlay — keep it visible for real-time setting preview.
if let overlayWindow, overlayWindow.isVisible {
overlayWindow.makeKeyAndOrderFront(nil)
diff --git a/Release/AppStore-7.6.0-20260524.1652/APP_STORE_CONNECT_METADATA.md b/Release/AppStore-7.6.0-20260524.1849/APP_STORE_CONNECT_METADATA.md
similarity index 100%
rename from Release/AppStore-7.6.0-20260524.1652/APP_STORE_CONNECT_METADATA.md
rename to Release/AppStore-7.6.0-20260524.1849/APP_STORE_CONNECT_METADATA.md
diff --git a/Release/AppStore-7.6.0-20260524.1652/BUILD_SIGN_UPLOAD.md b/Release/AppStore-7.6.0-20260524.1849/BUILD_SIGN_UPLOAD.md
similarity index 98%
rename from Release/AppStore-7.6.0-20260524.1652/BUILD_SIGN_UPLOAD.md
rename to Release/AppStore-7.6.0-20260524.1849/BUILD_SIGN_UPLOAD.md
index e1e5d2c..d2514a5 100644
--- a/Release/AppStore-7.6.0-20260524.1652/BUILD_SIGN_UPLOAD.md
+++ b/Release/AppStore-7.6.0-20260524.1849/BUILD_SIGN_UPLOAD.md
@@ -50,7 +50,7 @@
APP_STORE=1 \
CODESIGN_IDENTITY="3rd Party Mac Developer Application: <TEAM NAME> (<TEAMID>)" \
-APP_BUILD=20260524.1652 \
+APP_BUILD=20260524.1849 \
bash build.sh
productbuild \
diff --git a/Release/AppStore-7.6.0-20260524.1652/FIRST_TIME_MAC_APP_STORE_TODO.md b/Release/AppStore-7.6.0-20260524.1849/FIRST_TIME_MAC_APP_STORE_TODO.md
similarity index 100%
rename from Release/AppStore-7.6.0-20260524.1652/FIRST_TIME_MAC_APP_STORE_TODO.md
rename to Release/AppStore-7.6.0-20260524.1849/FIRST_TIME_MAC_APP_STORE_TODO.md
diff --git a/Release/AppStore-7.6.0-20260524.1652/QA_RELEASE_EVIDENCE.md b/Release/AppStore-7.6.0-20260524.1849/QA_RELEASE_EVIDENCE.md
similarity index 88%
rename from Release/AppStore-7.6.0-20260524.1652/QA_RELEASE_EVIDENCE.md
rename to Release/AppStore-7.6.0-20260524.1849/QA_RELEASE_EVIDENCE.md
index 273d9c5..5f28d6e 100644
--- a/Release/AppStore-7.6.0-20260524.1652/QA_RELEASE_EVIDENCE.md
+++ b/Release/AppStore-7.6.0-20260524.1849/QA_RELEASE_EVIDENCE.md
@@ -3,7 +3,7 @@
## Local Build
- Version: `7.6.0`
-- Build: `20260524.1652`
+- Build: `20260524.1849`
- App path: `/Users/ar/Projects/Taglauncher/build/TagLauncher.app`
- DMG path: `/Users/ar/Projects/Taglauncher/build/TagLauncher.dmg`
- App executable size: `1.4M`
@@ -12,8 +12,8 @@
## Hashes
```text
-1bf3e53410e18778038c310e1ed083cd89c27b781cc87942dd3dd9fb57d73cb2 build/TagLauncher.dmg
-13e74a774662dfec88e69b5f51e5c51c5c46e3e5f8884cbf8cbab5cd44c4442d build/TagLauncher.app/Contents/MacOS/TagLauncher
+dc5bc9cf90b9e04a9e4fda155b757e6e0af681ff1b79bf109552ae0c3ce2a193 build/TagLauncher.dmg
+a59f08f214bd4ac667197f6b2f2672aaf4a57c1fdd9872ea385099e84f18fa57 build/TagLauncher.app/Contents/MacOS/TagLauncher
```
## Commands Run
@@ -42,6 +42,8 @@
- Fullscreen Space overlay appears above the current fullscreen app.
- Fullscreen Space overlay is checked with `showDockIcon=true` and `showDockIcon=false`.
- Fullscreen Space overlay bounds must match the fullscreen target display and remain stable across repeated samples.
+- Fullscreen Space Quick Search opens from app grid without switching away from the current fullscreen app.
+- Fullscreen Space Settings opens from app grid without switching away from the current fullscreen app.
- Split View fullscreen geometry is checked for 50/50 and 33/67 split layouts, while ordinary side-by-side desktop windows are rejected.
- App grid claims foreground, hides Dock, and keeps menu bar visible.
- Settings floats above app grid.
diff --git a/Release/AppStore-7.6.0-20260524.1652/README.md b/Release/AppStore-7.6.0-20260524.1849/README.md
similarity index 93%
rename from Release/AppStore-7.6.0-20260524.1652/README.md
rename to Release/AppStore-7.6.0-20260524.1849/README.md
index 97281f7..b886612 100644
--- a/Release/AppStore-7.6.0-20260524.1652/README.md
+++ b/Release/AppStore-7.6.0-20260524.1849/README.md
@@ -8,9 +8,9 @@
- Platform: macOS / Mac App Store
- Bundle ID: `com.taglauncher.app`
- Version: `7.6.0`
-- Local QA build: `20260524.1652`
+- Local QA build: `20260524.1849`
- Source branch at preparation time: `main`
-- Release folder: `Release/AppStore-7.6.0-20260524.1652`
+- Release folder: `Release/AppStore-7.6.0-20260524.1849`
## What Changed For This Release
@@ -23,6 +23,7 @@
- Added an About-page Help PDF button that opens the online PDF matching the current app language, plus a copy-link button.
- Hardened fullscreen Space presentation so the app grid stays above the current fullscreen app instead of moving to another Space.
- Added Split View fullscreen detection so two apps sharing one fullscreen Space also keep the app grid on the current Space.
+- Kept Quick Search and Settings inside the same fullscreen/Split View Space session instead of activating TagLauncher and sliding to another Space.
- Expanded window QA to cover fullscreen Space behavior with Dock icon both enabled and disabled, including stability checks.
- Strengthened the window logic QA script to cover fullscreen Space, duplicate Dock tiles, repeated self-launch, force quit layering, Settings/file panel layering, Quick Search, and two-display pointer-following behavior.
diff --git a/Scripts/window_logic_qa.sh b/Scripts/window_logic_qa.sh
index 4990885..48f7b31 100755
--- a/Scripts/window_logic_qa.sh
+++ b/Scripts/window_logic_qa.sh
@@ -93,6 +93,26 @@
osascript -e 'tell application "System Events" to keystroke "w" using {command down}'
}
+close_settings_window() {
+ osascript <<'OSA' >/dev/null 2>&1 || true
+tell application "System Events"
+ tell process "TagLauncher"
+ repeat 20 times
+ repeat with windowRef in windows
+ try
+ if (name of windowRef as text) is not "" then
+ click button 1 of windowRef
+ return
+ end if
+ end try
+ end repeat
+ delay 0.1
+ end repeat
+ end tell
+end tell
+OSA
+}
+
dismiss_reopen_dialog() {
osascript <<'OSA' >/dev/null 2>&1 || true
tell application "System Events"
@@ -438,6 +458,20 @@
fail("TagLauncher layer \(tag[0].layer) is not above fullscreen target layer \(target.layer)")
}
print("PASS fullscreen overlay above target: overlayLayer=\(tag[0].layer) targetLayer=\(target.layer)")
+
+case "fullscreen-settings":
+ guard tag.count == 2 else { fail("fullscreen settings expected 2 TagLauncher windows, got \(tag.count)") }
+ assertTagLayer(tag)
+ guard let target = windows.first(where: { $0.name == "TagLauncherFullscreenQATargetFullscreen" }) else {
+ fail("fullscreen target disappeared after opening settings; TagLauncher likely switched to another Space")
+ }
+ guard !tag[0].name.isEmpty, tag[1].name.isEmpty else {
+ fail("fullscreen settings order wrong: \(tag.map(\.name))")
+ }
+ guard tag.allSatisfy({ $0.layer > target.layer }) else {
+ fail("TagLauncher settings stack is not above fullscreen target")
+ }
+ print("PASS fullscreen settings above target: tagLayers=\(tag.map(\.layer)) targetLayer=\(target.layer)")
case "split-geometry":
func isSingleFullscreenWindow(_ windowFrame: CGRect, on screenFrame: CGRect) -> Bool {
@@ -797,6 +831,20 @@
send_main_hotkey
wait_swift_assert fullscreen-overlay
assert_fullscreen_overlay_stable
+ log "==> QA fullscreen Space: quick search from appgrid does not switch Space"
+ send_keycode 49
+ sleep 0.4
+ assert_fullscreen_overlay_stable
+ send_keycode 53
+ sleep 0.3
+ wait_swift_assert fullscreen-overlay
+ log "==> QA fullscreen Space: settings from appgrid does not switch Space"
+ send_cmd_comma
+ sleep 0.7
+ wait_swift_assert fullscreen-settings
+ close_settings_window
+ sleep 0.5
+ wait_swift_assert fullscreen-overlay
send_keycode 53
sleep 0.4
wait_swift_assert no-overlay
--
Gitblit v1.9.3