git.lucas.co / cce-designer
graphic design tool
git clone https://git.lucas.co/cce-designer.git

commit428dd88bde5bacb0a1d183b93824df4c720e1d13
parenta6fb0a955d
authorLucas Galante <lsgalante12@gmail.com>
date2026-10-08 14:14
refactor(panes): the pane geometry is the designer's own

SplitterLayout and CircularPaneLayout lived in cce-ui's layout module with the
designer as their only user; they are src/panes.rs now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

 src/app.rs   |  8 ++---
 src/main.rs  |  1 +
 src/panes.rs | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 103 insertions(+), 4 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index 636388e..4505fb3 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -2958,7 +2958,7 @@ pub struct State {
 
     pub slots: Box<WidgetSlots>,
     pub positions: Vec<(f32, f32, f32, f32)>,
-    pub splitter_layout: cce_ui::layout::SplitterLayout,
+    pub splitter_layout: crate::panes::SplitterLayout,
     /// The node right-click context menu: the targeted node slot and the
     /// actions parallel to the visible items pushed into `context_menu::show`.
     /// `None` when no menu is open. The menu's geometry/paint lives in the
@@ -3190,7 +3190,7 @@ pub struct State {
     pub graph_scroll_friction: f32,
     pub last_config_read: Instant,
     pub circular_network_pane: bool,
-    pub circular_network_layout: cce_ui::layout::CircularPaneLayout,
+    pub circular_network_layout: crate::panes::CircularPaneLayout,
     pub is_detached_network: bool,
     pub detached_circular_network: bool,
     /// This process IS the detached window for one pane — the generic sibling
@@ -8118,7 +8118,7 @@ pub(crate) fn geometry_to_spreadsheet_columns(geom: &Detail) -> (Vec<String>, Ve
 
         // Bundled fonts only (the designer's UI uses bundled families).
 
-        let splitter_layout = cce_ui::layout::SplitterLayout::new(sw, SPLITTER_W, MIN_COLUMN);
+        let splitter_layout = crate::panes::SplitterLayout::new(sw, SPLITTER_W, MIN_COLUMN);
         let templates_root = load_fs_tree();
         let node_templates = flatten_node_templates(&templates_root);
 
@@ -8397,7 +8397,7 @@ pub(crate) fn geometry_to_spreadsheet_columns(geom: &Detail) -> (Vec<String>, Ve
             graph_scroll_friction: 0.90,
             last_config_read: Instant::now(),
             circular_network_pane: is_detached_network || settings.viewport.circular_pane,
-            circular_network_layout: cce_ui::layout::CircularPaneLayout::new(250.0, 300.0, 180.0),
+            circular_network_layout: crate::panes::CircularPaneLayout::new(250.0, 300.0, 180.0),
             is_detached_network,
             detached_circular_network: false,
             detached_pane: None,
diff --git a/src/main.rs b/src/main.rs
index 1ef7d16..7141819 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -40,6 +40,7 @@ pub mod slots;
 pub mod command;
 pub mod dialog;
 pub mod layout;
+pub mod panes;
 pub mod mold;
 pub mod hull;
 pub mod scatter;
diff --git a/src/panes.rs b/src/panes.rs
new file mode 100644
index 0000000..0c946d0
--- /dev/null
+++ b/src/panes.rs
@@ -0,0 +1,98 @@
+//! The window's pane geometry: the three resizable columns and their splitters
+//! (`SplitterLayout`), and the circular network pane's hit zones
+//! (`CircularPaneLayout`). Plain arithmetic over rects and points. Moved from
+//! cce-ui's `layout` (2026-10-08), where the designer was its only user.
+
+pub struct SplitterLayout {
+    pub splitter1_x: f32,
+    pub splitter2_x: f32,
+    pub splitter_width: f32,
+    pub min_column_width: f32,
+}
+
+impl SplitterLayout {
+    pub fn new(width: f32, splitter_width: f32, min_column_width: f32) -> Self {
+        let s1 = (width - 2.0 * splitter_width) / 3.0;
+        let s2 = s1 + splitter_width + (width - 2.0 * splitter_width) / 3.0;
+        Self {
+            splitter1_x: s1,
+            splitter2_x: s2,
+            splitter_width,
+            min_column_width,
+        }
+    }
+
+    pub fn clamp(&mut self, total_width: f32, detached_circular_network: bool) {
+        if detached_circular_network {
+            let min_s2 = self.min_column_width;
+            let max_s2 = (total_width - self.min_column_width).max(min_s2);
+            self.splitter2_x = self.splitter2_x.clamp(min_s2, max_s2);
+        } else {
+            let min_s1 = self.min_column_width;
+            let max_s1 = (self.splitter2_x - self.splitter_width - self.min_column_width).max(min_s1);
+            self.splitter1_x = self.splitter1_x.clamp(min_s1, max_s1);
+            let min_s2 = self.splitter1_x + self.splitter_width + self.min_column_width;
+            let max_s2 = (total_width - self.min_column_width).max(min_s2);
+            self.splitter2_x = self.splitter2_x.clamp(min_s2, max_s2);
+        }
+    }
+
+    pub fn scale(&mut self, factor: f32) {
+        self.splitter1_x *= factor;
+        self.splitter2_x *= factor;
+    }
+
+    pub fn left_col(&self) -> (f32, f32) { // (x, width)
+        (0.0, self.splitter1_x)
+    }
+
+    pub fn center_col(&self) -> (f32, f32) { // (x, width)
+        let x = self.splitter1_x + self.splitter_width;
+        (x, self.splitter2_x - x)
+    }
+
+    pub fn right_col(&self, total_width: f32) -> (f32, f32) { // (x, width)
+        let x = self.splitter2_x + self.splitter_width;
+        (x, (total_width - x).max(0.0))
+    }
+}
+
+pub struct CircularPaneLayout {
+    pub x: f32,
+    pub y: f32,
+    pub r: f32,
+}
+
+impl CircularPaneLayout {
+    pub fn new(x: f32, y: f32, r: f32) -> Self {
+        Self { x, y, r }
+    }
+
+    pub fn hit_test_content(&self, cx: f32, cy: f32, menubar_h: f32, breadcrumb_h: f32) -> bool {
+        let dx = cx - self.x;
+        let dy = cy - self.y;
+        let dist_sq = dx * dx + dy * dy;
+        dist_sq <= self.r * self.r && cy >= self.y - self.r + 45.0 + menubar_h + breadcrumb_h
+    }
+
+    pub fn hit_test_menubar(&self, cx: f32, cy: f32, menubar_h: f32) -> bool {
+        let dx = cx - self.x;
+        let dy = cy - self.y;
+        let dist = (dx * dx + dy * dy).sqrt();
+        dist >= self.r - menubar_h && dist <= self.r && cy < self.y
+    }
+
+    pub fn hit_test_breadcrumb(&self, cx: f32, cy: f32, menubar_h: f32, breadcrumb_h: f32) -> bool {
+        let dx = cx - self.x;
+        let dy = cy - self.y;
+        let dist_sq = dx * dx + dy * dy;
+        dist_sq <= self.r * self.r && cy >= self.y - self.r + menubar_h && cy < self.y - self.r + 45.0 + menubar_h + breadcrumb_h
+    }
+
+    pub fn hit_test_border(&self, cx: f32, cy: f32, border_thickness: f32) -> bool {
+        let dx = cx - self.x;
+        let dy = cy - self.y;
+        let dist = (dx * dx + dy * dy).sqrt();
+        dist >= self.r - border_thickness && dist <= self.r
+    }
+}