Dock: floating panels, tab groups and adjustable slot heights

Panels can now float over the workspace, share a docked slot as tabs,
and split an edge at heights the user picks.

- Dropping a panel away from the edges floats it at the pointer. Floating
  panels move by their title bar, resize from a corner grip, and come to
  the front when clicked.
- Dropping in the top or bottom quarter of a docked slot docks the panel
  above or below it, as before. Dropping in the middle adds the panel as
  a tab of that slot. Pressing a tab shows that panel; dragging a tab
  pulls the panel out again.
- While dragging, a blue ghost previews the result: the new slot, the
  slot with its future tab row, or the floating window.
- A splitter between stacked slots sets their heights; double-click it
  to make the slots equal again. Heights persist.
- A press only becomes a drag after 5 px of movement, so clicking a title
  bar or tab never moves the panel.

The persisted layout now stores each edge as tab groups with a height
weight, plus a list of floating panels. Configs with the old
bare-panel-id stacks still load as single-panel slots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt
This commit is contained in:
Claude committed 2026-10-08 19:10:36 +00:00
1 parent 1bf584a1c1
commit 76883695c3
11 files changed
+1452 -417

No files matched your search

+2 -3
View File
@@ -911,10 +911,9 @@ impl OpenCADStudio {
if self.show_external_references {
// Always open expanded so the panel is immediately usable;
// dock it on the right if the user closed it from the layout.
if self
if !self
.dock
.location(crate::ui::dock::PanelId::ExternalReferences)
.is_none()
.is_placed(crate::ui::dock::PanelId::ExternalReferences)
{
self.dock.dock(
crate::ui::dock::PanelId::ExternalReferences,
+1 -1
View File
@@ -236,7 +236,7 @@ impl OpenCADStudio {
self.count_palette.show = open;
self.ribbon.set_count_palette(open);
if open {
if self.dock.location(id).is_none() {
if !self.dock.is_placed(id) {
self.dock.dock(id, crate::app::config::DockSide::Right, usize::MAX);
}
self.dock_expanded = Some(id);
+1 -1
View File
@@ -679,7 +679,7 @@ impl OpenCADStudio {
// rearrange themselves on upgrade for a panel they have
// not asked for.
let id = crate::ui::dock::PanelId::Browser;
if self.dock.location(id).is_none() {
if !self.dock.is_placed(id) {
// dock() clamps an out-of-range index to the end of
// the edge, so this appends below whatever is there.
self.dock
+1 -1
View File
@@ -374,7 +374,7 @@ impl OpenCADStudio {
"POINTCLOUDMANAGER" => {
let id = crate::ui::dock::PanelId::PointCloudManager;
self.pc_manager.show = true;
if self.dock.location(id).is_none() {
if !self.dock.is_placed(id) {
self.dock.dock(id, crate::app::config::DockSide::Right, usize::MAX);
}
self.dock_expanded = Some(id);
+1 -1
View File
@@ -577,7 +577,7 @@ impl OpenCADStudio {
self.sheet_set.show = on;
self.ribbon.set_sheet_set(on);
if on {
if self.dock.location(id).is_none() {
if !self.dock.is_placed(id) {
self.dock.dock(id, crate::app::config::DockSide::Right, usize::MAX);
}
self.dock_expanded = Some(id);