diff --git a/crates/ui/src/docking/surface.rs b/crates/ui/src/docking/surface.rs index 2f827b75..cecdaa85 100644 --- a/crates/ui/src/docking/surface.rs +++ b/crates/ui/src/docking/surface.rs @@ -133,8 +133,13 @@ pub fn begin_drag(ctx: &Context, area_id: Id, true } +/// Discard all pending gestures before replacing a workspace or its layout. The host owns +/// the replacement snapshot, so this does not restore a group's previous rectangle. pub fn cancel_drag(ctx: &Context, area_id: Id) { - ctx.data_mut(|d| d.remove::>(area_id.with("drag"))); + ctx.data_mut(|d| { + d.remove::>(area_id.with("drag")); + d.remove::>(area_id.with("group-drag")); + }); } /// One mutable application adapter owns bodies, menus and shaped text, avoiding competing diff --git a/crates/ui/src/docking/tests.rs b/crates/ui/src/docking/tests.rs index 49f89792..57625d8e 100644 --- a/crates/ui/src/docking/tests.rs +++ b/crates/ui/src/docking/tests.rs @@ -649,6 +649,31 @@ struct CustomContent { menus: usize, bodies: usize, } + +#[test] +fn host_cancel_discards_group_drag_before_workspace_replacement() { + let ctx = Context::default(); + let mut layout = sample(); + for _ in 0..2 { + step(&ctx, &mut layout, vec![]); + } + let start = pos2(86.0, 82.0); + step(&ctx, &mut layout, vec![Event::PointerMoved(start), pointer_button(start, true)]); + let moved = start + vec2(50.0, 40.0); + let actions = step(&ctx, &mut layout, vec![Event::PointerMoved(moved)]); + assert!(actions.iter().any(|action| matches!(action, Action::MoveFloating { .. }))); + + cancel_drag::(&ctx, Id::new("test-area")); + layout = sample(); + let replacement = layout.clone(); + let actions = step(&ctx, &mut layout, vec![Event::PointerMoved(moved + vec2(20.0, 10.0))]); + assert!(actions.is_empty()); + assert_eq!(layout, replacement); + let mut content = CustomContent { labels: vec![], headers: 0, menus: 0, bodies: 0 }; + let output = custom_frame(&ctx, &layout, &mut content, vec![pointer_button(moved + vec2(20.0, 10.0), false)], &[], false); + assert!(output.group_drag.is_none()); + assert!(output.group_drop.is_none()); +} impl DockContent for CustomContent { fn body(&mut self, ui: &mut egui::Ui, _: &u32) { self.bodies += 1;