docking: a column's first tab docks beside its own column

A dock target named by the dragged panel itself anchors on another panel
of the same group or column, or leaves the panel in place when it is alone
there, instead of failing with InvalidPlacement.
This commit is contained in:
aiongg committed 2026-10-20 03:42:36 +00:00
1 parent ceb75f965e
commit cef258a356
2 files changed
+34 -1

No files matched your search

+23
View File
@@ -481,3 +481,26 @@ fn right_to_left_mirrors_docks_their_edges_and_new_docks() {
d.settle();
assert_eq!(d.rect("column-collapse", 5).left(), 0.0);
}
#[test]
fn a_column_s_first_tab_dropped_on_its_own_column_edge_anchors_on_another_panel() {
// Two columns, 0..240 and 240..480; the dragged tab is the first panel of the last one.
let wide = |columns: &[&[&[u32]]]| {
let mut layout = docked(Side::Start, columns);
if let Some(Node::Split { size, .. }) = &mut layout.root {
*size = SplitSize::FixedFirst(480.0);
}
layout
};
let mut d = Driver::new(wide(&[&[&[2]], &[&[3, 4]]]));
let out = d.drag(d.tab(3).center(), pos2(484.0, 300.0));
assert_eq!(out.actions, vec![Action::Move { panel: 3, anchor: 4, placement: Placement::Column { after: true } }]);
assert_eq!(columns(&d.layout, Side::Start), vec![vec![vec![2]], vec![vec![4]], vec![vec![3]]]);
// Alone in its column, the panel stays where it is.
let layout = wide(&[&[&[2]], &[&[3]]]);
let mut d = Driver::new(layout.clone());
let out = d.drag(d.tab(3).center(), pos2(484.0, 300.0));
assert_eq!(out.actions, vec![]);
assert_eq!(d.layout, layout);
}
+11 -1
View File
@@ -10,7 +10,8 @@ use egui::{Context, Key, Pos2, Rect, Sense, Stroke, StrokeKind, Ui, UiBuilder, p
use super::{GroupKind, Renderer};
use crate::docking::{
Action, Column, DockEvent, DockStyle, Group, GroupDrag, GroupGesture, Node, Profile, Side, Target,
Action, Column, DockEvent, DockStyle, Group, GroupDrag, GroupGesture, Node, Placement, Profile, Side, Target,
dock_model::Unit,
targets::{DockDrop, DockTarget, column_edge, horizontal_line, resolve_dock, usable, vertical_line},
};
use crate::layout::{ResizeHandle, ResizeHandleStyle, SplitAxis};
@@ -471,6 +472,15 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> {
let candidates = self.dock_candidates();
let drop = resolve_dock(pointer, &candidates)?;
let action = match drop.target.to() {
// A target named by the dragged panel is its own group or column: another panel
// there names the same one. With none, the drop leaves the panel where it is.
Target::At { anchor, placement } if &anchor == panel => {
let unit = if matches!(placement, Placement::Column { .. }) { Unit::Column } else { Unit::Group };
match self.layout.members(panel, unit).ok()?.into_iter().find(|p| p != panel) {
Some(anchor) => Action::Move { panel: panel.clone(), anchor, placement },
None => return Some((None, drop.line)),
}
}
Target::At { anchor, placement } => Action::Move { panel: panel.clone(), anchor, placement },
to @ Target::NewDock { .. }
if self.layout.place(panel).and_then(|place| self.layout.group(&place)).is_some_and(|g| g.panels.len() == 1) =>