feat(ui): add dockable properties panel

Preserve panel width across left/right docking and persist resize, position, and auto-collapse preferences.

Refs #348
Refs #650
This commit is contained in:
Hakan Seven committed 2026-08-04 13:46:02 +03:00
1 parent e35348525a
commit 4be8d9735c
8 files changed
+355 -27

No files matched your search

+12
View File
@@ -520,6 +520,11 @@ impl OpenCADStudio {
section: self.start_section,
},
statusbar: self.statusbar_config.clone(),
properties: crate::app::config::PropertiesDockConfig {
side: self.properties_side,
width: self.properties_width,
auto_collapse: self.properties_auto_collapse,
},
annotation_auto_scale: self.annotation_auto_scale,
ribbon: crate::app::config::RibbonConfig {
collapse: self.ribbon.collapse_mode(),
@@ -550,6 +555,13 @@ impl OpenCADStudio {
// (`refresh_recent_thumbs`) — never here on the boot path.
self.start_section = cfg.start.section;
self.statusbar_config = cfg.statusbar;
self.properties_side = cfg.properties.side;
self.properties_width = if cfg.properties.width.is_finite() {
cfg.properties.width.clamp(220.0, 600.0)
} else {
250.0
};
self.properties_auto_collapse = cfg.properties.auto_collapse;
self.annotation_auto_scale = cfg.annotation_auto_scale.clamp(-4, 4);
self.ribbon.set_collapse_mode(cfg.ribbon.collapse);
self.plot_dialog = cfg.plot;
+104
View File
@@ -350,6 +350,110 @@ impl OpenCADStudio {
Task::none()
}
Message::PropertiesClose => {
self.show_properties = false;
self.properties_hovered = false;
self.properties_dragging = false;
self.properties_resizing = false;
self.properties_drag_last = None;
self.properties_dock_preview = None;
self.ribbon.set_properties(false);
Task::none()
}
Message::PropertiesAutoCollapseToggle => {
self.properties_auto_collapse ^= true;
// Keep the panel open while the pointer is still over the
// button; leaving the panel performs the first collapse.
self.properties_hovered = self.properties_auto_collapse;
self.save_config();
Task::none()
}
Message::PropertiesHover(hovered) => {
if self.properties_auto_collapse
&& !self.properties_dragging
&& !self.properties_resizing
{
self.properties_hovered = hovered;
}
Task::none()
}
Message::PropertiesDockGrab => {
self.properties_dragging = true;
self.properties_resizing = false;
self.properties_drag_last = None;
self.properties_dock_preview = Some(self.properties_side);
self.properties_hovered = true;
Task::none()
}
Message::PropertiesResizeGrab => {
self.properties_resizing = true;
self.properties_dragging = false;
self.properties_drag_last = None;
self.properties_dock_preview = None;
self.properties_hovered = true;
Task::none()
}
Message::PropertiesWidthReset => {
self.properties_width = 250.0;
self.save_config();
Task::none()
}
Message::PropertiesDragMove(point) => {
if self.properties_dragging {
self.properties_dock_preview = Some(if point.x < self.win_size.0 * 0.5 {
crate::app::config::DockSide::Left
} else {
crate::app::config::DockSide::Right
});
} else if self.properties_resizing {
if let Some(last) = self.properties_drag_last {
let dx = point.x - last.x;
let delta = match self.properties_side {
crate::app::config::DockSide::Left => dx,
crate::app::config::DockSide::Right => -dx,
};
let max_width = (self.win_size.0 * 0.45).clamp(220.0, 600.0);
self.properties_width =
(self.properties_width + delta).clamp(220.0, max_width);
}
}
if self.properties_dragging || self.properties_resizing {
self.properties_drag_last = Some(point);
}
Task::none()
}
Message::PropertiesDragRelease => {
let changed = if self.properties_dragging {
if let Some(side) = self.properties_dock_preview {
let changed = side != self.properties_side;
self.properties_side = side;
changed
} else {
false
}
} else {
self.properties_resizing
};
self.properties_dragging = false;
self.properties_resizing = false;
self.properties_drag_last = None;
self.properties_dock_preview = None;
if self.properties_auto_collapse {
self.properties_hovered = false;
}
if changed {
self.save_config();
}
Task::none()
}
Message::ScrollLayoutTabs(dx) => iced::widget::operation::scroll_by(
iced::advanced::widget::Id::new(crate::ui::statusbar::LAYOUT_TABS_SCROLL_ID),
iced::widget::scrollable::AbsoluteOffset { x: dx, y: 0.0 },