mirror of
https://github.com/infinilabs/coco-app.git
synced 2025-12-16 03:27:43 +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(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)
|
||||
|
||||
|
||||
65
src-tauri/Cargo.lock
generated
65
src-tauri/Cargo.lock
generated
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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::<NsPanel>().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()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user