fix(tray): 二次启动时叫回被托盘隐藏的主窗口 - #242
Merged
feigeCode merged 2 commits intoSep 20, 2026
Merged
Conversation
现象:主窗口经「关闭按钮 → 最小化到托盘」隐藏之后,再次启动 navop(双击图标, 或通过关联文件打开)都不会再把窗口显示出来;但如果先用系统最小化、再启动,则 一切正常。 根因有两层: 1. 转发的启动请求只调了 `window.activate_window()`,从没走过托盘自己那套可见性 适配器。gpui_windows 的 `activate()` 只在 `IsIconic`(系统最小化)时才补一次 `ShowWindowAsync(SW_RESTORE)`;而托盘隐藏走的是 `ShowWindow(SW_HIDE)`,隐藏 窗口不是 iconic,于是一次 ShowWindow 都不会发出,紧随其后的 `SetActiveWindow`/`SetFocus` 对不可见窗口也毫无作用。系统最小化恰好命中 `IsIconic` 分支,所以那条路径表现正常——这正是「最小化之后再操作可以显示」的 由来。`file_open` 内部同样只有 `activate_window()`,关联文件打开一并受影响。 2. `window_visibility` 的 Windows 恢复写死了 `SW_RESTORE`。Win32 文档写明它对 最大化窗口会「还原到原始尺寸与位置」,也就是取消最大化;而二次启动时窗口往往 正处在最大化状态。若把这条路径直接接到转发上,会把用户的最大化窗口缩回去, 等于用一个更显眼的问题换掉原问题。 修复:转发循环复用 `window_visibility::main_window_target` + `show_main_window`,并把句柄获取放在窗口借用内、原生调用放在借用之外——原生激活 回调会同步重入 GPUI 抢同一个 App 借用,写在 `cx.update_window(...)` 闭包内只会 得到 `RefCell already borrowed`。同时让 `platform::show` 按 `IsIconic` 分派: 只有被最小化时才用 `SW_RESTORE`,否则用 `SW_SHOW`(按当前尺寸与位置显示,保住 最大化,对已可见窗口是无操作)。补一条源文本契约测试,同时钉住「借用外恢复」与 「恢复先于打开文件」两个形态约束。 验证: - 真机复现(安装版 v0.18.4,临时 --data-dir,未触碰真实配置):二次启动确实走了 转发(转发方退出码 0、场上窗口数仍为 1),但主窗口 `IsWindowVisible=false`; 此后外部直接 `ShowWindow(SW_SHOW)` + `SetForegroundWindow` 立即恢复可见。带 文件路径启动同样转发、同样恢复不了(修正「关联文件打开可以」的观察:那应发生 在系统最小化态,而非托盘隐藏态)。 - 真机实测 `SW_RESTORE` 与 `SW_SHOW` 对「隐藏且最大化」窗口的差异:`SW_RESTORE` 后可见但 `IsZoomed` true→false;`SW_SHOW` 后可见且 `IsZoomed` 保持 true;对已 可见窗口 `SW_SHOW` 完全无操作。据此确定分派策略。 - 离线契约校验(`.workbuddy/tmp/tray-restore-contract/`,用 `#[path]` 直接引用仓库 真实源文件、`--offline` 编译):3/3 通过,覆盖本次新增的两条 main.rs 断言与 window_visibility.rs 的分派形态。 - 测试目标:`CARGO_INCREMENTAL=0 cargo-msvc.py test -p main --bin navop --config profile.dev.package.main.debug=0` → **634 passed / 0 failed / 0 ignored**, 测试本体 4.55s,含本次新增的 `embedded_cli_removal_tests::forwarded_startup_request_restores_a_tray_hidden_window`。 本轮编译耗时 1h3m:上游把 gpui-pre 升到 fork-0.3.109 且本地无该 rev 缓存, 需联网重建整条依赖链。 - 另:`rustfmt` 已对改动文件做过格式化。 未验证项:真机完整「隐藏 → 更新 → 自动拉起新版本」链路;macOS/Linux 的 `platform::show` 未被本次改动触及(改动全部在 `target_os = "windows"` 分支内)。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
现象:主窗口经「关闭按钮 → 最小化到托盘」隐藏之后,再次启动 navop(双击图标,
或通过关联文件打开)都不会再把窗口显示出来;但如果先用系统最小化、再启动,则
一切正常。
根因有两层:
转发的启动请求只调了
window.activate_window(),从没走过托盘自己那套可见性 适配器。gpui_windows 的activate()只在IsIconic(系统最小化)时才补一次ShowWindowAsync(SW_RESTORE);而托盘隐藏走的是ShowWindow(SW_HIDE),隐藏 窗口不是 iconic,于是一次 ShowWindow 都不会发出,紧随其后的SetActiveWindow/SetFocus对不可见窗口也毫无作用。系统最小化恰好命中IsIconic分支,所以那条路径表现正常——这正是「最小化之后再操作可以显示」的 由来。file_open内部同样只有activate_window(),关联文件打开一并受影响。window_visibility的 Windows 恢复写死了SW_RESTORE。Win32 文档写明它对 最大化窗口会「还原到原始尺寸与位置」,也就是取消最大化;而二次启动时窗口往往 正处在最大化状态。若把这条路径直接接到转发上,会把用户的最大化窗口缩回去, 等于用一个更显眼的问题换掉原问题。修复:转发循环复用
window_visibility::main_window_target+show_main_window,并把句柄获取放在窗口借用内、原生调用放在借用之外——原生激活回调会同步重入 GPUI 抢同一个 App 借用,写在
cx.update_window(...)闭包内只会 得到RefCell already borrowed。同时让platform::show按IsIconic分派: 只有被最小化时才用SW_RESTORE,否则用SW_SHOW(按当前尺寸与位置显示,保住 最大化,对已可见窗口是无操作)。补一条源文本契约测试,同时钉住「借用外恢复」与「恢复先于打开文件」两个形态约束。
验证:
IsWindowVisible=false; 此后外部直接ShowWindow(SW_SHOW)+SetForegroundWindow立即恢复可见。带 文件路径启动同样转发、同样恢复不了(修正「关联文件打开可以」的观察:那应发生 在系统最小化态,而非托盘隐藏态)。SW_RESTORE与SW_SHOW对「隐藏且最大化」窗口的差异:SW_RESTORE后可见但IsZoomedtrue→false;SW_SHOW后可见且IsZoomed保持 true;对已 可见窗口SW_SHOW完全无操作。据此确定分派策略。.workbuddy/tmp/tray-restore-contract/,用#[path]直接引用仓库 真实源文件、--offline编译):3/3 通过,覆盖本次新增的两条 main.rs 断言与 window_visibility.rs 的分派形态。CARGO_INCREMENTAL=0 cargo-msvc.py test -p main --bin navop --config profile.dev.package.main.debug=0→ 634 passed / 0 failed / 0 ignored, 测试本体 4.55s,含本次新增的embedded_cli_removal_tests::forwarded_startup_request_restores_a_tray_hidden_window。 本轮编译耗时 1h3m:上游把 gpui-pre 升到 fork-0.3.109 且本地无该 rev 缓存, 需联网重建整条依赖链。rustfmt已对改动文件做过格式化。未验证项:真机完整「隐藏 → 更新 → 自动拉起新版本」链路;macOS/Linux 的
platform::show未被本次改动触及(改动全部在target_os = "windows"分支内)。Closes #[issue number]
Description
Describe in English for the changes made in this pull request and the problem it solves.
Please keep 1 PR to solve 1 problem, and keep Small improvements should be small modifications to make PR easier to review and to merge.
Screenshot
Break Changes
Describe any breaking changes introduced by this pull request. If none, remove this section.
How to Test
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.
Checklist
cargo runfor story tests related to the changes.