forked from 1b-agent/opencadstudio-upstream
Dock: drop dead pallet-width setters, split on_dock, share span maths
- Only docked pallets have a width divider, so the Width drag and WidthReset no longer carry a branch for an undocked pallet; remove the then unused DockState::set_width / reset_width. Per-pallet widths are still read (older configs, floating size). Tests now size groups. - on_dock hands hover messages to dock_hover and the release to dock_drag_release. - dock_slot_spans and dock_landing share dock_open_spans. No behaviour change. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
This commit is contained in:
4 files changed
+111
-107
No files matched your search
@@ -186,7 +186,7 @@ fn dock_icon_press_switches_the_shown_group() {
|
||||
assert_eq!(app.dock_shown_group(DockSide::Left), Some(1));
|
||||
assert_eq!(app.dock_slot_spans(DockSide::Left), vec![(0, 0.0, 900.0)]);
|
||||
// Each group keeps its own width.
|
||||
app.dock.set_width(PanelId::Browser, 400.0);
|
||||
app.dock.set_group_width(DockSide::Left, 1, 400.0);
|
||||
assert_eq!(app.dock_column_width(DockSide::Left), 400.0);
|
||||
let _ = app.on_dock(DockMsg::IconPress(PanelId::BlockPalette));
|
||||
let _ = app.on_dock(DockMsg::DragRelease);
|
||||
|
||||
+90
-65
@@ -30,7 +30,7 @@ impl OpenCADStudio {
|
||||
/// whichever panel the message names.
|
||||
pub(crate) fn on_dock(&mut self, m: crate::ui::dock::DockMsg) -> iced::Task<Message> {
|
||||
use crate::app::config::DockSide;
|
||||
use crate::ui::dock::{DockDrag, DockMsg, DropTarget, FloatPanel, HoverPending};
|
||||
use crate::ui::dock::{DockDrag, DockMsg, FloatPanel};
|
||||
match m {
|
||||
DockMsg::DockGrab(id) => self.dock_begin(DockDrag::moving(id, None), Some(id)),
|
||||
DockMsg::IconPress(id) => {
|
||||
@@ -66,12 +66,11 @@ impl OpenCADStudio {
|
||||
self.save_config();
|
||||
}
|
||||
DockMsg::WidthReset(id) => {
|
||||
// Only docked pallets have the divider that sends this.
|
||||
if let Some((side, gi)) = self.dock.location(id) {
|
||||
self.dock.reset_group_width(side, gi);
|
||||
} else {
|
||||
self.dock.reset_width(id);
|
||||
self.save_config();
|
||||
}
|
||||
self.save_config();
|
||||
}
|
||||
DockMsg::TitleHover(id) => self.dock_title_hover = Some(id),
|
||||
DockMsg::TitleHoverEnd(id) => {
|
||||
@@ -150,6 +149,22 @@ impl OpenCADStudio {
|
||||
self.save_config();
|
||||
}
|
||||
DockMsg::Close(id) => return self.dock_set_open(id, false),
|
||||
DockMsg::Hover(_)
|
||||
| DockMsg::HoverEnd(_)
|
||||
| DockMsg::HoverExit
|
||||
| DockMsg::HoverStay
|
||||
| DockMsg::HoverSettled(_) => return self.dock_hover(m),
|
||||
DockMsg::DragMove(point) => self.dock_drag_move(point),
|
||||
DockMsg::DragRelease => self.dock_drag_release(),
|
||||
}
|
||||
iced::Task::none()
|
||||
}
|
||||
|
||||
/// Pointer entering / leaving strip icons, edge columns and floating
|
||||
/// frames, and the delayed auto-hide reveal / hide that follows.
|
||||
fn dock_hover(&mut self, m: crate::ui::dock::DockMsg) -> iced::Task<Message> {
|
||||
use crate::ui::dock::{DockMsg, HoverPending};
|
||||
match m {
|
||||
DockMsg::Hover(id) => {
|
||||
self.dock_icon_hover = Some(id);
|
||||
// Only an auto-hiding edge (or floating pallet) reacts to
|
||||
@@ -198,54 +213,57 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
}
|
||||
DockMsg::DragMove(point) => self.dock_drag_move(point),
|
||||
DockMsg::DragRelease => {
|
||||
let mut changed = false;
|
||||
match self.dock_drag.take() {
|
||||
Some(DockDrag::Move {
|
||||
group: Some((side, gi)),
|
||||
target: Some(DropTarget::Edge { side: to, index }),
|
||||
..
|
||||
}) => changed = self.dock.move_group(side, gi, to, index),
|
||||
Some(DockDrag::Move {
|
||||
panel,
|
||||
group: None,
|
||||
target: Some(target),
|
||||
..
|
||||
}) => {
|
||||
changed = match target {
|
||||
DropTarget::Edge { side, index } => self.dock.dock(panel, side, index),
|
||||
DropTarget::Join { side, group, index } => {
|
||||
self.dock.join_group(panel, side, group, index)
|
||||
}
|
||||
DropTarget::Float { x, y } => {
|
||||
let (w, h) = self.dock_float_size(panel);
|
||||
// A floating panel keeps its docking choice.
|
||||
let docking = self.dock.float_rect(panel).is_none_or(|f| f.docking);
|
||||
self.dock.float(FloatPanel {
|
||||
docking,
|
||||
..FloatPanel::new(panel, x, y, w, h)
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
Some(DockDrag::Move { .. } | DockDrag::LayerColumn) | None => {}
|
||||
Some(DockDrag::Width(_) | DockDrag::Split { .. } | DockDrag::FloatSize { .. }) => {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
self.save_config();
|
||||
}
|
||||
self.dock_drag_last = None;
|
||||
self.xref_col_drag = None;
|
||||
self.xref_col_last = None;
|
||||
self.xref_split_drag = false;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
iced::Task::none()
|
||||
}
|
||||
|
||||
/// Pointer released: land what was dragged and save a changed layout.
|
||||
fn dock_drag_release(&mut self) {
|
||||
use crate::ui::dock::{DockDrag, DropTarget, FloatPanel};
|
||||
let mut changed = false;
|
||||
match self.dock_drag.take() {
|
||||
Some(DockDrag::Move {
|
||||
group: Some((side, gi)),
|
||||
target: Some(DropTarget::Edge { side: to, index }),
|
||||
..
|
||||
}) => changed = self.dock.move_group(side, gi, to, index),
|
||||
Some(DockDrag::Move {
|
||||
panel,
|
||||
group: None,
|
||||
target: Some(target),
|
||||
..
|
||||
}) => {
|
||||
changed = match target {
|
||||
DropTarget::Edge { side, index } => self.dock.dock(panel, side, index),
|
||||
DropTarget::Join { side, group, index } => {
|
||||
self.dock.join_group(panel, side, group, index)
|
||||
}
|
||||
DropTarget::Float { x, y } => {
|
||||
let (w, h) = self.dock_float_size(panel);
|
||||
// A floating panel keeps its docking choice.
|
||||
let docking = self.dock.float_rect(panel).is_none_or(|f| f.docking);
|
||||
self.dock.float(FloatPanel {
|
||||
docking,
|
||||
..FloatPanel::new(panel, x, y, w, h)
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
Some(DockDrag::Move { .. } | DockDrag::LayerColumn) | None => {}
|
||||
Some(DockDrag::Width(_) | DockDrag::Split { .. } | DockDrag::FloatSize { .. }) => {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
self.save_config();
|
||||
}
|
||||
self.dock_drag_last = None;
|
||||
self.xref_col_drag = None;
|
||||
self.xref_col_last = None;
|
||||
self.xref_split_drag = false;
|
||||
}
|
||||
|
||||
/// Pointer motion while something in the dock is dragged.
|
||||
fn dock_drag_move(&mut self, point: iced::Point) {
|
||||
// NOTE: xref column drags intentionally do NOT ride this path: its
|
||||
@@ -353,18 +371,12 @@ impl OpenCADStudio {
|
||||
}
|
||||
DockDrag::Width(id) => {
|
||||
let Some(last) = last else { return };
|
||||
let dx = point.x - last.x;
|
||||
match self.dock.location(id) {
|
||||
// The divider sizes the panel's group.
|
||||
Some((side, gi)) => {
|
||||
let delta = if side == DockSide::Left { dx } else { -dx };
|
||||
let cur = self.dock_group_width(side, gi) + delta;
|
||||
self.dock.set_group_width(side, gi, cur);
|
||||
}
|
||||
None => {
|
||||
let cur = self.dock.settings(id).width + dx;
|
||||
self.dock.set_width(id, cur);
|
||||
}
|
||||
// The divider sizes the docked pallet's group.
|
||||
if let Some((side, gi)) = self.dock.location(id) {
|
||||
let dx = point.x - last.x;
|
||||
let delta = if side == DockSide::Left { dx } else { -dx };
|
||||
let cur = self.dock_group_width(side, gi) + delta;
|
||||
self.dock.set_group_width(side, gi, cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -634,13 +646,26 @@ impl OpenCADStudio {
|
||||
&self,
|
||||
side: crate::app::config::DockSide,
|
||||
) -> Vec<(usize, f32, f32)> {
|
||||
let Some(gi) = self.dock_shown_group(side) else {
|
||||
return Vec::new();
|
||||
};
|
||||
match self.dock_shown_group(side) {
|
||||
Some(gi) => self.dock_open_spans(&self.dock.groups(side)[gi], None),
|
||||
None => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The open pallets of `group` (plus `extra`, a pallet about to land
|
||||
/// there), each as (position in the group, top, bottom) down the
|
||||
/// workspace height.
|
||||
pub(crate) fn dock_open_spans(
|
||||
&self,
|
||||
group: &crate::ui::dock::DockGroup,
|
||||
extra: Option<crate::ui::dock::PanelId>,
|
||||
) -> Vec<(usize, f32, f32)> {
|
||||
let (_, avail) = self.dock_workspace_size();
|
||||
let group = &self.dock.groups(side)[gi];
|
||||
let open: Vec<usize> = (0..group.panels.len())
|
||||
.filter(|i| self.dock_panel_visible(group.panels[*i]))
|
||||
.filter(|i| {
|
||||
let id = group.panels[*i];
|
||||
Some(id) == extra || self.dock_panel_visible(id)
|
||||
})
|
||||
.collect();
|
||||
let weights: Vec<f32> = open.iter().map(|i| group.weights[*i]).collect();
|
||||
open.into_iter()
|
||||
|
||||
@@ -690,13 +690,10 @@ impl OpenCADStudio {
|
||||
}
|
||||
let (side, gi) = after.location(id)?;
|
||||
let group = &after.groups(side)[gi];
|
||||
let open: Vec<usize> = (0..group.panels.len())
|
||||
.filter(|i| group.panels[*i] == id || self.dock_panel_visible(group.panels[*i]))
|
||||
.collect();
|
||||
let weights: Vec<f32> = open.iter().map(|i| group.weights[*i]).collect();
|
||||
let at = open.iter().position(|i| group.panels[*i] == id)?;
|
||||
let (_, avail) = self.dock_workspace_size();
|
||||
let (top, bottom) = *crate::ui::dock::slot_spans(&weights, avail).get(at)?;
|
||||
let (_, top, bottom) = self
|
||||
.dock_open_spans(group, Some(id))
|
||||
.into_iter()
|
||||
.find(|(i, _, _)| group.panels[*i] == id)?;
|
||||
Some((top, bottom, after.group_width_px(side, gi, self.win_size.0)))
|
||||
}
|
||||
|
||||
|
||||
+16
-34
@@ -18,9 +18,9 @@ use std::collections::BTreeMap;
|
||||
pub enum DockMsg {
|
||||
/// Begin dragging `panel` to another side / position.
|
||||
DockGrab(PanelId),
|
||||
/// Begin resizing `panel`'s width.
|
||||
/// Begin resizing the width of docked `panel`'s group.
|
||||
ResizeGrab(PanelId),
|
||||
/// Reset `panel`'s width to its default.
|
||||
/// Reset the width of docked `panel`'s group to its default.
|
||||
WidthReset(PanelId),
|
||||
/// Toggle `panel`'s auto-collapse (pin) behavior.
|
||||
AutoCollapseToggle(PanelId),
|
||||
@@ -582,18 +582,6 @@ impl DockState {
|
||||
on
|
||||
}
|
||||
|
||||
/// Set the persisted width, clamped.
|
||||
pub fn set_width(&mut self, id: PanelId, width: f32) {
|
||||
let entry = self.panels.entry(id).or_insert_with(|| DockPanel::for_id(id));
|
||||
entry.width = width.clamp(DOCK_MIN_W, id.max_width());
|
||||
}
|
||||
|
||||
/// Reset width to the panel's default.
|
||||
pub fn reset_width(&mut self, id: PanelId) {
|
||||
let entry = self.panels.entry(id).or_insert_with(|| DockPanel::for_id(id));
|
||||
entry.width = id.default_width();
|
||||
}
|
||||
|
||||
pub fn set_auto_collapse(&mut self, id: PanelId, on: bool) {
|
||||
let entry = self.panels.entry(id).or_insert_with(|| DockPanel::for_id(id));
|
||||
entry.auto_collapse = on;
|
||||
@@ -846,7 +834,7 @@ pub enum DockDrag {
|
||||
target: Option<DropTarget>,
|
||||
grab: iced::Vector,
|
||||
},
|
||||
/// Sizing the width of `panel`'s group (or of `panel`, floating).
|
||||
/// Sizing the width of docked `panel`'s group.
|
||||
Width(PanelId),
|
||||
/// Moving the splitter between pallets `upper` and `lower` of a group.
|
||||
Split {
|
||||
@@ -1192,11 +1180,12 @@ mod tests {
|
||||
// Wider default for the column-rich references table, double the maximum.
|
||||
// (The 45%-of-window rule still dominates on narrow windows.)
|
||||
assert_eq!(state.width(PanelId::ExternalReferences, 3000.0), 460.0);
|
||||
state.set_width(PanelId::ExternalReferences, 5000.0);
|
||||
assert_eq!(state.width(PanelId::ExternalReferences, 3000.0), DOCK_MAX_W * 2.0);
|
||||
state.dock(PanelId::ExternalReferences, DockSide::Right, 1);
|
||||
state.set_group_width(DockSide::Right, 1, 5000.0);
|
||||
assert_eq!(state.group_width_px(DockSide::Right, 1, 3000.0), DOCK_MAX_W * 2.0);
|
||||
// Other panels keep the shared maximum.
|
||||
state.set_width(PanelId::BlockPalette, 5000.0);
|
||||
assert_eq!(state.width(PanelId::BlockPalette, 3000.0), DOCK_MAX_W);
|
||||
state.set_group_width(DockSide::Right, 0, 5000.0);
|
||||
assert_eq!(state.group_width_px(DockSide::Right, 0, 3000.0), DOCK_MAX_W);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1247,32 +1236,23 @@ mod tests {
|
||||
assert!(!state.dock(PanelId::Properties, DockSide::Left, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_width_clamps() {
|
||||
let mut state = DockState::default();
|
||||
state.set_width(PanelId::BlockPalette, 10.0);
|
||||
assert_eq!(state.width(PanelId::BlockPalette, 1600.0), DOCK_MIN_W);
|
||||
state.set_width(PanelId::BlockPalette, 5000.0);
|
||||
assert_eq!(state.width(PanelId::BlockPalette, 1600.0), DOCK_MAX_W);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn width_respects_maximum_window_fraction() {
|
||||
let mut state = DockState::default();
|
||||
state.set_width(PanelId::BlockPalette, 500.0);
|
||||
state.set_group_width(DockSide::Right, 0, 500.0);
|
||||
// Window too narrow -> capped by the 0.45 fraction, not DOCK_MAX_W.
|
||||
assert_eq!(state.width(PanelId::BlockPalette, 800.0), DOCK_MAX_W.min(360.0));
|
||||
assert_eq!(state.group_width_px(DockSide::Right, 0, 800.0), DOCK_MAX_W.min(360.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn width_does_not_panic_when_window_minimized() {
|
||||
let mut state = DockState::default();
|
||||
state.set_width(PanelId::BlockPalette, 500.0);
|
||||
state.set_group_width(DockSide::Right, 0, 500.0);
|
||||
// A minimized or not-yet-laid-out window reports width 0; the clamp
|
||||
// must fall back to DOCK_MIN_W instead of panicking on min > max.
|
||||
assert_eq!(state.width(PanelId::BlockPalette, 0.0), DOCK_MIN_W);
|
||||
assert_eq!(state.group_width_px(DockSide::Right, 0, 0.0), DOCK_MIN_W);
|
||||
// Deleted right below the minimum dock width behaves the same way.
|
||||
assert_eq!(state.width(PanelId::BlockPalette, 100.0), DOCK_MIN_W);
|
||||
assert_eq!(state.group_width_px(DockSide::Right, 0, 100.0), DOCK_MIN_W);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1303,7 +1283,7 @@ mod tests {
|
||||
#[test]
|
||||
fn join_group_stacks_and_shows_the_group() {
|
||||
let mut state = DockState::default();
|
||||
state.set_width(PanelId::Properties, 320.0);
|
||||
state.set_group_width(DockSide::Left, 0, 320.0);
|
||||
// Below Properties in the left group.
|
||||
assert!(state.join_group(PanelId::BlockPalette, DockSide::Left, 0, 1));
|
||||
assert!(state.right.is_empty(), "its old group disappears");
|
||||
@@ -1412,6 +1392,8 @@ mod tests {
|
||||
assert_eq!(state.group_width(DockSide::Right, 0), 260.0);
|
||||
state.set_group_width(DockSide::Left, 0, 5000.0);
|
||||
assert_eq!(state.group_width(DockSide::Left, 0), DOCK_MAX_W);
|
||||
state.set_group_width(DockSide::Left, 0, 10.0);
|
||||
assert_eq!(state.group_width(DockSide::Left, 0), DOCK_MIN_W);
|
||||
state.reset_group_width(DockSide::Left, 0);
|
||||
assert_eq!(state.group_width(DockSide::Left, 0), 250.0);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user