docking: drop_edges defaults to all sides; the docking profile's outer band takes only its top and bottom

This commit is contained in:
aiongg committed 2026-10-25 05:20:27 +00:00
1 parent 889f0367f2
commit 876d2539f9
4 files changed
+50 -47

No files matched your search

+26 -31
View File
@@ -133,13 +133,8 @@ fn a_group_that_takes_no_tabs_maps_its_whole_body_to_its_edges() {
let canvas = Permissions { accepts_tabs: false, ..Permissions::default() };
let d = driver(&[(1, canvas)]);
let (body, rect) = group(&d, 1);
// By default only its top and bottom: left of center goes to the nearer of those.
for (zone, at, edges) in [
(Zone::Left, body.center() - vec2(10.0, 2.0), Sides::ALL),
(Zone::Top, body.center() - vec2(10.0, 2.0), DockStyle::default().drop_edges),
(Zone::Bottom, body.center() + vec2(-10.0, 2.0), DockStyle::default().drop_edges),
] {
let mut d = driver(&[(1, canvas)]).with_style(move |style| style.drop_edges = edges);
for (zone, at) in [(Zone::Left, body.center() - vec2(10.0, 0.0)), (Zone::Top, body.center() - vec2(0.0, 10.0))] {
let mut d = driver(&[(1, canvas)]);
d.hold(tab(&d, 3), at);
assert!(d.filled(line(rect, zone)), "{zone:?}");
let out = d.release(at);
@@ -171,7 +166,7 @@ fn a_side_without_room_joins_as_a_tab_or_offers_nothing() {
let out = d.drag(tab(&d, 3), top);
assert_eq!(out.actions, vec![Action::Move { panel: 3, anchor: 1, placement: Placement::Split(Zone::Top) }]);
// A group that takes no tabs offers nothing there.
let mut d = narrow(&[(1, Permissions { accepts_tabs: false, ..Permissions::default() })]).with_style(|style| style.drop_edges = Sides::ALL);
let mut d = narrow(&[(1, Permissions { accepts_tabs: false, ..Permissions::default() })]);
d.hold(tab(&d, 3), left);
assert!(!d.outlined(rect.shrink(1.0)));
let out = d.release(left);
@@ -275,22 +270,14 @@ fn drop_edges_limit_the_outer_edges_and_the_canvas_sides() {
// The canvas has no tab strip: its body is its frame.
let (canvas, _) = group(&d, 0);
let (side, _) = group(&d, 1);
// The bottom edge takes nothing.
let at = pos2(canvas.center().x, 590.0);
let mut d = canvas_driver(Permissions::PROTECTED, left_right);
d.hold(tab(&d, 3), at);
assert!(!d.filled(line(canvas, Zone::Bottom)));
assert!(!moves(&d.release(at)));
// The left edge splits around the canvas column.
let at = pos2(canvas.left() + 5.0, canvas.center().y);
let mut d = canvas_driver(Permissions::PROTECTED, left_right);
d.hold(tab(&d, 3), at);
assert!(d.filled(line(canvas, Zone::Left)), "a line along the canvas column's left edge");
let out = d.release(at);
assert_eq!(out.actions, vec![Action::Move { panel: 3, anchor: 0, placement: Placement::Edge(Zone::Left) }]);
let column = split(SplitAxis::Horizontal, SplitSize::Ratio(0.25), tabs(&[3]), tabs(&[0]));
let sides = split(SplitAxis::Horizontal, SplitSize::FixedSecond(200.0), column, tabs(&[2]));
assert_eq!(d.layout.root, Some(split(SplitAxis::Horizontal, SplitSize::FixedFirst(200.0), tabs(&[1]), sides)));
// Without top and bottom, no outer edge takes a drop: left and right are docks here.
for at in [pos2(canvas.center().x, 590.0), pos2(canvas.left() + 5.0, 300.0), pos2(canvas.right() - 5.0, 300.0)] {
let mut d = canvas_driver(Permissions::PROTECTED, left_right);
d.hold(tab(&d, 3), at);
assert!(!d.filled(line(canvas, Zone::Bottom)), "{at:?}");
let out = d.release(at);
assert!(!moves(&out), "{at:?}: {:?}", out.actions);
}
// A side frame keeps all four sides.
for (zone, y) in [(Zone::Bottom, 590.0), (Zone::Top, side.top() + side.height() * 0.2)] {
let mut d = canvas_driver(Permissions::PROTECTED, left_right);
@@ -310,13 +297,21 @@ fn a_canvas_that_takes_splits_takes_them_only_on_its_drop_edges() {
let beside = Permissions { accepts_splits: true, ..Permissions::PROTECTED };
let d = canvas_driver(beside, |_| {});
let (body, _) = group(&d, 0);
let at = pos2(body.left() + body.width() * 0.3, body.bottom() - 40.0);
for (edges, zone) in [(DockStyle::default().drop_edges, Some(Zone::Bottom)), (Sides::LEFT_RIGHT, Some(Zone::Left)), (Sides::NONE, None)] {
let mut d = canvas_driver(beside, move |style| style.drop_edges = edges);
let out = d.drag(tab(&d, 3), at);
match zone {
Some(zone) => assert_eq!(out.actions, vec![Action::Move { panel: 3, anchor: 0, placement: Placement::Split(zone) }], "{edges:?}"),
None => assert!(!moves(&out), "{:?}", out.actions),
// Nearer the bottom than the left, and right of center nearer the right than the top.
let low = pos2(body.left() + body.width() * 0.3, body.bottom() - 40.0);
let high = pos2(body.right() - body.width() * 0.3, body.top() + 40.0);
for (edges, zones) in [
(DockStyle::default().drop_edges, [Some(Zone::Bottom), Some(Zone::Top)]),
(Sides::LEFT_RIGHT, [Some(Zone::Left), Some(Zone::Right)]),
(Sides::NONE, [None, None]),
] {
for (at, zone) in [low, high].into_iter().zip(zones) {
let mut d = canvas_driver(beside, move |style| style.drop_edges = edges);
let out = d.drag(tab(&d, 3), at);
match zone {
Some(zone) => assert_eq!(out.actions, vec![Action::Move { panel: 3, anchor: 0, placement: Placement::Split(zone) }], "{edges:?}"),
None => assert!(!moves(&out), "{edges:?}: {:?}", out.actions),
}
}
}
}
+2 -2
View File
@@ -170,7 +170,7 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> {
paths.and_then(|paths| paths.get(path).cloned()).unwrap_or_else(|| path.to_vec())
}
/// The outer edge band under the pointer (P8): the sides of
/// The outer edge band under the pointer (P8): the top and bottom of
/// [`crate::docking::DockStyle::drop_edges`], all four with
/// [`crate::docking::DockStyle::side_edge_zones`]. Returns a docked anchor that is not
/// moving, the zone and the preview: a line along the edge area's whole edge, or with side
@@ -181,7 +181,7 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> {
let band = if self.style.side_edge_zones { crate::docking::targets::SIDE_EDGE_BAND } else { self.style.edge_band };
let band = band.min(area.width() / 4.0).min(area.height() / 4.0);
let line = |zone| crate::docking::targets::edge_line(area, zone, self.style.drop_line_width);
let sides = self.style.drop_sides();
let sides = self.style.outer_edges();
let edges = [
(pointer.y - area.top(), Zone::Top),
(area.bottom() - pointer.y, Zone::Bottom),
+14 -7
View File
@@ -64,11 +64,11 @@ pub struct DockStyle {
/// bottom edges add a full-width row, a group's zones are [`Self::side_zone_fraction`] of its
/// body, and every preview is a line or a tab outline.
pub side_edge_zones: bool,
/// The layout's outer edges that take a drop, a new frame along that edge, and the sides of
/// a group that takes no tabs (a canvas) that take a split (top and bottom; the left and
/// right window edges are new docks). With [`crate::docking::Sides::NONE`], palettes dock
/// only through the dock bands. Not used with [`Self::side_edge_zones`], which takes all
/// four. Groups that take tabs keep all four sides.
/// Which sides of a group that takes no tabs (a canvas), and of the layout's outer edge, take
/// a drop (all four). Without [`Self::side_edge_zones`] the outer left and right edges are
/// new docks, so only top and bottom apply there. A dock-column app whose palettes dock only
/// at the left and right sets [`crate::docking::Sides::NONE`] with [`Self::new_dock_zones`]
/// on. Not used with [`Self::side_edge_zones`]. Groups that take tabs keep all four sides.
pub drop_edges: super::Sides,
/// The share of a group's body, below the tab strip, that means "beside" on each side: of
/// its width from the left and right edges, of its height from the top and bottom edges
@@ -196,7 +196,7 @@ impl DockStyle {
title_height: super::chrome::TITLE_HEIGHT,
edge_band: 28.0,
side_edge_zones: false,
drop_edges: super::Sides::TOP_BOTTOM,
drop_edges: super::Sides::ALL,
side_zone_fraction: 1.0 / 3.0,
drop_line_width: 4.0,
new_dock_zones: true,
@@ -234,12 +234,19 @@ impl DockStyle {
}
}
/// The sides [`Self::drop_edges`] gives in this profile: all four with
/// The canvas sides [`Self::drop_edges`] gives in this profile: all four with
/// [`Self::side_edge_zones`].
pub(super) fn drop_sides(&self) -> super::Sides {
if self.side_edge_zones { super::Sides::ALL } else { self.drop_edges }
}
/// The outer edges that take a drop: all four with [`Self::side_edge_zones`], else the top
/// and bottom of [`Self::drop_edges`].
pub(super) fn outer_edges(&self) -> super::Sides {
let sides = self.drop_sides();
if self.side_edge_zones { sides } else { super::Sides { left: false, right: false, ..sides } }
}
/// How a group's body divides into drop zones: [`crate::docking::SIDE_EDGE_ZONE_DEPTH`] deep
/// with [`Self::side_edge_zones`], else [`Self::side_zone_fraction`] of it, with
/// [`Self::drop_line_width`] previews. Pass it to [`crate::docking::resolve_drop_by`].
+8 -7
View File
@@ -250,7 +250,7 @@ A flyout fits its content the same way, without the title bar.
| The seam between two stacked groups of a dock column | A new group between them | `seam_band` (28 pt) |
| Each edge of a dock column | A new column on that side | `column_edge_band` (28 pt) |
| The docked area's left or right edge without a dock, and an empty dock | A new dock | `new_dock_band` (28 pt) |
| The edge area's sides in `drop_edges` (top and bottom; all four with `side_edge_zones`) | A new frame along that whole side, a quarter of the edge area | `edge_band` (28 pt, 8 pt with `side_edge_zones`; at most a quarter of the edge area) |
| The edge area's top and bottom in `drop_edges` (all four sides with `side_edge_zones`) | A new frame along that whole side, a quarter of the edge area | `edge_band` (28 pt, 8 pt with `side_edge_zones`; at most a quarter of the edge area) |
Where a band overlaps a group's outer third, both mean the same side. Without
`side_edge_zones`, a band reaches only 4 pt past its line into a tab strip that takes tabs;
@@ -261,12 +261,13 @@ A flyout fits its content the same way, without the title bar.
one side holds a protected panel, that side. Side panels in split frames then get no band
across the whole window, and the drop wraps only the canvas column. A drop on the band
anchors on the canvas, so `Placement::Edge` narrows the same way when applied.
- `DockStyle::drop_edges` (a `Sides`) lists the edge area's sides that take a drop, and the
sides of a group that takes no tabs (a canvas) that take a split; there, the whole body goes
to the nearest listed side, and with none listed the canvas takes nothing. Groups that take
tabs keep all four sides. The default is top and bottom. An app whose palettes dock only
through the dock bands sets `Sides::NONE`; one whose palettes sit left and right of the
canvas sets `Sides::LEFT_RIGHT`. `side_edge_zones` uses all four and ignores it.
- `DockStyle::drop_edges` (a `Sides`, all four by default) lists which sides of a group that
takes no tabs (a canvas), and of the layout's outer edge, take a drop. A canvas's whole body
goes to the nearest listed side; with none listed it takes nothing. In the docking profile
the outer left and right edges are new docks, so only top and bottom apply there. Groups
that take tabs keep all four sides. A dock-column app whose palettes dock only at the left
and right, as in AutoCAD, sets `Sides::NONE` with `new_dock_zones` on. Not used with
`side_edge_zones`.
- With `side_edge_zones` (the split-tree arrangement), a frame's zones are a picture frame
instead: trapezoids along the four edges of the body below the tab strip, all four the same
depth, `docking::SIDE_EDGE_ZONE_DEPTH` (40 pt), or a quarter of the body's smaller side when