mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-16 11:37:47 +01:00
refactor: bump tauri_nspanel & show_coco/hide_coco now use NSPanel's function on macOS (#933)
This commit: 1. Bump dep tauri_nspenel to v2.1 2. On macOS, our main window is not a window but panel. Previously, we were using window.show() and window.hide() in show_coco() and hide_coco(). In this commit, we switch from window.show/hide to panel.show/hide Co-authored-by: ayang <473033518@qq.com>
This commit is contained in:
@@ -37,6 +37,8 @@ chore: add cross-domain configuration for web component #921
|
|||||||
refactor: retry if AXUIElementSetAttributeValue() does not work #924
|
refactor: retry if AXUIElementSetAttributeValue() does not work #924
|
||||||
refactor(calculator): skip evaluation if expr is in form "num => num" #929
|
refactor(calculator): skip evaluation if expr is in form "num => num" #929
|
||||||
chore: use a custom log directory #930
|
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)
|
## 0.8.0 (2025-09-28)
|
||||||
|
|
||||||
|
|||||||
65
src-tauri/Cargo.lock
generated
65
src-tauri/Cargo.lock
generated
@@ -976,7 +976,7 @@ dependencies = [
|
|||||||
"block",
|
"block",
|
||||||
"cocoa-foundation",
|
"cocoa-foundation",
|
||||||
"core-foundation 0.10.1",
|
"core-foundation 0.10.1",
|
||||||
"core-graphics 0.24.0",
|
"core-graphics",
|
||||||
"foreign-types 0.5.0",
|
"foreign-types 0.5.0",
|
||||||
"libc",
|
"libc",
|
||||||
"objc",
|
"objc",
|
||||||
@@ -1126,19 +1126,6 @@ dependencies = [
|
|||||||
"libc",
|
"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]]
|
[[package]]
|
||||||
name = "core-graphics-types"
|
name = "core-graphics-types"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -1561,7 +1548,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "67fd9ae1736d6ebb2e472740fbee86fb2178b8d56feb98a6751411d4c95b7e72"
|
checksum = "67fd9ae1736d6ebb2e472740fbee86fb2178b8d56feb98a6751411d4c95b7e72"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cocoa",
|
"cocoa",
|
||||||
"core-graphics 0.24.0",
|
"core-graphics",
|
||||||
"dunce",
|
"dunce",
|
||||||
"gdk",
|
"gdk",
|
||||||
"gdkx11",
|
"gdkx11",
|
||||||
@@ -1656,7 +1643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0cf6f550bbbdd5fe66f39d429cb2604bcdacbf00dca0f5bbe2e9306a0009b7c6"
|
checksum = "0cf6f550bbbdd5fe66f39d429cb2604bcdacbf00dca0f5bbe2e9306a0009b7c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core-foundation 0.10.1",
|
"core-foundation 0.10.1",
|
||||||
"core-graphics 0.24.0",
|
"core-graphics",
|
||||||
"foreign-types-shared 0.3.1",
|
"foreign-types-shared 0.3.1",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -4126,17 +4113,6 @@ dependencies = [
|
|||||||
"malloc_buf",
|
"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]]
|
[[package]]
|
||||||
name = "objc-sys"
|
name = "objc-sys"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@@ -4479,15 +4455,6 @@ dependencies = [
|
|||||||
"objc2-security",
|
"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]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.7"
|
version = "0.36.7"
|
||||||
@@ -4709,6 +4676,12 @@ version = "1.0.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pastey"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "path-clean"
|
name = "path-clean"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -6279,7 +6252,7 @@ checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"core-graphics 0.24.0",
|
"core-graphics",
|
||||||
"foreign-types 0.5.0",
|
"foreign-types 0.5.0",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
@@ -6527,7 +6500,7 @@ dependencies = [
|
|||||||
"bitflags 2.9.4",
|
"bitflags 2.9.4",
|
||||||
"block2 0.6.1",
|
"block2 0.6.1",
|
||||||
"core-foundation 0.10.1",
|
"core-foundation 0.10.1",
|
||||||
"core-graphics 0.24.0",
|
"core-graphics",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"dispatch",
|
"dispatch",
|
||||||
"dlopen2",
|
"dlopen2",
|
||||||
@@ -6727,17 +6700,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-nspanel"
|
name = "tauri-nspanel"
|
||||||
version = "2.0.1"
|
version = "2.1.0"
|
||||||
source = "git+https://github.com/ahkohd/tauri-nspanel?branch=v2#18ffb9a201fbf6fedfaa382fd4b92315ea30ab1a"
|
source = "git+https://github.com/ahkohd/tauri-nspanel?branch=v2.1#da9c9a8d4eb7f0524a2508988df1a7d9585b4904"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.4",
|
"objc2 0.6.2",
|
||||||
"block",
|
"objc2-app-kit 0.3.1",
|
||||||
"cocoa",
|
"objc2-foundation 0.3.1",
|
||||||
"core-foundation 0.10.1",
|
"pastey",
|
||||||
"core-graphics 0.25.0",
|
|
||||||
"objc",
|
|
||||||
"objc-foundation",
|
|
||||||
"objc_id",
|
|
||||||
"tauri",
|
"tauri",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ path-clean = "1.0.1"
|
|||||||
tempfile = "3.23.0"
|
tempfile = "3.23.0"
|
||||||
|
|
||||||
[target."cfg(target_os = \"macos\")".dependencies]
|
[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-app-kit = { version = "0.3.1", features = ["NSWindow"] }
|
||||||
objc2 = "0.6.2"
|
objc2 = "0.6.2"
|
||||||
objc2-core-foundation = {version = "0.3.1", features = ["CFString", "CFCGTypes", "CFArray"] }
|
objc2-core-foundation = {version = "0.3.1", features = ["CFString", "CFCGTypes", "CFArray"] }
|
||||||
|
|||||||
@@ -267,35 +267,57 @@ async fn show_coco(app_handle: AppHandle) {
|
|||||||
if let Some(window) = app_handle.get_webview_window(MAIN_WINDOW_LABEL) {
|
if let Some(window) = app_handle.get_webview_window(MAIN_WINDOW_LABEL) {
|
||||||
move_window_to_active_monitor(&window);
|
move_window_to_active_monitor(&window);
|
||||||
|
|
||||||
let _ = window.show();
|
cfg_if::cfg_if! {
|
||||||
let _ = window.unminimize();
|
if #[cfg(target_os = "macos")] {
|
||||||
|
use tauri_nspanel::ManagerExt;
|
||||||
|
|
||||||
// The Window Management (WM) extension (macOS-only) controls the
|
let app_handle_clone = app_handle.clone();
|
||||||
// frontmost window. Setting focus on macOS makes Coco the frontmost
|
|
||||||
// window, which means the WM extension would control Coco instead of other
|
app_handle.run_on_main_thread(move || {
|
||||||
// windows, which is not what we want.
|
let panel = app_handle_clone.get_webview_panel(MAIN_WINDOW_LABEL).unwrap();
|
||||||
//
|
|
||||||
// On Linux/Windows, however, setting focus is a necessity to ensure that
|
panel.show_and_make_key();
|
||||||
// users open Coco's window, then they can start typing, without needing
|
}).unwrap();
|
||||||
// to click on the window.
|
} else {
|
||||||
#[cfg(not(target_os = "macos"))]
|
let _ = window.show();
|
||||||
let _ = window.set_focus();
|
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", ());
|
let _ = app_handle.emit("show-coco", ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn hide_coco(app: AppHandle) {
|
async fn hide_coco(app_handle: AppHandle) {
|
||||||
if let Some(window) = app.get_webview_window(MAIN_WINDOW_LABEL) {
|
cfg_if::cfg_if! {
|
||||||
if let Err(err) = window.hide() {
|
if #[cfg(target_os = "macos")] {
|
||||||
log::error!("Failed to hide the window: {}", err);
|
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 {
|
} 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) {
|
fn move_window_to_active_monitor(window: &WebviewWindow) {
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
//! credits to: https://github.com/ayangweb/ayangweb-EcoPaste/blob/169323dbe6365ffe4abb64d867439ed2ea84c6d1/src-tauri/src/core/setup/mac.rs
|
//! 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 crate::common::MAIN_WINDOW_LABEL;
|
||||||
use objc2_app_kit::NSNonactivatingPanelMask;
|
use tauri::{AppHandle, Emitter, EventTarget, Manager, WebviewWindow};
|
||||||
use tauri::{AppHandle, Emitter, EventTarget, WebviewWindow};
|
use tauri_nspanel::{CollectionBehavior, PanelLevel, StyleMask, WebviewWindowExt, tauri_panel};
|
||||||
#[allow(deprecated)]
|
|
||||||
use tauri_nspanel::cocoa::appkit::NSWindowCollectionBehavior;
|
|
||||||
use tauri_nspanel::{WebviewWindowExt, panel_delegate};
|
|
||||||
|
|
||||||
const WINDOW_FOCUS_EVENT: &str = "tauri://focus";
|
const WINDOW_FOCUS_EVENT: &str = "tauri://focus";
|
||||||
const WINDOW_BLUR_EVENT: &str = "tauri://blur";
|
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(
|
pub fn platform(
|
||||||
_tauri_app_handle: &AppHandle,
|
_tauri_app_handle: &AppHandle,
|
||||||
@@ -26,70 +29,39 @@ pub fn platform(
|
|||||||
_check_window: WebviewWindow,
|
_check_window: WebviewWindow,
|
||||||
) {
|
) {
|
||||||
// Convert ns_window to ns_panel
|
// Convert ns_window to ns_panel
|
||||||
let panel = main_window.to_panel().unwrap();
|
let panel = main_window.to_panel::<NsPanel>().unwrap();
|
||||||
|
|
||||||
|
// set level
|
||||||
|
panel.set_level(PanelLevel::Utility.value());
|
||||||
|
|
||||||
// Do not steal focus from other windows
|
// Do not steal focus from other windows
|
||||||
//
|
panel.set_style_mask(StyleMask::empty().nonactivating_panel().into());
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Open the window in the active workspace and full screen
|
// Open the window in the active workspace and full screen
|
||||||
#[allow(deprecated)]
|
panel.set_collection_behavior(
|
||||||
panel.set_collection_behaviour(
|
CollectionBehavior::new()
|
||||||
NSWindowCollectionBehavior::NSWindowCollectionBehaviorMoveToActiveSpace
|
.stationary()
|
||||||
| NSWindowCollectionBehavior::NSWindowCollectionBehaviorStationary
|
.move_to_active_space()
|
||||||
| NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenAuxiliary,
|
.full_screen_auxiliary()
|
||||||
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Define the panel's delegate to listen to panel window events
|
let handler = NsPanelEventHandler::new();
|
||||||
#[allow(deprecated)]
|
|
||||||
let delegate = panel_delegate!(EcoPanelDelegate {
|
|
||||||
window_did_become_key,
|
|
||||||
window_did_resign_key,
|
|
||||||
window_did_resize,
|
|
||||||
window_did_move
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set event listeners for the delegate
|
let window = main_window.clone();
|
||||||
delegate.set_listener(Box::new(move |delegate_name: String| {
|
handler.window_did_become_key(move |_| {
|
||||||
let target = EventTarget::labeled(MAIN_WINDOW_LABEL);
|
let target = EventTarget::labeled(MAIN_WINDOW_LABEL);
|
||||||
|
|
||||||
let window_move_event = || {
|
let _ = window.emit_to(target, WINDOW_FOCUS_EVENT, true);
|
||||||
if let Ok(position) = main_window.outer_position() {
|
});
|
||||||
let _ = main_window.emit_to(target.clone(), WINDOW_MOVED_EVENT, position);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match delegate_name.as_str() {
|
let window = main_window.clone();
|
||||||
// Called when the window gets keyboard focus
|
handler.window_did_resign_key(move |_| {
|
||||||
"window_did_become_key" => {
|
let target = EventTarget::labeled(MAIN_WINDOW_LABEL);
|
||||||
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();
|
|
||||||
|
|
||||||
if let Ok(size) = main_window.inner_size() {
|
let _ = window.emit_to(target, WINDOW_BLUR_EVENT, true);
|
||||||
let _ = main_window.emit_to(target, WINDOW_RESIZED_EVENT, size);
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
// Called when the window position changes
|
|
||||||
"window_did_move" => window_move_event(),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Set the delegate object for the window to handle window events
|
// Set the delegate object for the window to handle window events
|
||||||
panel.set_delegate(delegate);
|
panel.set_event_handler(Some(handler.as_ref()));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user