From 8bd1109ffb4fe396706bc4dbade2b2446f834809 Mon Sep 17 00:00:00 2001 From: xialuoluo Date: Fri, 29 May 2026 01:03:07 +0800 Subject: [PATCH 1/2] feat(cardinal):Bring window to focus on the active Space(#188) --- cardinal/src-tauri/src/window_controls.rs | 91 +++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/cardinal/src-tauri/src/window_controls.rs b/cardinal/src-tauri/src/window_controls.rs index e07b1660..b3a2e974 100644 --- a/cardinal/src-tauri/src/window_controls.rs +++ b/cardinal/src-tauri/src/window_controls.rs @@ -1,4 +1,6 @@ use crate::commands::SearchState; +#[cfg(target_os = "macos")] +use objc2_app_kit::{NSWindow, NSWindowCollectionBehavior}; use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow}; use tracing::{error, info, warn}; @@ -10,6 +12,10 @@ pub enum WindowToggle { } pub fn activate_window(window: &WebviewWindow) { + activate_window_on_platform(window); +} + +fn activate_window_impl(window: &WebviewWindow) { if let Ok(true) = window.is_minimized() && let Err(err) = window.unminimize() { @@ -27,6 +33,53 @@ pub fn activate_window(window: &WebviewWindow) { } } +#[cfg(target_os = "macos")] +fn activate_window_on_platform(window: &WebviewWindow) { + let window_for_task = window.clone(); + if let Err(err) = window.run_on_main_thread(move || { + apply_active_space_behavior(&window_for_task); + activate_window_impl(&window_for_task); + }) { + error!(?err, "Failed to schedule macOS window activation"); + activate_window_impl(window); + } +} + +#[cfg(not(target_os = "macos"))] +fn activate_window_on_platform(window: &WebviewWindow) { + activate_window_impl(window); +} + +#[cfg(target_os = "macos")] +fn apply_active_space_behavior(window: &WebviewWindow) { + let ns_window = match window.ns_window() { + Ok(ns_window) => ns_window, + Err(err) => { + error!(?err, "Failed to get macOS NSWindow"); + return; + } + }; + + if ns_window.is_null() { + warn!("macOS NSWindow pointer is null"); + return; + } + + // Move the existing window to whichever Space is active before AppKit focuses it. + let ns_window = unsafe { &*ns_window.cast::() }; + let behavior = collection_behavior_for_active_space(ns_window.collectionBehavior()); + ns_window.setCollectionBehavior(behavior); +} + +#[cfg(target_os = "macos")] +fn collection_behavior_for_active_space( + mut behavior: NSWindowCollectionBehavior, +) -> NSWindowCollectionBehavior { + behavior.remove(NSWindowCollectionBehavior::CanJoinAllSpaces); + behavior.insert(NSWindowCollectionBehavior::MoveToActiveSpace); + behavior +} + pub fn hide_window(window: &WebviewWindow) -> bool { if let Err(err) = window.hide() { error!(?err, "Failed to hide window"); @@ -108,3 +161,41 @@ pub fn toggle_main_window_impl(app: &AppHandle) { warn!("Toggle requested but main window is unavailable"); } } + +#[cfg(all(test, target_os = "macos"))] +mod tests { + use super::*; + use objc2_app_kit::NSWindowCollectionBehavior; + + #[test] + fn active_space_behavior_moves_window_without_joining_all_spaces() { + let behavior = collection_behavior_for_active_space(NSWindowCollectionBehavior::Default); + + assert!(behavior.contains(NSWindowCollectionBehavior::MoveToActiveSpace)); + assert!(!behavior.contains(NSWindowCollectionBehavior::CanJoinAllSpaces)); + } + + #[test] + fn active_space_behavior_preserves_unrelated_flags() { + let existing = + NSWindowCollectionBehavior::Managed | NSWindowCollectionBehavior::ParticipatesInCycle; + + let behavior = collection_behavior_for_active_space(existing); + + assert!(behavior.contains(NSWindowCollectionBehavior::Managed)); + assert!(behavior.contains(NSWindowCollectionBehavior::ParticipatesInCycle)); + assert!(behavior.contains(NSWindowCollectionBehavior::MoveToActiveSpace)); + } + + #[test] + fn active_space_behavior_clears_all_spaces_visibility() { + let existing = + NSWindowCollectionBehavior::CanJoinAllSpaces | NSWindowCollectionBehavior::Managed; + + let behavior = collection_behavior_for_active_space(existing); + + assert!(!behavior.contains(NSWindowCollectionBehavior::CanJoinAllSpaces)); + assert!(behavior.contains(NSWindowCollectionBehavior::MoveToActiveSpace)); + assert!(behavior.contains(NSWindowCollectionBehavior::Managed)); + } +} From 9118ee02185dd50ca26f307a2c8228e5e70874d0 Mon Sep 17 00:00:00 2001 From: xialuoluo Date: Sun, 31 May 2026 21:40:27 +0800 Subject: [PATCH 2/2] refactor(cardinal): simplify macos window activation --- cardinal/src-tauri/src/window_controls.rs | 33 +++++++---------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/cardinal/src-tauri/src/window_controls.rs b/cardinal/src-tauri/src/window_controls.rs index b3a2e974..335310a7 100644 --- a/cardinal/src-tauri/src/window_controls.rs +++ b/cardinal/src-tauri/src/window_controls.rs @@ -1,5 +1,4 @@ use crate::commands::SearchState; -#[cfg(target_os = "macos")] use objc2_app_kit::{NSWindow, NSWindowCollectionBehavior}; use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow}; use tracing::{error, info, warn}; @@ -12,10 +11,17 @@ pub enum WindowToggle { } pub fn activate_window(window: &WebviewWindow) { - activate_window_on_platform(window); + let window_for_task = window.clone(); + if let Err(err) = window.run_on_main_thread(move || { + apply_active_space_behavior(&window_for_task); + show_and_focus_window(&window_for_task); + }) { + error!(?err, "Failed to schedule macOS window activation"); + show_and_focus_window(window); + } } -fn activate_window_impl(window: &WebviewWindow) { +fn show_and_focus_window(window: &WebviewWindow) { if let Ok(true) = window.is_minimized() && let Err(err) = window.unminimize() { @@ -33,24 +39,6 @@ fn activate_window_impl(window: &WebviewWindow) { } } -#[cfg(target_os = "macos")] -fn activate_window_on_platform(window: &WebviewWindow) { - let window_for_task = window.clone(); - if let Err(err) = window.run_on_main_thread(move || { - apply_active_space_behavior(&window_for_task); - activate_window_impl(&window_for_task); - }) { - error!(?err, "Failed to schedule macOS window activation"); - activate_window_impl(window); - } -} - -#[cfg(not(target_os = "macos"))] -fn activate_window_on_platform(window: &WebviewWindow) { - activate_window_impl(window); -} - -#[cfg(target_os = "macos")] fn apply_active_space_behavior(window: &WebviewWindow) { let ns_window = match window.ns_window() { Ok(ns_window) => ns_window, @@ -71,7 +59,6 @@ fn apply_active_space_behavior(window: &WebviewWindow) { ns_window.setCollectionBehavior(behavior); } -#[cfg(target_os = "macos")] fn collection_behavior_for_active_space( mut behavior: NSWindowCollectionBehavior, ) -> NSWindowCollectionBehavior { @@ -162,7 +149,7 @@ pub fn toggle_main_window_impl(app: &AppHandle) { } } -#[cfg(all(test, target_os = "macos"))] +#[cfg(test)] mod tests { use super::*; use objc2_app_kit::NSWindowCollectionBehavior;