diff --git a/docs/content.en/docs/release-notes/_index.md b/docs/content.en/docs/release-notes/_index.md index 2f19bccb..d83c741f 100644 --- a/docs/content.en/docs/release-notes/_index.md +++ b/docs/content.en/docs/release-notes/_index.md @@ -37,6 +37,8 @@ chore: add cross-domain configuration for web component #921 refactor: retry if AXUIElementSetAttributeValue() does not work #924 refactor(calculator): skip evaluation if expr is in form "num => num" #929 chore: use a custom log directory #930 +chore: bump tauri_nspanel to v2.1 #933 +refactor: show_coco/hide_coco now use NSPanel's function on macOS #933 ## 0.8.0 (2025-09-28) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 313a4386..cc099a63 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -976,7 +976,7 @@ dependencies = [ "block", "cocoa-foundation", "core-foundation 0.10.1", - "core-graphics 0.24.0", + "core-graphics", "foreign-types 0.5.0", "libc", "objc", @@ -1126,19 +1126,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-graphics" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" -dependencies = [ - "bitflags 2.9.4", - "core-foundation 0.10.1", - "core-graphics-types", - "foreign-types 0.5.0", - "libc", -] - [[package]] name = "core-graphics-types" version = "0.2.0" @@ -1561,7 +1548,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67fd9ae1736d6ebb2e472740fbee86fb2178b8d56feb98a6751411d4c95b7e72" dependencies = [ "cocoa", - "core-graphics 0.24.0", + "core-graphics", "dunce", "gdk", "gdkx11", @@ -1656,7 +1643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cf6f550bbbdd5fe66f39d429cb2604bcdacbf00dca0f5bbe2e9306a0009b7c6" dependencies = [ "core-foundation 0.10.1", - "core-graphics 0.24.0", + "core-graphics", "foreign-types-shared 0.3.1", "libc", "log", @@ -4126,17 +4113,6 @@ dependencies = [ "malloc_buf", ] -[[package]] -name = "objc-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - [[package]] name = "objc-sys" version = "0.3.5" @@ -4479,15 +4455,6 @@ dependencies = [ "objc2-security", ] -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - [[package]] name = "object" version = "0.36.7" @@ -4709,6 +4676,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + [[package]] name = "path-clean" version = "1.0.1" @@ -6279,7 +6252,7 @@ checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" dependencies = [ "bytemuck", "cfg_aliases", - "core-graphics 0.24.0", + "core-graphics", "foreign-types 0.5.0", "js-sys", "log", @@ -6527,7 +6500,7 @@ dependencies = [ "bitflags 2.9.4", "block2 0.6.1", "core-foundation 0.10.1", - "core-graphics 0.24.0", + "core-graphics", "crossbeam-channel", "dispatch", "dlopen2", @@ -6727,17 +6700,13 @@ dependencies = [ [[package]] name = "tauri-nspanel" -version = "2.0.1" -source = "git+https://github.com/ahkohd/tauri-nspanel?branch=v2#18ffb9a201fbf6fedfaa382fd4b92315ea30ab1a" +version = "2.1.0" +source = "git+https://github.com/ahkohd/tauri-nspanel?branch=v2.1#da9c9a8d4eb7f0524a2508988df1a7d9585b4904" dependencies = [ - "bitflags 2.9.4", - "block", - "cocoa", - "core-foundation 0.10.1", - "core-graphics 0.25.0", - "objc", - "objc-foundation", - "objc_id", + "objc2 0.6.2", + "objc2-app-kit 0.3.1", + "objc2-foundation 0.3.1", + "pastey", "tauri", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 5f5eb76e..76cde3bf 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -122,7 +122,7 @@ path-clean = "1.0.1" tempfile = "3.23.0" [target."cfg(target_os = \"macos\")".dependencies] -tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" } +tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2.1" } objc2-app-kit = { version = "0.3.1", features = ["NSWindow"] } objc2 = "0.6.2" objc2-core-foundation = {version = "0.3.1", features = ["CFString", "CFCGTypes", "CFArray"] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 12aa7f20..11d1a43d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -267,35 +267,57 @@ async fn show_coco(app_handle: AppHandle) { if let Some(window) = app_handle.get_webview_window(MAIN_WINDOW_LABEL) { move_window_to_active_monitor(&window); - let _ = window.show(); - let _ = window.unminimize(); + cfg_if::cfg_if! { + if #[cfg(target_os = "macos")] { + use tauri_nspanel::ManagerExt; - // The Window Management (WM) extension (macOS-only) controls the - // frontmost window. Setting focus on macOS makes Coco the frontmost - // window, which means the WM extension would control Coco instead of other - // windows, which is not what we want. - // - // On Linux/Windows, however, setting focus is a necessity to ensure that - // users open Coco's window, then they can start typing, without needing - // to click on the window. - #[cfg(not(target_os = "macos"))] - let _ = window.set_focus(); + let app_handle_clone = app_handle.clone(); + + app_handle.run_on_main_thread(move || { + let panel = app_handle_clone.get_webview_panel(MAIN_WINDOW_LABEL).unwrap(); + + panel.show_and_make_key(); + }).unwrap(); + } else { + let _ = window.show(); + let _ = window.unminimize(); + // The Window Management (WM) extension (macOS-only) controls the + // frontmost window. Setting focus on macOS makes Coco the frontmost + // window, which means the WM extension would control Coco instead of other + // windows, which is not what we want. + // + // On Linux/Windows, however, setting focus is a necessity to ensure that + // users open Coco's window, then they can start typing, without needing + // to click on the window. + let _ = window.set_focus(); + } + }; let _ = app_handle.emit("show-coco", ()); } } #[tauri::command] -async fn hide_coco(app: AppHandle) { - if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) { - if let Err(err) = window.hide() { - log::error!("Failed to hide the window: {}", err); +async fn hide_coco(app_handle: AppHandle) { + cfg_if::cfg_if! { + if #[cfg(target_os = "macos")] { + use tauri_nspanel::ManagerExt; + + let app_handle_clone = app_handle.clone(); + app_handle.run_on_main_thread(move || { + let panel = app_handle_clone.get_webview_panel(MAIN_WINDOW_LABEL).expect("cannot find the main window/panel"); + panel.hide(); + }).unwrap(); } else { - log::debug!("Window successfully hidden."); + let window = app_handle.get_webview_window(MAIN_WINDOW_LABEL).expect("cannot find the main window"); + + if let Err(err) = window.hide() { + log::error!("Failed to hide the window: {}", err); + } else { + log::debug!("Window successfully hidden."); + } } - } else { - log::error!("Main window not found."); - } + }; } fn move_window_to_active_monitor(window: &WebviewWindow) { diff --git a/src-tauri/src/setup/mac.rs b/src-tauri/src/setup/mac.rs index 196cf152..d91e9c62 100644 --- a/src-tauri/src/setup/mac.rs +++ b/src-tauri/src/setup/mac.rs @@ -1,23 +1,26 @@ //! credits to: https://github.com/ayangweb/ayangweb-EcoPaste/blob/169323dbe6365ffe4abb64d867439ed2ea84c6d1/src-tauri/src/core/setup/mac.rs -//! -//! # allow(deprecated) -//! -//! This file uses some deprecated interfaces from the `tauri_nspanel` crate. The -//! only way to get rid of them is to bump that crate (v2->v2.1), we are not going -//! to do that because doing that bump requires a re-write of the code in this -//! file and v2 has been working well. So we allow these deprecated interfaces. use crate::common::MAIN_WINDOW_LABEL; -use objc2_app_kit::NSNonactivatingPanelMask; -use tauri::{AppHandle, Emitter, EventTarget, WebviewWindow}; -#[allow(deprecated)] -use tauri_nspanel::cocoa::appkit::NSWindowCollectionBehavior; -use tauri_nspanel::{WebviewWindowExt, panel_delegate}; +use tauri::{AppHandle, Emitter, EventTarget, Manager, WebviewWindow}; +use tauri_nspanel::{CollectionBehavior, PanelLevel, StyleMask, WebviewWindowExt, tauri_panel}; const WINDOW_FOCUS_EVENT: &str = "tauri://focus"; const WINDOW_BLUR_EVENT: &str = "tauri://blur"; -const WINDOW_MOVED_EVENT: &str = "tauri://move"; -const WINDOW_RESIZED_EVENT: &str = "tauri://resize"; + +tauri_panel! { + panel!(NsPanel { + config: { + is_floating_panel: true, + can_become_key_window: true, + can_become_main_window: false + } + }) + + panel_event!(NsPanelEventHandler { + window_did_become_key(notification: &NSNotification) -> (), + window_did_resign_key(notification: &NSNotification) -> (), + }) +} pub fn platform( _tauri_app_handle: &AppHandle, @@ -26,70 +29,39 @@ pub fn platform( _check_window: WebviewWindow, ) { // Convert ns_window to ns_panel - let panel = main_window.to_panel().unwrap(); + let panel = main_window.to_panel::().unwrap(); + + // set level + panel.set_level(PanelLevel::Utility.value()); // Do not steal focus from other windows - // - // Cast is safe - panel.set_style_mask(NSNonactivatingPanelMask.0 as i32); - // Set its level to NSFloatingWindowLevel to ensure it appears in front of - // all normal-level windows - // - // NOTE: some Chinese input methods use a level between NSDockWindowLevel (20) - // and NSMainMenuWindowLevel (24), setting our level above NSDockWindowLevel - // would block their window - panel.set_floating_panel(true); + panel.set_style_mask(StyleMask::empty().nonactivating_panel().into()); // Open the window in the active workspace and full screen - #[allow(deprecated)] - panel.set_collection_behaviour( - NSWindowCollectionBehavior::NSWindowCollectionBehaviorMoveToActiveSpace - | NSWindowCollectionBehavior::NSWindowCollectionBehaviorStationary - | NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenAuxiliary, + panel.set_collection_behavior( + CollectionBehavior::new() + .stationary() + .move_to_active_space() + .full_screen_auxiliary() + .into(), ); - // Define the panel's delegate to listen to panel window events - #[allow(deprecated)] - let delegate = panel_delegate!(EcoPanelDelegate { - window_did_become_key, - window_did_resign_key, - window_did_resize, - window_did_move - }); + let handler = NsPanelEventHandler::new(); - // Set event listeners for the delegate - delegate.set_listener(Box::new(move |delegate_name: String| { + let window = main_window.clone(); + handler.window_did_become_key(move |_| { let target = EventTarget::labeled(MAIN_WINDOW_LABEL); - let window_move_event = || { - if let Ok(position) = main_window.outer_position() { - let _ = main_window.emit_to(target.clone(), WINDOW_MOVED_EVENT, position); - } - }; + let _ = window.emit_to(target, WINDOW_FOCUS_EVENT, true); + }); - match delegate_name.as_str() { - // Called when the window gets keyboard focus - "window_did_become_key" => { - let _ = main_window.emit_to(target, WINDOW_FOCUS_EVENT, true); - } - // Called when the window loses keyboard focus - "window_did_resign_key" => { - let _ = main_window.emit_to(target, WINDOW_BLUR_EVENT, true); - } - // Called when the window size changes - "window_did_resize" => { - window_move_event(); + let window = main_window.clone(); + handler.window_did_resign_key(move |_| { + let target = EventTarget::labeled(MAIN_WINDOW_LABEL); - if let Ok(size) = main_window.inner_size() { - let _ = main_window.emit_to(target, WINDOW_RESIZED_EVENT, size); - } - } - // Called when the window position changes - "window_did_move" => window_move_event(), - _ => (), - } - })); + let _ = window.emit_to(target, WINDOW_BLUR_EVENT, true); + }); // Set the delegate object for the window to handle window events - panel.set_delegate(delegate); + panel.set_event_handler(Some(handler.as_ref())); }