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

commit979e8da1770f71f93b546a22844563399833410a
parent55030fb08e
authorLucas Galante <lsgalante12@gmail.com>
date2026-09-30 12:43
refactor: the palette has no Camera Pivot Size row

Its owner was a "Camera Pivot Size" param on the active camera, which no
camera node has, so the row only ever wrote the live field; the viewport
menu's slider sets that. Owner::ActiveCamera goes with its one row.

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

 CLAUDE.md     | 12 +++++-----
 src/dialog.rs | 72 +++++++----------------------------------------------------
 src/main.rs   | 15 ++++++-------
 3 files changed, 22 insertions(+), 77 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 5b5757a..8491cbe 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -413,7 +413,7 @@ Where it all went:
   `DesignSettings` into `state.kdl` (`viewport` and the new `render` block),
   and edited as rows of the dialog's one list — `SETTINGS` in
   `src/dialog.rs`, whose rows are `Owner::Field` (a live field, with a `Ctl`
-  saying what control draws it) or `Owner::ActiveCamera`; the toggles are
+  saying what control draws it); the toggles are
   registry commands whose palette rows carry a switch, read through
   `command_toggle_state`. The table plus the toggle commands are the app's
   whole display configuration, so a value left out of both is GONE, not
@@ -3089,11 +3089,13 @@ selection stays where it was:
 
 **A setting row edits the live field** — see "There are no meta nodes"
 above, which is where these values used to live and why a direct write did
-not stick. `SETTINGS` is the table of which row belongs to which owner:
+not stick. `SETTINGS` is the table of rows and their owners, each an
 `Owner::Field` (a live field, with a `Ctl` saying what control draws it,
-since a bare Rust field carries no type or range the way a param did) or
-`Owner::ActiveCamera` (an active-camera param with the live field as its
-fallback — the Default Camera has no node). The toggles the retired
+since a bare Rust field carries no type or range the way a param did).
+There was a second kind, `Owner::ActiveCamera` — an active-camera param
+with the live field as its fallback — whose one row, Camera Pivot Size,
+went on 2026-09-30: no camera node has that param, so the row only ever
+wrote the field, which the viewport menu's slider sets. The toggles the retired
 subnets held are NOT rows of the table: each is a registry command with a
 switch on its own row, and a second row per toggle would have listed every
 switch twice. A row's id is its label under `SETTING_ROW_PREFIX`
diff --git a/src/dialog.rs b/src/dialog.rs
index 55852fe..8244c49 100644
--- a/src/dialog.rs
+++ b/src/dialog.rs
@@ -1279,21 +1279,19 @@ pub fn setting_of_row(id: &str) -> Option<&'static Setting> {
 /// were DOWNSTREAM of the root meta node, whose utility subnets were copied
 /// over live state on every param change, so a write straight to
 /// `State::grid_thickness` survived exactly until the next one. With that
-/// node retired the live field IS the value; this enum says which of the two
-/// remaining kinds of owner each row has. (A third kind — a toggle the
-/// command registry owns — went when the settings joined the commands list:
-/// those toggles ARE command rows there, and a second row for each would
-/// have listed every switch twice.)
+/// node retired the live field IS the value, and it is the one kind of owner
+/// left. (A toggle the command registry owns went when the settings joined
+/// the commands list: those toggles ARE command rows there, and a second row
+/// for each would have listed every switch twice. A param on the ACTIVE
+/// camera, the Camera Pivot Size row's, went on 2026-09-30: no camera node
+/// has the param, so the row only ever wrote the live field, which the
+/// viewport menu's slider sets.)
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum Owner {
     /// A display setting the app owns outright: a live field on `State`,
     /// persisted by `DesignSettings` into `state.kdl`. Named by the key
     /// `settings_field_*` dispatch on.
     Field(&'static str),
-    /// A param on the ACTIVE camera node, with the live field as the
-    /// fallback: the Default Camera has no node, so there is nothing to write
-    /// but the field.
-    ActiveCamera(&'static str),
 }
 
 /// The control a [`Setting`] row draws.
@@ -1332,12 +1330,6 @@ impl Setting {
     const fn field(label: &'static str, key: &'static str, ctl: Ctl) -> Self {
         Setting { label, owner: Owner::Field(key), ctl }
     }
-
-    /// A row over the active camera's param of that name, a whole number
-    /// in tenths as the camera template's own spinbox is.
-    const fn camera(label: &'static str, name: &'static str) -> Self {
-        Setting { label, owner: Owner::ActiveCamera(name), ctl: Ctl::Spin { min: 1.0, max: 50.0, unit: 10.0 } }
-    }
 }
 
 /// The setting rows, in the order an empty query lists them.
@@ -1387,7 +1379,6 @@ pub const SETTINGS: &[Setting] = &[
     Setting::field("Grid Color", "grid_color", Ctl::Color),
     Setting::field("Grid Thickness", "grid_thickness", Ctl::Spin { min: 2.0, max: 200.0, unit: 1000.0 }),
     Setting::field("Origin Size", "origin_size", Ctl::Spin { min: 1.0, max: 50.0, unit: 10.0 }),
-    Setting::camera("Camera Pivot Size", "Camera Pivot Size"),
 ];
 
 impl State {
@@ -1872,23 +1863,6 @@ impl State {
                 Ctl::Slider { dec, .. } => format!("{:.*}", dec, self.settings_field_f32(key)),
                 Ctl::Choice(_) => self.settings_field_text(key),
             },
-            Owner::ActiveCamera(name) => {
-                let node_value = (self.active_camera != "Default Camera")
-                    .then(|| {
-                        self.current_dir()
-                            .children
-                            .iter()
-                            .find(|c| c.node_type == "camera" && c.name == self.active_camera)?
-                            .params
-                            .iter()
-                            .find(|p| p.name == name)
-                            .map(|p| p.text().to_string())
-                    })
-                    .flatten();
-                // No camera node behind the Default Camera: the live field
-                // is the value, in the same tenths the camera param uses.
-                node_value.unwrap_or_else(|| ((self.camera_pivot_size * 10.0).round() as i32).to_string())
-            }
         }
     }
 
@@ -2029,29 +2003,6 @@ impl State {
     fn setting_write(&mut self, s: &Setting, value: &str) {
         match s.owner {
             Owner::Field(key) => self.settings_field_write(key, s.ctl, value),
-            Owner::ActiveCamera(name) => {
-                let active = self.active_camera.clone();
-                let wrote = {
-                    let dir = self.current_dir_mut();
-                    match dir
-                        .children
-                        .iter_mut()
-                        .find(|c| c.node_type == "camera" && c.name == active)
-                        .and_then(|c| c.params.iter_mut().find(|p| p.name == name))
-                    {
-                        Some(p) => {
-                            p.set_text(value.to_string());
-                            true
-                        }
-                        None => false,
-                    }
-                };
-                if !wrote {
-                    if let Ok(v) = value.parse::<f32>() {
-                        self.camera_pivot_size = v / 10.0;
-                    }
-                }
-            }
         }
     }
 
@@ -2251,9 +2202,7 @@ impl State {
     /// drag this runs on every motion, so state.kdl is written on the
     /// RELEASE (`dialog_mouse_input`) rather than here; a wheel notch or an
     /// arrow key is a single landing and saves at once, as the menu's wheel
-    /// does. A spin row lands its whole number over the row's unit; the
-    /// Camera Pivot Size row, whose owner is the active camera, writes as
-    /// `setting_write` always has and re-bakes the pivot. A row the landing
+    /// does. A spin row lands its whole number over the row's unit. A row the landing
     /// does not know falls through to `apply_setting`.
     pub(crate) fn land_dialog_slider(&mut self, id: &str, v: f32) {
         if id == ZOOM_ROW_ID {
@@ -2268,11 +2217,6 @@ impl State {
                     self.land_draw_time_setting(key, shown)
                 }
                 (Owner::Field(key), Ctl::Spin { unit, .. }) => self.land_draw_time_setting(key, v.round() / unit),
-                (Owner::ActiveCamera(_), Ctl::Spin { .. }) => {
-                    self.setting_write(s, &(v.round() as i64).to_string());
-                    self.update_pivot_geometry();
-                    true
-                }
                 _ => false,
             };
             if landed {
diff --git a/src/main.rs b/src/main.rs
index 076ba42..fd49be3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13440,9 +13440,6 @@ mod tests {
                         }
                     }
                 }
-                // The active camera's params exist only once a camera node
-                // does; the Default Camera branch is exercised below.
-                Owner::ActiveCamera(_) => {}
             }
         }
     }
@@ -13892,9 +13889,6 @@ mod tests {
         state.land_dialog_slider(&setting_row_id("Group Marker Size"), 0.05);
         assert!((state.group_marker_size - state.point_marker_size).abs() < 1e-6);
         assert_eq!(state.settings_row_value("Point Marker Size"), state.settings_row_value("Group Marker Size"));
-        state.land_dialog_slider(&setting_row_id("Camera Pivot Size"), 20.0);
-        assert!((state.camera_pivot_size - 2.0).abs() < 1e-6, "{}", state.camera_pivot_size);
-        assert!(state.pending_pivot.is_some(), "the pivot re-baked");
         assert_eq!(state.rt_geometry_version, version, "a spin row re-evaluated the graph");
         assert!((saved(&path) - 1.0).abs() < 1e-3, "the file follows every single landing");
         let kdl = fs::read_to_string(&path).expect("a settings file");
@@ -14203,12 +14197,17 @@ mod tests {
             "Wireframe Color", "Wire Opacity", "Wire Thickness", "Geometry Opacity",
             // main
             "Background Color",
-            // camera
-            "Camera Pivot Size",
         ] {
             assert!(labels.contains(&label), "'{label}' has no Settings row and no other way in");
         }
+        // The camera subnet's Camera Pivot Size is the viewport menu's
+        // slider, under Show Camera Pivot; its palette row is gone.
+        assert!(!labels.contains(&"Camera Pivot Size"), "the palette's pivot size row is retired");
         let mut state = State::new(false);
+        assert!(
+            state.viewport_menu_rows_of(None).1.contains(&crate::app::ViewportMenuAction::CameraPivotSizeSlider),
+            "Camera Pivot Size has no way in"
+        );
         for id in [
             "toggle_grid", "toggle_origin", "toggle_wireframe",
             "toggle_wire_single_color", "toggle_ray_traced_preview",