docking: window.panel.dock to a side without a dock moves one panel of a shared group

Action::MovePanel moves one panel to a Target; NewDock and Float give it a group of its own.
DockCommand::Dock with a side that has no dock used to fail with InvalidPlacement for a panel
that shares its group.
This commit is contained in:
aiongg committed 2026-10-19 13:30:02 +00:00
1 parent c715d3f25f
commit 5d5d11f854
4 files changed
+38 -3

No files matched your search

+1 -1
View File
@@ -284,7 +284,7 @@ impl Unit {
let panel = panel.clone();
Ok(match (self, to) {
(Self::Panel, Target::At { anchor, placement }) => Action::Move { panel, anchor, placement },
(Self::Panel, _) => return Err(DockError::InvalidPlacement),
(Self::Panel, to) => Action::MovePanel { panel, to },
(Self::Group, to) => Action::MoveGroup { panel, to },
(Self::Column, to) if floating => Action::MoveWindow { panel, to },
(Self::Column, to) => Action::MoveColumn { panel, to },
+10
View File
@@ -371,3 +371,13 @@ fn float_without_a_rect_takes_the_drag_size_inside_the_dock_area() {
fn float_with(space: &FloatSpace<P>, panel: &str) -> Vec<Action<P>> {
parse(json!({"command": "window.panel.float", "panel": panel})).unwrap().to_actions(&workspace(), false, space).unwrap()
}
#[test]
fn dock_to_a_side_without_a_dock_takes_one_panel_out_of_a_shared_group() {
let params = json!({"command": "window.panel.dock", "panel": "layers", "side": "End"});
assert_eq!(actions(params.clone()), vec![Action::MovePanel { panel: s("layers"), to: Target::NewDock { side: Side::End } }]);
let (workspace, _) = applied(params);
let side = |panel: &str| workspace.layout.location(&s(panel)).unwrap().group.and_then(|g| g.column).map(|c| c.side);
assert_eq!(side("layers"), Some(Side::End));
assert_eq!(side("channels"), Some(Side::Start));
}
+25
View File
@@ -310,6 +310,12 @@ pub enum Action<P> {
panel: P,
rect: Option<[f32; 4]>,
},
/// Move one panel, without the rest of its group. `Target::At` is [`Action::Move`];
/// `NewDock` and `Float` give the panel a group of its own.
MovePanel {
panel: P,
to: Target<P>,
},
}
/// What may happen to a panel. [`Layout::apply_with_permissions`] enforces these for every
@@ -726,6 +732,18 @@ impl<P: Clone + Eq + Hash> Layout<P> {
Action::Close { panel } if !permissions(panel).closable => return Err(DockError::NotAllowed),
Action::MoveFloating { panel, .. } if !permissions(panel).movable => return Err(DockError::NotAllowed),
Action::Open { anchor: Some(anchor), .. } if !permissions(anchor).accepts_tabs => return Err(DockError::NotAllowed),
Action::MovePanel { panel, to } => {
let policy = permissions(panel);
let allowed = policy.movable
&& match to {
Target::Float { .. } => policy.floatable,
Target::At { anchor, placement } => placement_allowed(placement, permissions(anchor)),
Target::NewDock { .. } => true,
};
if !allowed {
return Err(DockError::NotAllowed);
}
}
Action::MoveGroup { panel, to } | Action::MoveColumn { panel, to } | Action::MoveWindow { panel, to } => {
let members = self.members(panel, unit_of(&action))?;
if members.iter().any(|p| !permissions(p).movable) {
@@ -899,6 +917,13 @@ impl<P: Clone + Eq + Hash> Layout<P> {
let moving = self.take_unit(panel, unit)?;
self.insert_moving(moving, to.clone())
}
Action::MovePanel { panel, to } => match to {
Target::At { anchor, placement } => self.apply_inner(Action::Move { panel, anchor, placement }),
to => {
let entry = self.take(&panel).ok_or(DockError::MissingPanel)?;
self.insert_moving(Moving::group(Group::new(vec![entry.panel], 0)), to)
}
},
Action::CloseGroup { ref panel } | Action::CloseWindow { ref panel } => self.take_unit(panel, unit_of(&action)).map(|_| ()),
Action::CloseOthers { panel } => {
for other in self.members(&panel, Unit::Group)? {
+2 -2
View File
@@ -57,8 +57,8 @@ layout.apply(Action::Move {
# Ok::<(), craft_ui::docking::DockError>(())
```
- `Move`, `Float` and `Close` act on one panel. `MoveGroup`, `MoveColumn` and `MoveWindow` move a
group, a dock column or a floating window, named by any panel in it.
- `Move`, `MovePanel`, `Float` and `Close` act on one panel. `MoveGroup`, `MoveColumn` and
`MoveWindow` move a group, a dock column or a floating window, named by any panel in it.
- A `Target` is `At { anchor, placement }`, `Float { rect }` or `NewDock { side }`.
- A `Placement` is a tab position, a split zone, a new group above or below (with an optional
preferred `height`), a new column, or a new frame on an outer edge.