Skip to content

feat(macOS): 支持全屏应用覆盖显示、跨显示器拖拽及隐藏 Dock 图标#12

Open
letteryzzm wants to merge 5 commits into
QingJ01:mainfrom
letteryzzm:feat/fullscreen-overlay
Open

feat(macOS): 支持全屏应用覆盖显示、跨显示器拖拽及隐藏 Dock 图标#12
letteryzzm wants to merge 5 commits into
QingJ01:mainfrom
letteryzzm:feat/fullscreen-overlay

Conversation

@letteryzzm
Copy link
Copy Markdown

概述

  • 运行时通过 object_setClass 将 pet/hit 窗口提升为 NSPanel 子类,启用 NSWindowStyleMaskNonactivatingPanel — 这是
    macOS 10.14 以来能让窗口浮在全屏应用之上的方式
  • 设置 CanJoinAllSpaces | FullScreenAuxiliary | Stationary collectionBehavior,使宠物出现在所有 Space(包括全屏
    Space 和外接显示器)
  • 窗口层级提升至 NSScreenSaverWindowLevel(1000),确保在全屏应用内容之上
  • 拖拽范围从单个显示器改为所有显示器的联合包围盒,支持跨屏拖拽
  • 设置 ActivationPolicy::Accessory 隐藏 Dock 图标,通过托盘图标管理应用

测试计划

  • macOS:宠物在全屏应用上方可见(Ctrl+Cmd+F)
  • macOS:宠物在所有虚拟桌面可见(三指滑动切换)
  • macOS:宠物可从主屏拖到外接显示器
  • macOS:宠物交互正常(点击、拖拽、右键菜单)
  • Windows/Linux:无回归(macOS 代码有 #[cfg] 保护)

letteryzzm and others added 5 commits April 5, 2026 16:54
通过运行时 object_setClass 将窗口提升为 NSPanel 子类,启用
NonactivatingPanel style mask,这是 macOS 10.14 以来唯一能让
窗口浮在全屏应用之上的方式。同时设置 CanJoinAllSpaces +
FullScreenAuxiliary + Stationary collectionBehavior 和
NSScreenSaverWindowLevel 窗口层级。拖拽范围改为所有显示器的
联合包围盒以支持跨屏拖拽。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
设置 ActivationPolicy::Accessory 隐藏 Dock 上的应用图标,
桌面宠物通过系统托盘图标进行交互和管理。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
使用 CoreGraphics CGEvent 获取全局鼠标坐标,替代前端
screenX * devicePixelRatio 的方式,避免跨不同 DPI 显示器
拖拽时坐标缩放不一致的问题。同时在 hit 窗口添加
setPointerCapture,防止快速拖拽时 pointer 事件丢失。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
在宠物窗口左上角始终显示最多3条任务,右键菜单「编辑任务」
打开编辑面板,支持添加、编辑、删除任务。任务存储在
~/.clyde/tasks.json,通过 Tauri events 实时同步到宠物窗口。
同时添加二次开发交接文档 INTEGRATION.md。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
删除无法接收事件的 tasks 独立窗口方案,改用右键菜单子菜单 +
osascript 原生对话框编辑任务。弹框前临时切换 ActivationPolicy
到 Regular 使输入法正常工作,关闭后切回 Accessory。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@QingJ01
Copy link
Copy Markdown
Owner

QingJ01 commented Apr 17, 2026

感谢你的贡献!macOS 全屏覆盖和跨显示器拖拽是很有价值的功能。以下是详细的 review 意见:


🔴 需要修复(阻塞合并)

1. Windows/Linux 置顶回归

tauri.conf.jsonalwaysOnToptrue 改为 false,这是全局配置。macOS 端由 NSPanel 接管置顶没问题,但 Windows 和 Linux 上 pet 窗口将不再置顶,这是一个功能回归。

建议:保持 alwaysOnTop: true,在 macOS setup 阶段再由 Rust 代码覆盖窗口层级。或者在 setup_pet_window / setup_hit_window 中对非 macOS 平台显式调用 set_always_on_top(true)

2. PR 范围过大 — 建议拆分

这个 PR 混合了三组独立功能:

  • 全屏覆盖 + 跨屏拖拽 + 隐藏 Dock(核心功能)
  • 任务系统(tasks.rs + tasks 窗口 + 右键菜单 + pet 面板显示)
  • INTEGRATION.md(交接文档)

建议拆为至少两个 PR:

  1. macOS 全屏覆盖 + 跨屏拖拽(本 PR 的核心目标)
  2. 任务系统(独立功能,可单独讨论和迭代)

INTEGRATION.md 看起来是你自己项目的二次开发交接文档,内容和本项目的定位不太一致(比如"语音聊天接入"等场景),建议不要包含在 PR 中。

3. prompt_text_input 仅 macOS 可用

任务的添加/编辑通过 osascript 实现,非 macOS 平台直接返回 None,意味着 Windows/Linux 用户无法通过右键菜单添加或编辑任务。如果保留任务功能,需要一个跨平台的输入方案(比如用 Tauri 的 webview 窗口做输入框,项目中的 bubble 窗口就是这个模式)。

4. save_tasks 非原子写入

let _ = std::fs::write(path, json);

项目中其他持久化(prefs、runtime.json 等)都用 tmp + rename 的原子写入模式,防止写入中断导致数据损坏。这里应该保持一致:

let tmp = path.with_extension("json.tmp");
std::fs::write(&tmp, &json)?;
std::fs::rename(&tmp, &path)?;

🟡 建议改进

5. unsafe 代码审查 — promote_to_panel

static mut PANEL_CLASS: *const AnyClass = std::ptr::null();

static mut 在 Rust 2024 edition 中已被标记为不推荐。虽然有 Once 保护写入,但读取仍然是 unsafe 的。建议改用 std::sync::OnceLock<&'static AnyClass>

static PANEL_CLASS: OnceLock<&'static AnyClass> = OnceLock::new();
let cls = PANEL_CLASS.get_or_init(|| {
    // ClassBuilder::new + register
});

6. object_setClass 的安全性说明

运行时替换 isa 指针是一个大胆的操作。建议在注释中说明:

  • 为什么不能在窗口创建时直接用 NSPanel(Tauri 内部创建 NSWindow,无法干预)
  • 为什么 NSPanel 子类是必须的(NSWindow 不接受 NonactivatingPanel style mask bit)
  • 这个操作在哪些 macOS 版本上验证过

7. native_cursor_position 的开销

每次 drag_move 都创建一个 CGEventSource + CGEvent,在高频拖拽时(~60fps)可能有不必要的开销。考虑缓存 CGEventSource 或使用 CGEvent::mouseLocation() 类方法(如果可用)。

8. 硬编码的中文字符串

任务相关的 UI 字符串("添加任务"、"删除"、"编辑任务"、"输入任务内容"等)都是硬编码中文。项目使用 i18n::t(key, lang) 做多语言,新增的字符串应该走同样的 i18n 路径,支持 enzh

9. 任务面板遮挡 SVG

pet 窗口左上角的 .task-panel 覆盖在 SVG 动画上方,在 S 尺寸(200×200)下可能会遮挡宠物主体。建议考虑是否需要根据尺寸调整显示策略。


🟢 看起来不错的部分

  • NSPanel + object_setClass 的技术方案:这确实是 macOS 10.14+ 唯一能在全屏应用上方浮动的方式,方向正确
  • CoreGraphics 获取全局鼠标坐标:绕过 Tauri 的 DPI 问题是合理的
  • union_of_all_monitors:简洁清晰
  • Pointer Capture:解决快速拖拽丢失事件的问题,好的改进
  • apply_space_followshow() 后重新调用:处理了 Tauri 在 show 时可能重置窗口属性的问题

总结

核心功能(全屏覆盖 + 跨屏拖拽)方向正确,技术方案合理。主要问题是:

  1. 必须修复 Windows/Linux 置顶回归
  2. 强烈建议 拆分 PR(至少把任务系统分出去)
  3. 建议 移除 INTEGRATION.md

期待你的更新!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants