diff --git a/crates/ui-egui/src/keys.rs b/crates/ui-egui/src/keys.rs index 431391b86..71542f8ab 100644 --- a/crates/ui-egui/src/keys.rs +++ b/crates/ui-egui/src/keys.rs @@ -465,7 +465,7 @@ pub fn canvas_events(app: &mut WordApp, ctx: &egui::Context) { } continue; } - if leave_full_screen(app, key, modifiers, true) { + if leave_full_screen(app, ctx, key, modifiers, true) { continue; } app.canvas.swallow_text = dispatch(app, key, modifiers) && modifiers.alt; @@ -479,12 +479,17 @@ pub fn canvas_events(app: &mut WordApp, ctx: &egui::Context) { /// took it: a pen, a text box, a header, a dialog, a menu or a popup, a drag on the page, and in /// the document (`canvas`) a selection, which Escape collapses first (`select.collapse`), as in a /// normal window. -fn leave_full_screen(app: &mut WordApp, key: Key, m: Modifiers, canvas: bool) -> bool { +fn leave_full_screen(app: &mut WordApp, ctx: &egui::Context, key: Key, m: Modifiers, canvas: bool) -> bool { let screen = crate::chrome::Screen::of(&app.session.view).exit_command(); let Some(id) = screen.or(app.session.view.full_screen.then_some("view.fullScreen")) else { return false }; if key != Key::Escape || !m.is_none() || !app.escape_free { return false; } + // Escape with a button held (dragging a task pane's title bar, say) cancels the drag only. + // egui has already dropped the drag itself, so the held button is what is left of it. + if ctx.input(|i| i.pointer.any_down()) { + return false; + } let c = &app.canvas; if c.obj_drag.is_some() || c.table_resize.is_some() || c.table_drag.is_some() || c.link_from.is_some() { return false; @@ -720,7 +725,7 @@ pub fn global_shortcuts(app: &mut WordApp, ctx: &egui::Context) { let events = ctx.input(|i| i.events.clone()); for e in events { if let egui::Event::Key { key, pressed: true, modifiers, .. } = e { - if leave_full_screen(app, key, modifiers, false) { + if leave_full_screen(app, ctx, key, modifiers, false) { continue; } if is_global_shortcut(key, modifiers) { diff --git a/crates/ui-egui/src/panes.rs b/crates/ui-egui/src/panes.rs index 12120265b..5274f083d 100644 --- a/crates/ui-egui/src/panes.rs +++ b/crates/ui-egui/src/panes.rs @@ -1305,6 +1305,34 @@ mod tests { assert_eq!(h.state().dialog.as_ref().map(|d| d.name()), Some("wordCount")); } + /// Escape during a pane's title-bar drag cancels the drag and leaves full screen on. + #[test] + #[cfg(not(target_arch = "wasm32"))] + fn escape_during_a_pane_drag_keeps_full_screen() { + use egui_kittest::kittest::Queryable; + + let mut app = crate::WordApp::new(wordcraft_engine::Session::new(wordcraft_doc::Document::from_text("")), crate::Services::default()); + app.run("view.navigationPane", serde_json::json!({"value": true})).unwrap(); + app.run("view.fullScreen", serde_json::json!({"value": true})).unwrap(); + let mut h = pane_harness(app); + let title = h.get_by_label_contains("Move Pane: ").rect().center(); + let button = + |pos, pressed| egui::Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers: egui::Modifiers::NONE }; + h.input_mut().events.extend([egui::Event::PointerMoved(title), button(title, true)]); + h.run_steps(2); + let away = title + egui::vec2(300.0, 200.0); + h.input_mut().events.push(egui::Event::PointerMoved(away)); + h.run_steps(2); + assert!(h.ctx.dragged_id().is_some(), "the title bar is dragged"); + press(&mut h, egui::Key::Escape, egui::Modifiers::NONE); + assert!(h.state().session.view.full_screen); + h.input_mut().events.push(button(away, false)); + h.run_steps(2); + assert!(h.state().session.view.full_screen); + let docked = craft_ui::panes::Place::Docked(craft_ui::panes::Edge::Start); + assert_eq!(super::pane_state(h.state(), Pane::Navigation).place, docked, "the drag was cancelled"); + } + /// An F6 the user bound runs its command; plain F6 still moves to the panes when only /// Shift+F6 is bound. #[test]