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

commit061438963fc4840cbb3921880cd08028c4ccf693
parentbefb275c37
authorLucas Galante <lsgalante12@gmail.com>
date2026-09-29 17:40
refactor: a graph's cells have no colour, and its lines are grid_color

The graph filled itself with style.surface.graph.cell_color when a host
set uniform_background, and drew its lattice in gap_color, the cell
model's name for it. The fill is gone: the cells are whatever the graph
is painted on. The lines are graph.grid_color. cell_color and gap_color
are retired, reported by path at load and not read.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

 src/color.rs                | 46 +++++++++++++++++++++++----------------------
 src/layout.rs               |  8 ++------
 src/widget/display/graph.rs | 37 +++++++++++++++---------------------
 3 files changed, 41 insertions(+), 50 deletions(-)

diff --git a/src/color.rs b/src/color.rs
index 5f4109d..9feb8cf 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -182,8 +182,7 @@ static TREE_SEPARATOR_COLOR: RwLock<[f32; 4]> = RwLock::new([0.15, 0.15, 0.19, 1
 static SCROLLBAR_TRACK_COLOR: RwLock<[f32; 4]> = RwLock::new([0.15, 0.15, 0.20, 0.3]);
 static SCROLLBAR_THUMB_COLOR: RwLock<[f32; 4]> = RwLock::new([0.60, 0.60, 0.65, 0.4]);
 
-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_GRID_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
@@ -550,11 +549,11 @@ fn parse_and_set_colors(content: &str) {
         }
     }
 
-    if let Some(c) = get_color("/style/surface/graph/cell_color") {
-        if let Ok(mut lock) = GRAPH_CELL_COLOR.write() { *lock = [c[0], c[1], c[2]]; }
-    }
-    if let Some(c) = get_color("/style/surface/graph/gap_color") {
-        if let Ok(mut lock) = GRAPH_GAP_COLOR.write() { *lock = [c[0], c[1], c[2]]; }
+    // The lattice's lines. The cells between them have no colour of their
+    // own: a graph shows whatever it is painted on. `cell_color` and
+    // `gap_color`, the cell model's pair, are retired (`retired_surface_keys`).
+    if let Some(c) = get_color("/style/surface/graph/grid_color") {
+        if let Ok(mut lock) = GRAPH_GRID_COLOR.write() { *lock = [c[0], c[1], c[2]]; }
     }
     if let Some(c) = get_color("/style/surface/graph/node/color") {
         if let Ok(mut lock) = GRAPH_NODE_COLOR.write() { *lock = c; }
@@ -683,7 +682,7 @@ fn parse_and_set_colors(content: &str) {
     let retired = retired_surface_keys(&val);
     if !retired.is_empty() {
         log::warn!(
-            "retired style.surface keys in config: {} — the frost is `plate {{ frost radius= compression= refraction= }}` (a material's `frost` child spells `compression`), every roll's width is `relief width=`, the light strength is `relief light=` (a material's `finish light=`), and the relief's geometry is `relief {{ wall height= profile= ; edge height= profile= }}` (the window_manager bevel_* spellings are the same relief keys, and its bevel_shader is `relief shader=`)",
+            "retired style.surface keys in config: {} — the frost is `plate {{ frost radius= compression= refraction= }}` (a material's `frost` child spells `compression`), every roll's width is `relief width=`, the light strength is `relief light=` (a material's `finish light=`), and the relief's geometry is `relief {{ wall height= profile= ; edge height= profile= }}` (the window_manager bevel_* spellings are the same relief keys, and its bevel_shader is `relief shader=`); a graph's lines are `graph {{ grid_color }}` and its cells take no colour",
             retired.join(", ")
         );
     }
@@ -781,7 +780,9 @@ fn parse_and_set_colors(content: &str) {
 /// / `bevel_width` / `bevel_shader`, the block the relief keys were born in
 /// before `style.surface.relief` existed (the compositor never read them;
 /// the shader toggle is `relief.shader` now). Each was an alias for part
-/// of 2026-09-28 and is not read now. Empty for a clean config.
+/// of 2026-09-28 and is not read now. And the graph's `cell_color` and
+/// `gap_color` (2026-09-29): a graph's cells have no colour, and its lines
+/// are `graph.grid_color`. Empty for a clean config.
 pub fn retired_surface_keys(val: &serde_json::Value) -> Vec<String> {
     let mut found = Vec::new();
     for k in ["blur", "radius", "backdrop_compression", "refraction", "bevel_width"] {
@@ -812,6 +813,11 @@ pub fn retired_surface_keys(val: &serde_json::Value) -> Vec<String> {
             }
         }
     }
+    for k in ["cell_color", "gap_color"] {
+        if val.pointer(&format!("/style/surface/graph/{k}")).is_some() {
+            found.push(format!("style.surface.graph.{k}"));
+        }
+    }
     found
 }
 
@@ -941,22 +947,14 @@ pub fn set_graph_node_drag_color(color: [f32; 4]) {
     style_write(&GRAPH_NODE_DRAG_COLOR, color);
 }
 
-pub fn graph_cell_color() -> [f32; 3] {
-    load_colors_once();
-    style_read(&GRAPH_CELL_COLOR)
-}
-
-pub fn set_graph_cell_color(color: [f32; 3]) {
-    style_write(&GRAPH_CELL_COLOR, color);
-}
-
-pub fn graph_gap_color() -> [f32; 3] {
+/// The colour of a graph's grid lines (`style.surface.graph.grid_color`).
+pub fn graph_grid_color() -> [f32; 3] {
     load_colors_once();
-    style_read(&GRAPH_GAP_COLOR)
+    style_read(&GRAPH_GRID_COLOR)
 }
 
-pub fn set_graph_gap_color(color: [f32; 3]) {
-    style_write(&GRAPH_GAP_COLOR, color);
+pub fn set_graph_grid_color(color: [f32; 3]) {
+    style_write(&GRAPH_GRID_COLOR, color);
 }
 
 pub fn graph_opacity() -> f32 {
@@ -2275,6 +2273,8 @@ mod tests {
             "relief": { "depth": 0.15, "height": 1.0, "profile": "a", "edge_height": 2.0, "edge_profile": "b" },
             "material": { "glass": { "frost": { "backdrop_compression": 0.6 }, "finish": { "depth": 0.2 } } }
         } }, "window_manager": { "bevel_depth": 0.15, "bevel_width": 9.3, "bevel_shader": 0 } });
+        let mut old = old;
+        old["style"]["surface"]["graph"] = serde_json::json!({ "cell_color": "#545467", "gap_color": "#48485b", "grid_color": "#48485b" });
         assert_eq!(retired_surface_keys(&old), vec![
             "style.surface.plate.blur",
             "style.surface.plate.radius",
@@ -2292,6 +2292,8 @@ mod tests {
             "window_manager.bevel_shader",
             "style.surface.material.glass.frost.backdrop_compression",
             "style.surface.material.glass.finish.depth",
+            "style.surface.graph.cell_color",
+            "style.surface.graph.gap_color",
         ]);
         assert!(retired_surface_keys(&serde_json::json!({})).is_empty());
     }
diff --git a/src/layout.rs b/src/layout.rs
index 952fc29..0ccbaf3 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -348,8 +348,7 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
                 "style.surface.statusbar.font" => "statusbar_font",
                 "style.surface.page.opacity" => "page_opacity",
                 "style.surface.page.margin" => "page_margin",
-                "style.surface.graph.cell_color" => "graph_cell_color",
-                "style.surface.graph.gap_color" => "graph_gap_color",
+                "style.surface.graph.grid_color" => "graph_grid_color",
                 "style.surface.graph.opacity" => "graph_opacity",
                 "style.surface.graph.node.opacity" => "graph_node_opacity",
                 "style.surface.graph.spacing_x" => "graph_spacing_x",
@@ -7108,10 +7107,7 @@ mod tests {
         // module's reach; what is checkable without writing them is that each
         // resolves to a real, in-gamut colour rather than an unparsed or
         // uninitialised one.
-        let rgb: [(&str, [f32; 3]); 2] = [
-            ("graph_cell", crate::color::graph_cell_color()),
-            ("graph_gap", crate::color::graph_gap_color()),
-        ];
+        let rgb: [(&str, [f32; 3]); 1] = [("graph_grid", crate::color::graph_grid_color())];
         for (name, c) in rgb {
             assert!(c.iter().all(|v| v.is_finite() && (0.0..=1.0).contains(v)), "{name}: {c:?}");
         }
diff --git a/src/widget/display/graph.rs b/src/widget/display/graph.rs
index 8213e80..c1b901f 100644
--- a/src/widget/display/graph.rs
+++ b/src/widget/display/graph.rs
@@ -106,8 +106,7 @@ pub struct Graph {
     /// 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],
+    grid_color: [f32; 3],
 
     // Connection state
     connecting_from: Option<(usize, PortType, usize)>,
@@ -138,8 +137,7 @@ impl Graph {
         let node_h = crate::layout::graph_node_height();
         let grid_snap_enabled = crate::layout::graph_grid_snap();
 
-        let cell_col = crate::color::graph_cell_color();
-        let gap_col = crate::color::graph_gap_color();
+        let grid_col = crate::color::graph_grid_color();
 
         Adapted::new(Graph {
             show_network_grid: false,
@@ -166,8 +164,7 @@ impl Graph {
             uniform_background: false,
             network_opacity: crate::color::graph_opacity(),
             node_opacity: crate::color::graph_node_opacity(),
-            cell_color: cell_col,
-            gap_color: gap_col,
+            grid_color: grid_col,
             connecting_from: None,
             current_mouse_pos: (0.0, 0.0),
             pending_connection: None,
@@ -215,11 +212,8 @@ impl Graph {
     pub fn grid_snap_enabled(&self) -> bool {
         self.grid_snap_enabled
     }
-    pub fn set_cell_color(&mut self, color: [f32; 3]) {
-        self.cell_color = color;
-    }
-    pub fn set_gap_color(&mut self, color: [f32; 3]) {
-        self.gap_color = color;
+    pub fn set_grid_color(&mut self, color: [f32; 3]) {
+        self.grid_color = color;
     }
 
     /// The top-left corner of a node body centred on lattice cell (col, row).
@@ -378,17 +372,16 @@ impl Graph {
         }
     }
 
-    /// The graph's plate, as the colour-typed host paints it: the cell
-    /// colour (or a near-transparent black) at the network opacity, and
-    /// under `graph_blur` a frosted material whose tint alpha IS the blur
-    /// value — the knob doubles as the frost's opacity.
+    /// The graph's plate, as the colour-typed host paints it: a
+    /// near-transparent black, so the cells between the grid lines are
+    /// whatever the graph is painted on, and under `graph_blur` a frosted
+    /// material whose tint alpha IS the blur value — the knob doubles as
+    /// the frost's opacity. Until 2026-09-29 a `uniform_background` graph
+    /// filled itself with a cell colour of its own (`graph.cell_color`);
+    /// the flag is kept for the hosts that set it and chooses nothing here.
     fn bg_color(&self) -> [f32; 4] {
         use crate::scene::{Frost, Material, PlateRole};
-        let c = if self.uniform_background {
-            [self.cell_color[0], self.cell_color[1], self.cell_color[2], self.network_opacity]
-        } else {
-            [0.0, 0.0, 0.0, 0.01 * self.network_opacity]
-        };
+        let c = [0.0, 0.0, 0.0, 0.01 * self.network_opacity];
         let blur_val = crate::layout::graph_blur();
         let m = if blur_val > 0.0 {
             Material::opaque([c[0], c[1], c[2], blur_val.abs() * self.network_opacity]).with_frost(Frost::from_style())
@@ -416,7 +409,7 @@ impl Graph {
     }
 
     /// The grid lines, flat, over whatever the graph is painted on — the
-    /// pane plate. A lattice of lines one pitch apart in the gap colour at
+    /// pane plate. A lattice of lines one pitch apart in the grid colour at
     /// the network opacity, each centred on its coordinate (the pitch is
     /// measured centre to centre, and `graph_line_width` only thickens
     /// them), so the intersections are exactly where the node centres go.
@@ -445,7 +438,7 @@ impl Graph {
 
         let line = crate::layout::graph_line_width().max(0.0);
         if self.show_network_grid && line > 0.0 && self.pitch_x >= 4.0 && self.pitch_y >= 4.0 {
-            let color = [self.gap_color[0], self.gap_color[1], self.gap_color[2], self.network_opacity];
+            let color = [self.grid_color[0], self.grid_color[1], self.grid_color[2], self.network_opacity];
             // The line indices that can cross the rect, one past each edge so
             // a line's own width never pops at the boundary.
             let c0 = ((min_x - self.grid_origin_x) / self.pitch_x).floor() as i32 - 1;