From cc17d10ca92343b6ddfc3dd921b83f2413544a46 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 10 Oct 2026 12:34:02 +0000 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt --- src/app/dock/tests.rs | 2 +- src/app/dock/update.rs | 155 ++++++++++++++++++++++++----------------- src/app/dock/view.rs | 11 ++- src/ui/dock.rs | 50 +++++-------- 4 files changed, 111 insertions(+), 107 deletions(-) diff --git a/src/app/dock/tests.rs b/src/app/dock/tests.rs index 3f97ec975..8d93c89ce 100644 --- a/src/app/dock/tests.rs +++ b/src/app/dock/tests.rs @@ -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); diff --git a/src/app/dock/update.rs b/src/app/dock/update.rs index ad0b89cc8..15194a7bf 100644 --- a/src/app/dock/update.rs +++ b/src/app/dock/update.rs @@ -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 { 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 { + 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, + ) -> Vec<(usize, f32, f32)> { let (_, avail) = self.dock_workspace_size(); - let group = &self.dock.groups(side)[gi]; let open: Vec = (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 = open.iter().map(|i| group.weights[*i]).collect(); open.into_iter() diff --git a/src/app/dock/view.rs b/src/app/dock/view.rs index bd5b01864..c9ad9627d 100644 --- a/src/app/dock/view.rs +++ b/src/app/dock/view.rs @@ -690,13 +690,10 @@ impl OpenCADStudio { } let (side, gi) = after.location(id)?; let group = &after.groups(side)[gi]; - let open: Vec = (0..group.panels.len()) - .filter(|i| group.panels[*i] == id || self.dock_panel_visible(group.panels[*i])) - .collect(); - let weights: Vec = 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))) } diff --git a/src/ui/dock.rs b/src/ui/dock.rs index 5ef7d1986..66b342ec1 100644 --- a/src/ui/dock.rs +++ b/src/ui/dock.rs @@ -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, 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); }