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

commit8749dec96e724cd705bd9e385929bc2fc84657c7
parent15fb054248
authorLucas Galante <lsgalante12@gmail.com>
date2026-09-25 10:50
feat: a Show Origin switch in a new Guides group of the viewport menu

The existing toggle_origin command as a marked row, in a Guides group of
its own right after the framing rows — the scene furniture that is not the
geometry.

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

 CLAUDE.md   |  3 ++-
 src/app.rs  |  8 +++++++-
 src/main.rs | 33 ++++++++++++++++++++++++++++-----
 3 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 7841c94..d242972 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1377,7 +1377,8 @@ multi-selection, so taking `Shift+L` now would have to be given back later.
 ### Display mode: the viewport menu, and smooth shading
 
 The viewport's right-click menu carries the DISPLAY MODE under Frame All
-and View 1:1, in four groups a separator apart — wireframe (switch,
+and View 1:1, in five groups a separator apart — guides (Show Origin, the
+scene furniture that is not the geometry); wireframe (switch,
 thickness); points (Show Points, Point Size, and Group Marker Scale, which
 multiplies it); overlays (Show Point Markers and its size, Show Point
 Numbers, Show Point Normals — the annotations over the scene's points);
diff --git a/src/app.rs b/src/app.rs
index 4acf6c4..b187b96 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -4462,7 +4462,9 @@ impl State {
     }
 
     /// The viewport menu's rows and what each does, in groups a separator
-    /// apart: framing; the WIREFRAME (its switch and thickness); the POINTS
+    /// apart: framing; the GUIDES (Show Origin — the scene furniture that
+    /// is not the geometry); the WIREFRAME (its switch and thickness); the
+    /// POINTS
     /// (the Show Points switch, point size, and the group marker scale that
     /// multiplies it); the OVERLAYS (Show Point Markers and its size, Show
     /// Point Numbers, Show Point Normals — the annotations drawn over the
@@ -4482,6 +4484,10 @@ impl State {
         };
         let sep = ViewportMenuAction::Separator;
 
+        // Guides: the scene furniture that is not the geometry.
+        row(&mut options, &mut actions, "-".into(), sep);
+        row(&mut options, &mut actions, format!("{} {}", mark(self.viewport().show_origin), label("toggle_origin", "Show Origin")), ViewportMenuAction::Command("toggle_origin"));
+
         // Wireframe.
         row(&mut options, &mut actions, "-".into(), sep);
         row(&mut options, &mut actions, format!("{} {}", mark(self.wireframe), label("toggle_wireframe", "Show Wireframe")), ViewportMenuAction::Command("toggle_wireframe"));
diff --git a/src/main.rs b/src/main.rs
index c1c7600..062419a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1645,7 +1645,7 @@ mod tests {
     }
 
     /// The viewport menu reads in groups a separator apart: framing, then
-    /// the wireframe, the points, the point overlays, and the surface
+    /// the guides, the wireframe, the points, the point overlays, and the surface
     /// (shading, opacity, Show Occluded) — every display row in exactly one
     /// group.
     #[test]
@@ -1658,13 +1658,14 @@ mod tests {
             .map(|g| g.to_vec())
             .collect();
         assert_eq!(groups[0], vec![A::FrameAll, A::OneToOne]);
-        assert_eq!(groups[1], vec![A::Command("toggle_wireframe"), A::WireThicknessSlider]);
+        assert_eq!(groups[1], vec![A::Command("toggle_origin")]);
+        assert_eq!(groups[2], vec![A::Command("toggle_wireframe"), A::WireThicknessSlider]);
         assert_eq!(
-            groups[2],
+            groups[3],
             vec![A::Command("toggle_render_points"), A::PointSizeSlider, A::GroupMarkerScaleSlider]
         );
         assert_eq!(
-            groups[3],
+            groups[4],
             vec![
                 A::Command("toggle_point_markers"),
                 A::PointMarkerSizeSlider,
@@ -1673,13 +1674,35 @@ mod tests {
             ]
         );
         assert_eq!(
-            groups[4],
+            groups[5],
             vec![A::Shading(false), A::Shading(true), A::OpacitySlider, A::Command("toggle_show_occluded")]
         );
         assert_eq!(options.len(), actions.len());
         assert!(options.iter().zip(&actions).all(|(o, a)| (o == "-") == (*a == A::Separator)), "separator rows line up");
     }
 
+    /// Show Origin is a switch in the Guides group, marked from the live
+    /// flag; the row runs the command, which also keeps the viewport
+    /// menubar's Guides checkmark in step.
+    #[test]
+    fn the_viewport_menu_toggles_show_origin() {
+        use crate::app::ViewportMenuAction as A;
+        let mut state = State::new(false);
+        state.viewport_mut().show_origin = true;
+        let row = |state: &State| {
+            let (options, actions) = state.viewport_menu_rows();
+            let i = actions.iter().position(|a| *a == A::Command("toggle_origin")).expect("a Show Origin row");
+            options[i].clone()
+        };
+        let label = crate::command::by_id("toggle_origin").unwrap().label;
+        assert_eq!(row(&state), format!("● {label}"));
+        state.run_viewport_menu_action(A::Command("toggle_origin"));
+        assert!(!state.viewport().show_origin);
+        assert_eq!(row(&state), format!("○ {label}"));
+        let kdl = fs::read_to_string(crate::app::DesignSettings::file_path()).expect("saved");
+        assert!(!crate::app::DesignSettings::from_kdl_str(&kdl).viewport.show_origin_enabled, "persisted");
+    }
+
     /// Show Point Normals is a switch in the Points group after the
     /// numbers, marked from the live flag; the row runs the command, which
     /// collects the whiskers with the scene.