fix(desktop): 识别浮层浮在其他 App 全屏之上——改 Accessory 激活策略
标准 NSWindow 靠 collectionBehavior 无法叠加到别的 App 的全屏 Space(macOS 设计限制,Apple 论坛 26677 / tauri#11488;FullScreenAuxiliary 实为反效果)。确认可用解法是把 app 激活策略设为 Accessory(菜单栏辅助 App)——dudu 本就是托盘应用无主窗口,不占 Dock 正是其正确形态。配合 CanJoinAllSpaces,浮层即可显示在全屏 App 上。configure_overlay_spaces 改为每次 show 时在主线程重设 + 文件诊断(确认 behavior 0x111/level 已设上)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -331,32 +331,61 @@ fn set_tray_tooltip(app: &AppHandle, text: &str) {
|
||||
}
|
||||
|
||||
/// 让识别浮层能浮在全屏 App 之上(macOS):给 NSWindow 追加
|
||||
/// CanJoinAllSpaces | Stationary | FullScreenAuxiliary 集合行为。
|
||||
/// CanJoinAllSpaces | Stationary | FullScreenAuxiliary 集合行为,并抬高窗口层级。
|
||||
/// 仅 alwaysOnTop 不够——全屏 App 独占 Space,普通置顶窗口不会出现在其中,
|
||||
/// FullScreenAuxiliary 才让浮层作为辅助窗叠加到全屏 Space 上。
|
||||
/// 必须在主线程调用(Tauri setup 闭包即主线程),窗口创建后调用一次即可。
|
||||
///
|
||||
/// 强制派发到主线程执行(设 NSWindow 属性必须主线程);每次显示浮层都重设一遍,
|
||||
/// 规避「setup 时隐藏窗口的原生窗口尚未就绪 / 属性被 show 重置」。
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn configure_overlay_spaces(app: &AppHandle) {
|
||||
use objc2::{msg_send, runtime::AnyObject};
|
||||
// NSWindowCollectionBehavior 位标志
|
||||
const CAN_JOIN_ALL_SPACES: usize = 1 << 0;
|
||||
const STATIONARY: usize = 1 << 4;
|
||||
const FULL_SCREEN_AUXILIARY: usize = 1 << 8;
|
||||
if let Some(w) = app.get_webview_window(OVERLAY) {
|
||||
if let Ok(ptr) = w.ns_window() {
|
||||
let ns = ptr as *mut AnyObject;
|
||||
unsafe {
|
||||
let cur: usize = msg_send![ns, collectionBehavior];
|
||||
let next = cur | CAN_JOIN_ALL_SPACES | STATIONARY | FULL_SCREEN_AUXILIARY;
|
||||
let _: () = msg_send![ns, setCollectionBehavior: next];
|
||||
let app2 = app.clone();
|
||||
let _ = app.run_on_main_thread(move || {
|
||||
use objc2::{msg_send, runtime::AnyObject};
|
||||
const CAN_JOIN_ALL_SPACES: usize = 1 << 0;
|
||||
const STATIONARY: usize = 1 << 4;
|
||||
const FULL_SCREEN_AUXILIARY: usize = 1 << 8;
|
||||
// NSStatusWindowLevel(25):浮在普通/全屏窗口之上、菜单栏之下。
|
||||
const STATUS_LEVEL: isize = 25;
|
||||
let Some(w) = app2.get_webview_window(OVERLAY) else {
|
||||
overlay_diag("configure: overlay window 不存在");
|
||||
return;
|
||||
};
|
||||
match w.ns_window() {
|
||||
Ok(ptr) => {
|
||||
let ns = ptr as *mut AnyObject;
|
||||
unsafe {
|
||||
let before: usize = msg_send![ns, collectionBehavior];
|
||||
let next = before | CAN_JOIN_ALL_SPACES | STATIONARY | FULL_SCREEN_AUXILIARY;
|
||||
let _: () = msg_send![ns, setCollectionBehavior: next];
|
||||
let _: () = msg_send![ns, setLevel: STATUS_LEVEL];
|
||||
let after: usize = msg_send![ns, collectionBehavior];
|
||||
overlay_diag(&format!(
|
||||
"configure: ns_window OK behavior {before:#x} → {after:#x} level={STATUS_LEVEL}"
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(e) => overlay_diag(&format!("configure: ns_window 失败: {e}")),
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub fn configure_overlay_spaces(_app: &AppHandle) {}
|
||||
|
||||
// TODO(诊断): 全屏浮层排查——写文件便于 open 启动时读取,定位后删除。
|
||||
#[cfg(target_os = "macos")]
|
||||
fn overlay_diag(msg: &str) {
|
||||
use std::io::Write;
|
||||
if let Ok(mut f) = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open("/tmp/dudu-overlay-diag.log")
|
||||
{
|
||||
let _ = writeln!(f, "{msg}");
|
||||
}
|
||||
}
|
||||
|
||||
fn show_overlay(app: &AppHandle) {
|
||||
if let Some(w) = app.get_webview_window(OVERLAY) {
|
||||
// 跟随光标:浮层出现在指针下方 24px,水平居中
|
||||
@@ -365,6 +394,8 @@ fn show_overlay(app: &AppHandle) {
|
||||
}
|
||||
let _ = w.show();
|
||||
}
|
||||
// 每次显示都重设全屏叠加行为(主线程派发;见 configure_overlay_spaces)。
|
||||
configure_overlay_spaces(app);
|
||||
}
|
||||
|
||||
fn hide_overlay(app: &AppHandle) {
|
||||
|
||||
@@ -98,6 +98,12 @@ pub fn run() {
|
||||
})
|
||||
.setup(|app| {
|
||||
let handle = app.handle().clone();
|
||||
// dudu 是托盘应用(无主窗口):设为 Accessory 激活策略——菜单栏辅助 App,
|
||||
// 不占 Dock。这也是识别浮层能浮在其他 App 全屏之上的前提:标准 NSWindow
|
||||
// 靠 collectionBehavior 无法叠加到别的 App 全屏 Space(macOS 设计限制,
|
||||
// 见 Apple 论坛 26677 / tauri#11488),Accessory 策略 + CanJoinAllSpaces 才生效。
|
||||
#[cfg(target_os = "macos")]
|
||||
let _ = app.set_activation_policy(tauri::ActivationPolicy::Accessory);
|
||||
// 设置 → 管理状态
|
||||
app.manage(settings::SettingsStore::load(&handle));
|
||||
app.manage(dictation::DictationState::default());
|
||||
|
||||
Reference in New Issue
Block a user