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

commit0e755c3731b93eb8c8235e3912b4f29fbec8ed9d
parent0e31ca3ab5
authorLucas Galante <lsgalante12@gmail.com>
date2026-10-06 10:43
fix(controls): dialog query line, playbar and breadcrumb at the toolkit's heights

The dialog's query line was a hand-set 30px, the playbar's glyph buttons
a box clamped to 28px (20px in the docked 36px bar), and the network
breadcrumb a 24px constant. The query line now asks textbox_height(), the
transport buttons are square at button_height() centred in the strip,
and the breadcrumb (a run of segment buttons) is button_height() at all
nineteen of its layout and hit-test sites.

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

 src/app.rs     | 42 ++++++++++++++++++++++++------------------
 src/dialog.rs  |  8 ++++++--
 src/playbar.rs |  8 ++++----
 3 files changed, 34 insertions(+), 24 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index 0e499f1..b8dd873 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -71,7 +71,11 @@ pub const MENUBAR_H: f32 = 0.0;
 pub const SPLITTER_W: f32 = 6.0;
 
 pub const MIN_COLUMN: f32 = 120.0;
-pub const BREADCRUMB_H: f32 = 24.0;
+/// The network editor's breadcrumb strip: a run of segment buttons, so the
+/// toolkit's button height (the toolkit has no breadcrumb key of its own).
+pub fn breadcrumb_h() -> f32 {
+    cce_ui::layout::button_height()
+}
 pub const PLAYBAR_H: f32 = 36.0;
 
 pub use crate::param::{invalid_params, is_param_name, misnamed_params, param_name_of, unknown_param_kinds, ParamDef, ParamKind, ParamSlot, ParamValue};
@@ -3710,11 +3714,11 @@ impl State {
             let inside = free
                 && if self.circular_network_pane && is_network_part {
                     if i == CONTENT_IDX {
-                        self.circular_network_layout.hit_test_content(cx, cy, 0.0, BREADCRUMB_H)
+                        self.circular_network_layout.hit_test_content(cx, cy, 0.0, breadcrumb_h())
                     } else if i == LEFT_MENUBAR_IDX {
                         false
                     } else if i == BREADCRUMB_IDX {
-                        self.circular_network_layout.hit_test_breadcrumb(cx, cy, 0.0, BREADCRUMB_H)
+                        self.circular_network_layout.hit_test_breadcrumb(cx, cy, 0.0, breadcrumb_h())
                     } else if i == NETWORK_PANEL_IDX {
                         self.slots.network_panel.hit_test(cx, cy, &self.ui_context)
                     } else {
@@ -3887,7 +3891,7 @@ impl State {
             return false;
         }
         if self.circular_network_pane {
-            return self.circular_network_layout.hit_test_content(px, py, 0.0, BREADCRUMB_H);
+            return self.circular_network_layout.hit_test_content(px, py, 0.0, breadcrumb_h());
         }
         self.network_plate
             && [NETWORK_PANEL_IDX, crate::slots::NETWORK_PANEL2_IDX].iter().any(|&idx| {
@@ -3919,7 +3923,7 @@ impl State {
     /// stopped being drawn would be a strange thing to ship.
     pub fn in_network_area(&self, px: f32, py: f32) -> bool {
         if self.circular_network_pane {
-            return self.circular_network_layout.hit_test_content(px, py, 0.0, BREADCRUMB_H);
+            return self.circular_network_layout.hit_test_content(px, py, 0.0, breadcrumb_h());
         }
         let (cx, cy, cw, ch) = self.positions[CONTENT_IDX];
         px >= cx
@@ -3931,7 +3935,7 @@ impl State {
 
     pub fn in_network_pane(&self) -> bool {
         if self.circular_network_pane {
-            self.circular_network_layout.hit_test_content(self.cursor_x, self.cursor_y, 0.0, BREADCRUMB_H)
+            self.circular_network_layout.hit_test_content(self.cursor_x, self.cursor_y, 0.0, breadcrumb_h())
         } else if self.network_overlay() {
             // An overlay claims only what it DRAWS. The pane spans the window,
             // so a rect test would swallow every click meant for the scene
@@ -8709,8 +8713,9 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
             if let Some(menubar) = self.slots.left_menubar.as_any_mut().downcast_mut::<cce_ui::widget::MenuBar>() {
                 menubar.set_curved_circle(None);
             }
-            self.positions[BREADCRUMB_IDX] = (cx - r, cy - r + 45.0, 2.0 * r, BREADCRUMB_H);
-            self.positions[CONTENT_IDX] = (cx - r, cy - r + 45.0 + BREADCRUMB_H, 2.0 * r, 2.0 * r - (45.0 + BREADCRUMB_H));
+            let bc_h = breadcrumb_h();
+            self.positions[BREADCRUMB_IDX] = (cx - r, cy - r + 45.0, 2.0 * r, bc_h);
+            self.positions[CONTENT_IDX] = (cx - r, cy - r + 45.0 + bc_h, 2.0 * r, 2.0 * r - (45.0 + bc_h));
             self.positions[NETWORK_PANEL_IDX] = (cx - r, cy - r, 2.0 * r, 2.0 * r);
             self.slots.network_panel.set_rect(cx - r, cy - r, 2.0 * r, 2.0 * r);
             if let Some(plate) = self.slots.network_panel.as_any_mut().downcast_mut::<PassivePlate>() {
@@ -8912,8 +8917,9 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
                 if let Some(menubar) = self.slots.left_menubar.as_any_mut().downcast_mut::<cce_ui::widget::MenuBar>() {
                     menubar.set_curved_circle(None);
                 }
-                self.positions[BREADCRUMB_IDX] = (cx - r, cy - r + 45.0, 2.0 * r, BREADCRUMB_H);
-                self.positions[CONTENT_IDX] = (cx - r, cy - r + 45.0 + BREADCRUMB_H, 2.0 * r, 2.0 * r - (45.0 + BREADCRUMB_H));
+                let bc_h = breadcrumb_h();
+                self.positions[BREADCRUMB_IDX] = (cx - r, cy - r + 45.0, 2.0 * r, bc_h);
+                self.positions[CONTENT_IDX] = (cx - r, cy - r + 45.0 + bc_h, 2.0 * r, 2.0 * r - (45.0 + bc_h));
                 self.positions[NETWORK_PANEL_IDX] = (cx - r, cy - r, 2.0 * r, 2.0 * r);
                 self.slots.network_panel.set_rect(cx - r, cy - r, 2.0 * r, 2.0 * r);
                 if let Some(plate) = self.slots.network_panel.as_any_mut().downcast_mut::<PassivePlate>() {
@@ -9032,7 +9038,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
                 }
                 
                 let mb_h = 0.0;
-                let bc_h = if self.show_network { BREADCRUMB_H } else { 0.0 };
+                let bc_h = if self.show_network { breadcrumb_h() } else { 0.0 };
                 // The breadcrumb HOVERS over the graph: its strip rect stays
                 // (the widget lays its run out in it, and hit() claims only
                 // the segments), but the content spans the full plate — no
@@ -9058,7 +9064,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
                 // roll, so the two shadings never overlap — flush at the corner
                 // they read as one clipped lump, not a part in front of a plate.
                 let bc_pad = cce_ui::layout::bevel_width()
-                    + cce_ui::layout::bevel_width().min(BREADCRUMB_H * 0.2);
+                    + cce_ui::layout::bevel_width().min(breadcrumb_h() * 0.2);
                 self.positions[BREADCRUMB_IDX] =
                     (px + bc_pad, py + mb_h + bc_pad, (pw - 2.0 * bc_pad).max(0.0), bc_h);
                 self.positions[LEFT_MENUBAR_IDX] = (0.0, 0.0, 0.0, 0.0);
@@ -9085,7 +9091,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
                     plate.set_curved_circle(None);
                 }
                 self.positions[crate::slots::CONTENT2_IDX] = (qx, qy, qw, qh);
-                let bc2 = if qw > 0.0 { BREADCRUMB_H } else { 0.0 };
+                let bc2 = if qw > 0.0 { breadcrumb_h() } else { 0.0 };
                 self.positions[crate::slots::BREADCRUMB2_IDX] =
                     (qx + bc_pad, qy + bc_pad, (qw - 2.0 * bc_pad).max(0.0), bc2);
                 for (slot, on) in [
@@ -10808,7 +10814,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
                 if *button == MouseButton::Middle
                     && *btn_state == ElementState::Pressed
                     && self.cursor_in_viewport()
-                    && !(self.circular_network_pane && self.circular_network_layout.hit_test_content(self.cursor_x, self.cursor_y, 0.0, BREADCRUMB_H))
+                    && !(self.circular_network_pane && self.circular_network_layout.hit_test_content(self.cursor_x, self.cursor_y, 0.0, breadcrumb_h()))
                     && self.app_drag.is_none()
                 {
                     self.pan_drag = Some((self.cursor_x, self.cursor_y));
@@ -10828,11 +10834,11 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
                 let hits_widget = |state: &State, i: usize, x: f32, y: f32| -> bool {
                     if state.circular_network_pane && (i == CONTENT_IDX || i == LEFT_MENUBAR_IDX || i == BREADCRUMB_IDX) {
                         if i == CONTENT_IDX {
-                            state.circular_network_layout.hit_test_content(x, y, 0.0, BREADCRUMB_H)
+                            state.circular_network_layout.hit_test_content(x, y, 0.0, breadcrumb_h())
                         } else if i == LEFT_MENUBAR_IDX {
                             false
                         } else if i == BREADCRUMB_IDX {
-                            state.circular_network_layout.hit_test_breadcrumb(x, y, 0.0, BREADCRUMB_H)
+                            state.circular_network_layout.hit_test_breadcrumb(x, y, 0.0, breadcrumb_h())
                         } else {
                             false
                         }
@@ -10851,7 +10857,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
                 };
 
                 let in_circle_network_pane = if self.circular_network_pane {
-                    self.circular_network_layout.hit_test_content(self.cursor_x, self.cursor_y, 0.0, BREADCRUMB_H)
+                    self.circular_network_layout.hit_test_content(self.cursor_x, self.cursor_y, 0.0, breadcrumb_h())
                 } else {
                     in_network_pane
                 };
@@ -11019,7 +11025,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Detail) -> (Vec<String>, Vec<V
                                 self.slots.param.unfocus();
                                 self.sync_parameters_to_project();
                                 return true;
-                            } else if cx >= fx && cx < fx + fw && cy >= fy && cy < fy + (if self.show_network { BREADCRUMB_H } else { 0.0 }) {
+                            } else if cx >= fx && cx < fx + fw && cy >= fy && cy < fy + (if self.show_network { breadcrumb_h() } else { 0.0 }) {
                                 if self.slots.breadcrumb.mouse_input(*button, *btn_state, cx, cy, &mut self.ui_context) {
                                     return true;
                                 }
diff --git a/src/dialog.rs b/src/dialog.rs
index f967947..df8b07c 100644
--- a/src/dialog.rs
+++ b/src/dialog.rs
@@ -207,7 +207,11 @@ const DIALOG_W: f32 = 520.0;
 const DIALOG_H: f32 = 640.0;
 
 const PAD: f32 = 12.0;
-const QUERY_H: f32 = 30.0;
+/// The query line's height: the toolkit's text box height, since the line
+/// is the dialog's text field.
+fn query_h() -> f32 {
+    cce_ui::layout::textbox_height()
+}
 pub const ROW_H: f32 = 24.0;
 /// A toggle row's switch: the toolkit's `Toggle`, at the row's height less a
 /// hair of air, and about twice as wide as tall — the proportion the params
@@ -275,7 +279,7 @@ pub fn layout_at(width: f32, height: f32, ax: f32, ay: f32) -> (f32, f32, f32, f
 
 /// The query line, at the top of the plate.
 fn query_rect(rect: Rect) -> Rect {
-    Rect { x: rect.x + PAD, y: rect.y + PAD, width: (rect.width - 2.0 * PAD).max(0.0), height: QUERY_H }
+    Rect { x: rect.x + PAD, y: rect.y + PAD, width: (rect.width - 2.0 * PAD).max(0.0), height: query_h() }
 }
 
 /// The list's viewport.
diff --git a/src/playbar.rs b/src/playbar.rs
index 2f9655b..9e0973a 100644
--- a/src/playbar.rs
+++ b/src/playbar.rs
@@ -37,8 +37,6 @@ pub struct Playbar {
 }
 
 const PAD: f32 = 8.0;
-/// Width of the play/pause button box (square-ish, clamped to pane height).
-const BTN_W: f32 = 28.0;
 /// Space between two transport buttons.
 const BTN_GAP: f32 = 4.0;
 /// Width reserved right of the track for the frame readout.
@@ -91,9 +89,11 @@ impl Playbar {
     }
 
     /// The transport's square buttons, left to right: Previous Frame, Play,
-    /// Next Frame with the step buttons on, Play alone with them off.
+    /// Next Frame with the step buttons on, Play alone with them off. Glyph
+    /// buttons, so square at the toolkit's button height (clamped to the
+    /// pane), centred in the strip.
     fn button_rects(&self, rect: Rect) -> Vec<(Btn, Rect)> {
-        let s = (rect.height - 2.0 * PAD).max(12.0).min(BTN_W);
+        let s = cce_ui::layout::button_height().min(rect.height).max(12.0);
         let y = rect.y + (rect.height - s) * 0.5;
         let order: &[Btn] = if self.step_buttons { &[Btn::Prev, Btn::Play, Btn::Next] } else { &[Btn::Play] };
         order