From e3b9e239c52c14677be462e94d8920ea243de4f3 Mon Sep 17 00:00:00 2001 From: aiongg <67543387+aiongg@users.noreply.github.com> Date: Sun, 25 Oct 2026 15:20:32 -0400 Subject: [PATCH] docking: a window-edge drop beside an iconic column highlights only the edge (#336) A group dragged to the window edge where an iconic column sits showed the edge line and an outline around the column. The edge line is drawn just inside the window, so it lay in the column's body, and paint_drop_line outlines the iconic column a line lies in. That outline is for drops into the column; a window-edge drop docks beside it, so it now shows the line alone. --- crates/ui/src/docking/iconic_tests.rs | 21 +++++++++++++++++++++ crates/ui/src/docking/render/dock.rs | 10 +++++++--- crates/ui/src/docking/render/preview.rs | 6 +++--- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/crates/ui/src/docking/iconic_tests.rs b/crates/ui/src/docking/iconic_tests.rs index 58a85fa5..0a353f2c 100644 --- a/crates/ui/src/docking/iconic_tests.rs +++ b/crates/ui/src/docking/iconic_tests.rs @@ -273,6 +273,27 @@ fn group_dropped_on_an_iconic_column_joins_as_an_icon_group() { assert!(first_column(&d.layout).iconic); } +/// A group dropped on the window edge beside an iconic column at that edge docks in a new column +/// there: the preview is the edge line alone, without the column's outline. +#[test] +fn an_edge_drop_next_to_an_iconic_column_highlights_only_the_edge() { + let mut layout = docked(&[&[1], &[2]], true); + layout.floating.push(Floating::single(vec![5, 6], 0, [300.0, 200.0, 240.0, 200.0])); + let mut d = DockDriver::new(layout, Content); + let bar = hit(&d, Hit::DragBar(5)).center(); + let target = pos2(2.0, 300.0); + d.hold(bar, target); + let column = Rect::from_min_max(pos2(0.0, 14.0), pos2(36.0, 600.0)); + assert!(d.filled(Rect::from_x_y_ranges(0.0..=4.0, 0.0..=600.0)), "the edge line"); + assert!(!d.outlined(column.shrink(1.0)), "the iconic column is not outlined"); + d.release(target); + let Some(Node::Split { first, .. }) = &d.layout.root else { panic!("{:?}", d.layout.root) }; + let Node::Dock { columns, .. } = first.as_ref() else { panic!("{first:?}") }; + let panels: Vec> = columns.iter().map(|c| c.groups.iter().flat_map(|g| g.panels.clone()).collect()).collect(); + assert_eq!(panels, vec![vec![5, 6], vec![1, 2]]); + assert!(!columns[0].iconic && columns[1].iconic); +} + /// Every kind of gesture source has a rect, and dragging from it does something. #[test] fn hit_rects_cover_every_gesture_source() { diff --git a/crates/ui/src/docking/render/dock.rs b/crates/ui/src/docking/render/dock.rs index 4eaab778..878d909e 100644 --- a/crates/ui/src/docking/render/dock.rs +++ b/crates/ui/src/docking/render/dock.rs @@ -789,11 +789,15 @@ impl Renderer<'_, P> { } /// The line of a drop that shows no result rect: on the window edge, into an iconic column, - /// or any dock drop with [`DockStyle::side_edge_zones`]. - pub(super) fn paint_drop_line(&self, ctx: &Context, line: Rect) { + /// or any dock drop with [`DockStyle::side_edge_zones`]. A line inside an iconic column also + /// outlines the column, unless it is on the window edge (`outer`): a column at that edge holds + /// the line, but the drop docks beside the column, not into it. + pub(super) fn paint_drop_line(&self, ctx: &Context, line: Rect, outer: bool) { if usable(line) { let painter = self.preview_painter(ctx); - self.iconic.outline(&painter, line, self.style); + if !outer { + self.iconic.outline(&painter, line, self.style); + } painter.rect_filled(line, 1.0, self.style.accent); } } diff --git a/crates/ui/src/docking/render/preview.rs b/crates/ui/src/docking/render/preview.rs index 65c15fd3..5404984e 100644 --- a/crates/ui/src/docking/render/preview.rs +++ b/crates/ui/src/docking/render/preview.rs @@ -599,12 +599,12 @@ impl Renderer<'_, P> { if self.style.side_edge_zones { drop_preview(painter, proposal.preview.shrink(1.0), self.style.accent, filled); } else if matches!(proposal.placement, Placement::Edge(_)) { - self.paint_drop_line(ctx, proposal.preview); + self.paint_drop_line(ctx, proposal.preview, true); } else if beside { let to = Target::At { anchor: proposal.anchor.clone(), placement: proposal.placement.clone() }; match self.result_rect(action(to), members) { Some(rect) => tab_outline(painter, rect.shrink(1.0), self.style.accent), - None => self.paint_drop_line(ctx, proposal.preview), + None => self.paint_drop_line(ctx, proposal.preview, false), } } else { tab_outline(painter, proposal.preview.shrink(1.0), self.style.accent); @@ -621,7 +621,7 @@ impl Renderer<'_, P> { let rect = (!self.style.side_edge_zones && !drop.outer).then_some(action).and_then(|action| self.result_rect(action, members)); match rect { Some(rect) => tab_outline(painter, rect.shrink(1.0), self.style.accent), - None => self.paint_drop_line(ctx, drop.line), + None => self.paint_drop_line(ctx, drop.line, drop.outer), } }