From 3685bae8987d5fc05fa6d2c9681efbae7a68af18 Mon Sep 17 00:00:00 2001 From: KotovGGG <162052215+KotovGGG@users.noreply.github.com> Date: Sun, 1 Nov 2026 02:09:38 +0300 Subject: [PATCH] fix(ui): suppress trailing drops for host-owned groups --- crates/ui/src/docking/host_hooks_tests.rs | 46 +++++++++++++++++++++++ crates/ui/src/docking/render/dock.rs | 5 ++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/crates/ui/src/docking/host_hooks_tests.rs b/crates/ui/src/docking/host_hooks_tests.rs index d3585733..ba6cb41f 100644 --- a/crates/ui/src/docking/host_hooks_tests.rs +++ b/crates/ui/src/docking/host_hooks_tests.rs @@ -317,3 +317,49 @@ fn exclusive_docked_groups_skip_stock_body_bands_but_keep_column_edges() { } } } + +#[test] +fn owned_minimized_groups_skip_the_trailing_stock_target() { + for owned in [false, true] { + let ctx = Context::default(); + let area = Id::unique("owned-dock-targets"); + let mut layout = Layout { + root: Some(Node::Dock { side: Side::Start, columns: vec![Column::new(vec![Group::new(vec![1, 2], 0)], 240.0)] }), + floating: vec![Floating::single(vec![99], 0, [500.0, 100.0, 200.0, 150.0])], + }; + if let Some(Node::Dock { columns, .. }) = &mut layout.root { + columns[0].groups[0].minimized = true; + } + let mut host = Host { exclusive: owned, stock_group: Some(99), ..Default::default() }; + let mut step = |events, start: bool| { + let mut out = DockOutput::default(); + ctx.run_ui(input(events), |ui| { + let mut style = DockStyle::from_ui(ui); + super::floating_tests::frame_style(&mut style); + out = DockArea::new(area).show_content(ui, &layout, &style, &mut host); + if start { + assert!(begin_drag(ui.ctx(), area, 99, Rect::from_min_size(pos2(500.0, 100.0), vec2(200.0, 150.0)))); + } + }) + .drop_without_applying_deltas(); + (out, host.groups.get(&1).copied()) + }; + step(vec![], false); + step(vec![], false); + let (_, target) = step(vec![], false); + let target = target.expect("the docked group is drawn"); + let from = pos2(550.0, 140.0); + step(vec![Event::PointerMoved(from), button(from, true)], true); + let to = pos2(target.center().x, target.bottom() + 60.0); + step(vec![Event::PointerMoved(to)], false); + let (out, _) = step(vec![Event::PointerMoved(to), button(to, false)], false); + let group_drop = out.actions.iter().any(|a| { + matches!( + a, + Action::MovePanel { to: Target::At { placement: Placement::Group { .. }, .. }, .. } + | Action::MoveGroup { to: Target::At { placement: Placement::Group { .. }, .. }, .. } + ) + }); + assert_eq!(group_drop, !owned, "owned={owned}: {:?}", out.actions); + } +} diff --git a/crates/ui/src/docking/render/dock.rs b/crates/ui/src/docking/render/dock.rs index adbfe5bd..7805ff0d 100644 --- a/crates/ui/src/docking/render/dock.rs +++ b/crates/ui/src/docking/render/dock.rs @@ -528,7 +528,7 @@ impl Renderer<'_, P> { } }; let mut previous: Option<&P> = None; - for ((group, rect), owned) in column.groups.iter().zip(&rects).zip(owned) { + for ((group, rect), owned) in column.groups.iter().zip(&rects).zip(&owned) { let Some(anchor) = group.panels.first() else { continue }; if let Some(above) = previous { let band = Rect::from_x_y_ranges(xs, (rect.top() - half)..=(rect.top() + half)); @@ -544,13 +544,14 @@ impl Renderer<'_, P> { self.dock_groups.push(DockGroup { body, center, - sides: [None, None, (!owned).then(|| (above, line(rect.top()))), (!owned).then(|| (below, line(rect.bottom())))], + sides: [None, None, (!*owned).then(|| (above, line(rect.top()))), (!*owned).then(|| (below, line(rect.bottom())))], }); } previous = Some(anchor); } // The room under the last group, when every group is minimized, stacks below it. if let Some(last) = previous + && !owned.last().copied().unwrap_or(false) && top < view.bottom() { let band = Rect::from_x_y_ranges(xs, top..=view.bottom());