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

commite3d6d47f2c74ac7191bc0ffa8de76112bd5ae71a
parent02e8613b12
authorLucas Galante <[email protected]>
date2026-08-23 13:42
refactor: one is_enterable predicate for Enter/double-click/`i`

The "can this node be dived into" test was hand-copied at three sites (the
node context menu, the double-click consumer, the network pane's `i` key) and
none of them knew about simnet — an emptied simnet stopped being enterable.
FsNode::is_enterable is now the single gate: subnet-like types by name (node,
utility, simnet), otherwise anything with children.

Double-click-to-enter itself was fixed on the cce-ui side (graph@6a09d5c):
the Graph widget's detection state was wiped by the designer's per-event
set_nodes re-sync, so no double-click could ever complete. Verified live —
double-clicking View and a Simnet node both enter (breadcrumb "/ Sim 1").

 src/app.rs | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index 8cdcbe4..a90d79a 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -140,6 +140,17 @@ pub struct FsNode {
     pub outputs: usize,
 }
 
+impl FsNode {
+    /// Can this node be dived into (Enter, double-click, the network's `i`)?
+    /// One predicate, because it was hand-copied at three call sites and none
+    /// of them learned about new container types: subnet-like types by name,
+    /// otherwise anything that actually has children.
+    pub fn is_enterable(&self) -> bool {
+        matches!(self.node_type.as_str(), "node" | "utility" | "simnet")
+            || !self.children.is_empty()
+    }
+}
+
 fn default_node_type() -> String { "node".to_string() }
 fn default_node_geometry_visible() -> bool { true }
 fn default_node_position() -> (f32, f32) { (0.0, 0.0) }
@@ -1893,9 +1904,7 @@ impl State {
         let (is_utility, geom_visible, enterable) = {
             let dir = self.current_dir();
             let Some(node) = dir.children.get(slot) else { return };
-            let enterable = node.node_type == "node"
-                || node.node_type == "utility"
-                || !node.children.is_empty();
+            let enterable = node.is_enterable();
             (node.node_type == "utility", node.geometry_visible, enterable)
         };
         let mut options: Vec<String> = Vec::new();
@@ -4520,7 +4529,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                                 if let Some(dir_idx) = self.graph().double_clicked_node() {
                                     self.graph_mut().clear_double_clicked_node();
                                     let dir = self.current_dir();
-                                    if dir_idx < dir.children.len() && (dir.children[dir_idx].node_type == "node" || dir.children[dir_idx].node_type == "utility" || !dir.children[dir_idx].children.is_empty()) {
+                                    if dir_idx < dir.children.len() && dir.children[dir_idx].is_enterable() {
                                         self.current_path.push(dir_idx);
                                         self.on_path_changed();
                                         changed = true;
@@ -4786,7 +4795,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                                             if self.focused_pane == LEFT_MENUBAR_IDX {
                                                 if let Some(slot_idx) = self.graph().selected_node() {
                                                     let dir = self.current_dir();
-                                                     if slot_idx < dir.children.len() && (dir.children[slot_idx].node_type == "node" || dir.children[slot_idx].node_type == "utility" || !dir.children[slot_idx].children.is_empty()) {
+                                                     if slot_idx < dir.children.len() && dir.children[slot_idx].is_enterable() {
                                                          self.current_path.push(slot_idx);
                                                          self.on_path_changed();
                                                          changed = true;