From 4a77f1b421c08e42984ab7683db0a0406ab4e72d Mon Sep 17 00:00:00 2001 From: AskaEth Date: Wed, 11 Nov 2026 22:41:40 +0800 Subject: [PATCH] =?UTF-8?q?fix(app-iced):=20=E9=80=80=E5=9C=BA=E7=AA=97?= =?UTF-8?q?=E5=8F=A3=E5=86=85=20Esc=20=E4=B9=9F=E8=A2=AB=E5=90=83=E6=8E=89?= =?UTF-8?q?=EF=BC=88=E8=A1=A5=E9=94=AE=E7=9B=98=E5=85=A5=E5=8F=A3=E7=9A=84?= =?UTF-8?q?=E9=97=B8=E9=97=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 模态退场期间逻辑状态已关(state.dialogs 空),但 Esc 走的是 Message::Agent(Key) 那条 独立路径,不经过 on_dialog 的 modal_closing 闸门 ⇒ 那 150ms 里 Esc 会落到 AI 栏去收起面板。 escape() 顶部加同一道闸门(返回 true = 被吃掉),并给退场用例补一条断言。 --- crates/app-iced/src/app/update/dialog.rs | 6 ++++++ crates/app-iced/src/app/update/motion_sync.rs | 12 +++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/crates/app-iced/src/app/update/dialog.rs b/crates/app-iced/src/app/update/dialog.rs index 341ec2e..8bb295b 100644 --- a/crates/app-iced/src/app/update/dialog.rs +++ b/crates/app-iced/src/app/update/dialog.rs @@ -145,6 +145,12 @@ pub(super) fn on_dialog(app: &mut App, message: DialogMessage) -> Task bool { + // M256:退场窗口里 Esc **也算被吃掉**。`on_dialog` 那道闸门管不到这里(Esc 走的是 + // `Message::Agent(Key)` 这条独立路径),而逻辑状态已经关掉 ⇒ 不加这一行,Esc 会落到 + // AI 栏去收起面板,用户看到的是「框还在缩、AI 栏先没了」。 + if motion::modal_closing() { + return true; + } let Some(current) = app.state.dialogs.current().map(|request| request.id) else { return false; }; diff --git a/crates/app-iced/src/app/update/motion_sync.rs b/crates/app-iced/src/app/update/motion_sync.rs index 63f5070..69b32e8 100644 --- a/crates/app-iced/src/app/update/motion_sync.rs +++ b/crates/app-iced/src/app/update/motion_sync.rs @@ -322,9 +322,8 @@ mod tests { /// **退场窗口内那个框已经关掉,且不可再交互**(tower 的硬要求)。 /// - /// 三件事分别断言:①逻辑状态当拍就空(`state.dialogs` 里没有它);②画面还在退场 - /// (`dialog_model` 仍 `Some` ⇒ 不是硬跳);③这 150ms 内再点一次**什么都不发生** - /// (`on_dialog` 的 `modal_closing` 闸门 → 连一次重画都不该有)。 + /// 四件事分别断言:①逻辑状态当拍就空;②画面还在退场(`dialog_model` 仍 `Some` ⇒ 不是硬跳); + /// ③这 150ms 内再点一次**什么都不发生**(`on_dialog` 的闸门);④**Esc 那条独立路径**也挡住。 /// /// **抓得住**:把 `on_dialog` 顶部的 `if motion::modal_closing()` 删掉(第二次点击会走进 /// `close(app)`,`refreshes` 涨一次 ⇒ 红);或让逻辑关闭延后(①红);或让退场直接清模型(②红)。 @@ -380,6 +379,13 @@ mod tests { "幽灵点击不许复活那个框" ); assert!(app.dialog_model.is_some(), "也不许把它重新打开"); + // **键盘入口单列一条**:`Esc` 不走 `on_dialog`(它是 `Message::Agent(Key)` 那条路), + // 必须在 `escape` 里单独挡住,否则退场那 150ms 里 Esc 会落到 AI 栏去收起面板。 + assert!( + crate::app::update::dialog::escape(&mut app), + "退场窗口内的 Esc 也必须被吃掉(不许落到 AI 栏)" + ); + assert!(app.state.dialogs.current().is_none()); // 退场走完:下一拍的 `sync_motion` 把它丢掉,帧泵停 run_until_idle();