graphic design tool
git clone https://git.lucas.co/cce-designer.git
feat: View utility node — the pane toggles' new home
The pane-visibility switches move off Main's View section onto a
dedicated View utility node, the Guides pattern: a setting's home is a
utility node, the header menu items stay as command access. The node
sits in the spaced utility column — Main (0,0), View (0,2), Guides
(0,4), Render (0,6); older saves' Guides/Render parked at prior default
slots slide down, and Main's copies of the toggles (plus its now-empty
View section) retire on load. Toggle labels read by pane name alone
inside the Panes section; the param names stay the dispatch identity,
so sync_parameters_to_project's pane actions and the live-toggle
refresh (now with a View arm) work unchanged. Pane state remains
session-owned: the toggles seed and refresh from live state, never from
the saved project.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/app.rs | 9 ++++--
src/project.rs | 89 ++++++++++++++++++++++++++++++++++++++--------------------
2 files changed, 64 insertions(+), 34 deletions(-)
diff --git a/src/app.rs b/src/app.rs
index 266460c..90498d7 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1865,14 +1865,16 @@ impl State {
}
fn refresh_main_node_live_toggles(&mut self, slot_idx: usize) {
- let live_main: [(&str, bool); 7] = [
+ let live_main: [(&str, bool); 2] = [
+ ("Circular Pane", self.circular_network_pane),
+ ("Ray Traced Preview", self.viewport().rt_mode),
+ ];
+ let live_view: [(&str, bool); 5] = [
("Show Network Pane", self.show_network),
("Show Viewport Pane", self.show_viewport),
("Show Parameters Pane", self.show_parameters),
("Show Spreadsheet Pane", self.show_spreadsheet),
("Show Playbar Pane", self.show_playbar),
- ("Circular Pane", self.circular_network_pane),
- ("Ray Traced Preview", self.viewport().rt_mode),
];
let live_guides: [(&str, bool); 3] = [
("Show Grid Guide", self.viewport().show_grid),
@@ -1883,6 +1885,7 @@ impl State {
let Some(child) = dir.children.get_mut(slot_idx) else { return };
let live: &[(&str, bool)] = match child.name.as_str() {
"Main" => &live_main,
+ "View" => &live_view,
"Guides" => &live_guides,
_ => return,
};
diff --git a/src/project.rs b/src/project.rs
index a61d3c4..1bc5bae 100644
--- a/src/project.rs
+++ b/src/project.rs
@@ -383,11 +383,17 @@ impl State {
ensure_param(main_node, "Undo", "button", "", &[], None, None, None);
ensure_param(main_node, "Redo", "button", "", &[], None, None, None);
- ensure_param(main_node, "View", "section", "", &[], None, None, None);
- ensure_param(main_node, "Show Viewport Pane", "toggle", bool_str(show_viewport), &[], None, None, None);
- ensure_param(main_node, "Show Parameters Pane", "toggle", bool_str(show_parameters), &[], None, None, None);
- ensure_param(main_node, "Show Spreadsheet Pane", "toggle", bool_str(show_spreadsheet), &[], None, None, None);
- ensure_param(main_node, "Show Playbar Pane", "toggle", bool_str(show_playbar), &[], None, None, None);
+ // The pane-visibility toggles moved to the View utility node (below):
+ // retire Main's copies and its now-empty View section from older saves.
+ // No value migration — pane state is session-owned, never applied from
+ // the project, so the View node seeds from live state.
+ main_node.params.retain(|p| {
+ !matches!(
+ p.name.as_str(),
+ "View" | "Show Network Pane" | "Show Viewport Pane" | "Show Parameters Pane"
+ | "Show Spreadsheet Pane" | "Show Playbar Pane"
+ )
+ });
// Network — renamed from the retired "Network Settings" (migrate older
// saves' section param in place so its position survives the reorder).
@@ -396,7 +402,6 @@ impl State {
sec.label = "Network".to_string();
}
ensure_param(main_node, "Network", "section", "", &[], None, None, None);
- ensure_param(main_node, "Show Network Pane", "toggle", bool_str(show_network), &[], None, None, None);
ensure_param(main_node, "Zoom In", "button", "", &[], None, None, None);
ensure_param(main_node, "Zoom Out", "button", "", &[], None, None, None);
ensure_param(main_node, "Reset Zoom", "button", "", &[], None, None, None);
@@ -499,9 +504,9 @@ impl State {
!matches!(p.name.as_str(), "Square Aspect" | "Show Camera Pivot" | "Camera Pivot Size")
});
- // Boolean settings and pane-visibility items render as toggles. Older
- // saves stored these as choice dropdowns / buttons; retype them and
- // reflect live pane state so a reopened project shows real switches.
+ // Boolean settings render as toggles. Older saves stored these as
+ // choice dropdowns / buttons; retype them so a reopened project shows
+ // real switches.
for p in main_node.params.iter_mut() {
match p.name.as_str() {
"Circular Pane" | "Ray Traced Preview" => {
@@ -509,21 +514,14 @@ impl State {
p.options.clear();
if p.default != "true" { p.default = "false".to_string(); }
}
- "Show Network Pane" => set_toggle(p, show_network),
- "Show Viewport Pane" => set_toggle(p, show_viewport),
- "Show Parameters Pane" => set_toggle(p, show_parameters),
- "Show Spreadsheet Pane" => set_toggle(p, show_spreadsheet),
- "Show Playbar Pane" => set_toggle(p, show_playbar),
_ => {}
}
}
- const MAIN_PARAM_ORDER: [&str; 29] = [
+ const MAIN_PARAM_ORDER: [&str; 23] = [
"File", "New Project", "Open", "Save", "Save As", "Exit",
"Edit", "Undo", "Redo",
- "View", "Show Viewport Pane", "Show Parameters Pane",
- "Show Spreadsheet Pane", "Show Playbar Pane",
- "Network", "Show Network Pane", "Zoom In", "Zoom Out",
+ "Network", "Zoom In", "Zoom Out",
"Reset Zoom", "Detach Circular Window", "Circular Pane",
"Viewport", "Active Camera",
"Ray Traced Preview",
@@ -544,9 +542,6 @@ impl State {
for p in main_node.params.iter_mut() {
if p.name == "New Project" {
p.label = "New".to_string();
- } else if p.name == "Show Network Pane" {
- // In the Network section the pane toggle reads as "Visible".
- p.label = "Visible".to_string();
} else if p.param_type == "toggle" {
if let Some(rest) = p.name.strip_prefix("Show ") {
p.label = rest.to_string();
@@ -554,13 +549,45 @@ impl State {
}
}
- // 2. Guides subnet — viewport guide toggles (home of the grid toggle,
+ // 2. View subnet — the pane-visibility switches, migrated off Main's
+ // View section (the Guides pattern: a setting's home is a utility
+ // node; the header menu items stay as command access). Pane state is
+ // session-owned and never applied from the project, so the toggles
+ // seed and refresh from live state.
+ let view_node = find_or_create_subnet(&mut self.fs_root, "View", "utility", (0.0, 2.0));
+ view_node.children.clear();
+ ensure_param(view_node, "Panes", "section", "", &[], None, None, None);
+ ensure_param(view_node, "Show Network Pane", "toggle", bool_str(show_network), &[], None, None, None);
+ ensure_param(view_node, "Show Viewport Pane", "toggle", bool_str(show_viewport), &[], None, None, None);
+ ensure_param(view_node, "Show Parameters Pane", "toggle", bool_str(show_parameters), &[], None, None, None);
+ ensure_param(view_node, "Show Spreadsheet Pane", "toggle", bool_str(show_spreadsheet), &[], None, None, None);
+ ensure_param(view_node, "Show Playbar Pane", "toggle", bool_str(show_playbar), &[], None, None, None);
+ for p in view_node.params.iter_mut() {
+ match p.name.as_str() {
+ "Show Network Pane" => set_toggle(p, show_network),
+ "Show Viewport Pane" => set_toggle(p, show_viewport),
+ "Show Parameters Pane" => set_toggle(p, show_parameters),
+ "Show Spreadsheet Pane" => set_toggle(p, show_spreadsheet),
+ "Show Playbar Pane" => set_toggle(p, show_playbar),
+ _ => {}
+ }
+ // "Show Network Pane" -> "Network": inside the Panes section the
+ // toggles read by pane name alone. The `name` stays the dispatch
+ // identity execute_menu_action fires on.
+ if p.param_type == "toggle" {
+ if let Some(rest) = p.name.strip_prefix("Show ").and_then(|r| r.strip_suffix(" Pane")) {
+ p.label = rest.to_string();
+ }
+ }
+ }
+
+ // 3. Guides subnet — viewport guide toggles (home of the grid toggle,
// migrated off Main). The utility column keeps one empty cell between
- // nodes: Main (0,0), Guides (0,2), Render (0,4); older saves parked
- // at prior defaults slide to the spaced slots.
- let guides_node = find_or_create_subnet(&mut self.fs_root, "Guides", "utility", (0.0, 2.0));
- if guides_node.position == (0.0, 1.0) {
- guides_node.position = (0.0, 2.0);
+ // nodes: Main (0,0), View (0,2), Guides (0,4), Render (0,6); older
+ // saves parked at prior defaults slide to the spaced slots.
+ let guides_node = find_or_create_subnet(&mut self.fs_root, "Guides", "utility", (0.0, 4.0));
+ if guides_node.position == (0.0, 1.0) || guides_node.position == (0.0, 2.0) {
+ guides_node.position = (0.0, 4.0);
}
guides_node.children.clear();
ensure_param(guides_node, "Guides", "section", "", &[], None, None, None);
@@ -589,13 +616,13 @@ impl State {
}
}
- // 3. Render subnet — render/display controls, present by default like
+ // 4. Render subnet — render/display controls, present by default like
// Main. Toggles reflect live state so a reopened project shows real
// switches. Two rows below Guides (the spaced column); older saves
// parked at the prior defaults slide down.
- let render_node = find_or_create_subnet(&mut self.fs_root, "Render", "utility", (0.0, 4.0));
- if render_node.position == (0.0, 1.0) || render_node.position == (0.0, 2.0) {
- render_node.position = (0.0, 4.0);
+ let render_node = find_or_create_subnet(&mut self.fs_root, "Render", "utility", (0.0, 6.0));
+ if render_node.position == (0.0, 1.0) || render_node.position == (0.0, 2.0) || render_node.position == (0.0, 4.0) {
+ render_node.position = (0.0, 6.0);
}
render_node.children.clear();