git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commitd3d576a90a550f3be4971a48d8e0bfc52e6d1af2
parent9bd53563d8
authorLucas Galante <[email protected]>
date2026-07-21 12:22
feat: node-domain opacity for the graph (style.surface.graph.node.opacity)

Graph node bodies, wires, connectors, and toggles fade via a new
node_opacity, independent of network_opacity which fades only the pane
surface (grid cells/gaps).

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/color.rs                | 18 ++++++++++++++++++
 src/widget/display/graph.rs | 19 +++++++++++++------
 2 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/src/color.rs b/src/color.rs
index 102631a..0b4a32d 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -128,6 +128,10 @@ static SCROLLBAR_THUMB_COLOR: RwLock<[f32; 4]> = RwLock::new([0.60, 0.60, 0.65,
 static GRAPH_CELL_COLOR: RwLock<[f32; 3]> = RwLock::new([0.13, 0.13, 0.16]);
 static GRAPH_GAP_COLOR: RwLock<[f32; 3]> = RwLock::new([0.07, 0.07, 0.09]);
 static GRAPH_OPACITY: RwLock<f32> = RwLock::new(0.95);
+/// Opacity of the graph's NODE-domain content (node bodies, wires, connectors,
+/// node text) — `style.surface.graph.node.opacity`, deliberately independent of
+/// `GRAPH_OPACITY`, which fades only the pane surface (grid cells/gaps).
+static GRAPH_NODE_OPACITY: RwLock<f32> = RwLock::new(1.0);
 
 static GRAPH_NODE_COLOR: RwLock<[f32; 4]> = RwLock::new(NODE_IDLE);
 static GRAPH_NODE_SELECTED_COLOR: RwLock<[f32; 4]> = RwLock::new(NODE_SELECTED);
@@ -553,6 +557,9 @@ fn parse_and_set_colors(content: &str) {
     if let Some(opacity) = val.pointer("/style/surface/graph/opacity").and_then(|v| v.as_f64()) {
         if let Ok(mut lock) = GRAPH_OPACITY.write() { *lock = opacity as f32; }
     }
+    if let Some(opacity) = val.pointer("/style/surface/graph/node/opacity").and_then(|v| v.as_f64()) {
+        if let Ok(mut lock) = GRAPH_NODE_OPACITY.write() { *lock = opacity as f32; }
+    }
 
     if let Some(c) = get_color("/style/data/tree/background_color") {
         if let Ok(mut lock) = TREE_BACKGROUND_COLOR.write() { *lock = c; }
@@ -795,6 +802,17 @@ pub fn graph_opacity() -> f32 {
     *GRAPH_OPACITY.read().unwrap()
 }
 
+pub fn graph_node_opacity() -> f32 {
+    load_colors_once();
+    *GRAPH_NODE_OPACITY.read().unwrap()
+}
+
+pub fn set_graph_node_opacity(opacity: f32) {
+    if let Ok(mut lock) = GRAPH_NODE_OPACITY.write() {
+        *lock = opacity;
+    }
+}
+
 pub fn set_graph_opacity(opacity: f32) {
     if let Ok(mut lock) = GRAPH_OPACITY.write() {
         *lock = opacity;
diff --git a/src/widget/display/graph.rs b/src/widget/display/graph.rs
index 07ec0b6..9931001 100644
--- a/src/widget/display/graph.rs
+++ b/src/widget/display/graph.rs
@@ -76,6 +76,9 @@ pub struct Graph {
 
     uniform_background: bool,
     network_opacity: f32,
+    /// Node-domain opacity (bodies, wires, connectors) — independent of
+    /// `network_opacity`, which fades the pane surface (grid cells/gaps).
+    node_opacity: f32,
     cell_color: [f32; 3],
     gap_color: [f32; 3],
 
@@ -120,6 +123,7 @@ impl Graph {
             toggle_hovered_idx: None,
             uniform_background: false,
             network_opacity: crate::color::graph_opacity(),
+            node_opacity: crate::color::graph_node_opacity(),
             cell_color: cell_col,
             gap_color: gap_col,
             connecting_from: None,
@@ -135,6 +139,9 @@ impl Graph {
     pub fn set_network_opacity(&mut self, opacity: f32) {
         self.network_opacity = opacity;
     }
+    pub fn set_node_opacity(&mut self, opacity: f32) {
+        self.node_opacity = opacity;
+    }
     pub fn grid_sizes(&self) -> (f32, f32) {
         (self.grid_size_x, self.grid_size_y)
     }
@@ -295,7 +302,7 @@ impl Graph {
 
         // Connection wires: each node with an "input" parameter draws a wire from that source
         // node's first output port to its own first input port.
-        let wire_color = [0.0, 0.75, 1.0, 0.7 * self.network_opacity]; // Vibrant cyan glow
+        let wire_color = [0.0, 0.75, 1.0, 0.7 * self.node_opacity]; // Vibrant cyan glow
         for i in 0..self.nodes.len() {
             let node = &self.nodes[i];
             if let Some((_, input_name, _)) = node.parameters.iter().find(|(name, _, _)| name.eq_ignore_ascii_case("input")) {
@@ -420,7 +427,7 @@ impl Graph {
                 } else {
                     colors::node_color()
                 };
-                bg_color[3] *= self.network_opacity;
+                bg_color[3] *= self.node_opacity;
                 if nx + nw > min_x && nx < max_x && ny + nh > min_y && ny < max_y {
                     quads.push((nx, ny, nw, nh, bg_color));
                 }
@@ -431,13 +438,13 @@ impl Graph {
                     } else {
                         colors::TOGGLE_OFF
                     };
-                    btn_color[3] *= self.network_opacity;
+                    btn_color[3] *= self.node_opacity;
                     push_clipped(tx, ty, tw, th, btn_color, &mut quads);
 
                     if self.nodes[i].geom_visible {
                         let inset = 3.0 * scale_f;
                         let mut toggle_on_color = colors::TOGGLE_ON;
-                        toggle_on_color[3] *= self.network_opacity;
+                        toggle_on_color[3] *= self.node_opacity;
                         push_clipped(tx + inset, ty + inset, tw - inset * 2.0, th - inset * 2.0, toggle_on_color, &mut quads);
                     }
                 }
@@ -493,8 +500,8 @@ impl Graph {
         let conn_size = crate::layout::graph_connector_size();
         let mut conn_color = colors::graph_connector_color();
         let mut conn_hl_color = colors::graph_connector_highlight_color();
-        conn_color[3] *= self.network_opacity;
-        conn_hl_color[3] *= self.network_opacity;
+        conn_color[3] *= self.node_opacity;
+        conn_hl_color[3] *= self.node_opacity;
 
         for i in 0..self.nodes.len() {
             if let Some((nx, ny, nw, nh)) = self.node_rect(i) {