graphic design tool
git clone https://git.lucas.co/cce-designer.git
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",