Skip to content

Commit 635815f

Browse files
authored
Merge pull request #356 from kdroidFilter/fix/tray-popup-wrong-space
Fix TrayApp opening on wrong Space (macOS)
2 parents 8fbfa4c + e94a5d6 commit 635815f

4 files changed

Lines changed: 66 additions & 12 deletions

File tree

maclib/tray.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,4 +424,16 @@ public func tray_get_status_item_region_for(
424424
let midX = screen.frame.midX
425425
let region = rect.minX < midX ? "top-left" : "top-right"
426426
return strdup(region)
427+
}
428+
429+
// MARK: - Spaces / Virtual Desktop support
430+
431+
/// Sets NSWindowCollectionBehavior.moveToActiveSpace on all app windows
432+
/// so that showing a window moves it to the current Space instead of
433+
/// switching back to the Space where it was originally created.
434+
@_cdecl("tray_set_windows_move_to_active_space")
435+
public func tray_set_windows_move_to_active_space() {
436+
for window in NSApp.windows {
437+
window.collectionBehavior.insert(.moveToActiveSpace)
438+
}
427439
}

src/commonMain/kotlin/com/kdroid/composetray/lib/mac/MacOsWindowManager.kt

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,6 @@ interface Foundation : Library {
2929

3030
class MacOSWindowManager {
3131

32-
companion object {
33-
// Constants for NSApplication activation policies
34-
const val NSApplicationActivationPolicyRegular = 0L
35-
const val NSApplicationActivationPolicyAccessory = 1L
36-
const val NSApplicationActivationPolicyProhibited = 2L
37-
38-
// Constants for window levels
39-
const val NSNormalWindowLevel = 0L
40-
const val NSFloatingWindowLevel = 3L
41-
const val NSModalPanelWindowLevel = 8L
42-
}
43-
4432
// Detect platform once
4533
private val isMacOs: Boolean = getOperatingSystem() == OperatingSystem.MACOS
4634

@@ -206,5 +194,51 @@ class MacOSWindowManager {
206194
return getNSApplication() != null
207195
}
208196

197+
/**
198+
* Configure an AWT window so that macOS moves it to the active Space
199+
* when it is ordered front, instead of switching back to the Space
200+
* where the window was originally created.
201+
*/
202+
fun setMoveToActiveSpace(awtWindow: java.awt.Window): Boolean {
203+
if (!isMacOs) return false
204+
val localObjc = objc ?: return false
205+
return try {
206+
val viewPtr = Native.getComponentID(awtWindow)
207+
if (viewPtr == 0L) return false
208+
209+
val nsView = Pointer(viewPtr)
210+
val windowSel = localObjc.sel_registerName("window")
211+
val nsWindow = localObjc.objc_msgSend(nsView, windowSel)
212+
if (nsWindow == Pointer.NULL) return false
213+
214+
// Read current collectionBehavior and add moveToActiveSpace (1 << 1)
215+
val getCollSel = localObjc.sel_registerName("collectionBehavior")
216+
val current = Pointer.nativeValue(localObjc.objc_msgSend(nsWindow, getCollSel))
217+
val setCollSel = localObjc.sel_registerName("setCollectionBehavior:")
218+
localObjc.objc_msgSend(nsWindow, setCollSel, current or NSWindowCollectionBehaviorMoveToActiveSpace)
219+
220+
debugln { "Window configured to move to active Space" }
221+
true
222+
} catch (e: Throwable) {
223+
debugln { "Failed to set moveToActiveSpace: ${e.message}" }
224+
false
225+
}
226+
}
227+
228+
companion object {
229+
// Constants for NSApplication activation policies
230+
const val NSApplicationActivationPolicyRegular = 0L
231+
const val NSApplicationActivationPolicyAccessory = 1L
232+
const val NSApplicationActivationPolicyProhibited = 2L
233+
234+
// Constants for window levels
235+
const val NSNormalWindowLevel = 0L
236+
const val NSFloatingWindowLevel = 3L
237+
const val NSModalPanelWindowLevel = 8L
238+
239+
// NSWindowCollectionBehavior
240+
const val NSWindowCollectionBehaviorMoveToActiveSpace = 2L // 1 << 1
241+
}
242+
209243
}
210244

src/commonMain/kotlin/com/kdroid/composetray/lib/mac/MacTrayManager.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,8 @@ internal class MacTrayManager(
349349

350350
@JvmStatic external fun tray_get_status_item_region(): String?
351351
@JvmStatic external fun tray_get_status_item_region_for(tray: MacTray): String?
352+
353+
@JvmStatic external fun tray_set_windows_move_to_active_space()
352354
}
353355

354356
// Structure for a menu item

src/commonMain/kotlin/com/kdroid/composetray/tray/api/TrayApp.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import androidx.compose.ui.window.*
2525
import com.kdroid.composetray.lib.linux.LinuxOutsideClickWatcher
2626
import com.kdroid.composetray.lib.mac.MacOSWindowManager
2727
import com.kdroid.composetray.lib.mac.MacOutsideClickWatcher
28+
import com.kdroid.composetray.lib.mac.MacTrayLoader
2829
import com.kdroid.composetray.lib.windows.WindowsOutsideClickWatcher
2930
import com.kdroid.composetray.menu.api.TrayMenuBuilder
3031
import com.kdroid.composetray.utils.*
@@ -620,6 +621,11 @@ private fun ApplicationScope.TrayAppImplOriginal(
620621
runCatching { WindowVisibilityMonitor.recompute() }
621622

622623
invokeLater {
624+
// Move the popup to the current Space before bringing it to front (macOS)
625+
if (getOperatingSystem() == MACOS) {
626+
runCatching { MacTrayLoader.lib.tray_set_windows_move_to_active_space() }
627+
runCatching { MacOSWindowManager().setMoveToActiveSpace(window) }
628+
}
623629
runCatching {
624630
window.toFront()
625631
window.requestFocus()

0 commit comments

Comments
 (0)