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

commit0f4e08c4fa5ad05baf29cdbac50dc6a05f651344
parent686c576520
authorLucas Galante <[email protected]>
date2026-07-03 16:08
feat: support custom fonts for Graph and Node elements in cce-ui

 src/layout.rs               | 154 ++++++++++++++++++++++++++++++++++++++++++++
 src/widget/display/graph.rs |   4 ++
 2 files changed, 158 insertions(+)

diff --git a/src/layout.rs b/src/layout.rs
index b4b2804..d389f79 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -144,7 +144,9 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
                 "style.surface.graph.spacing_y" => "graph_spacing_y",
                 "style.surface.graph.grid_snap" => "graph_grid_snap",
                 "style.surface.graph.blur" => "graph_blur",
+                "style.surface.graph.font" => "graph_font",
                 "style.surface.graph.node.color" => "graph_node_color",
+                "style.surface.graph.node.font" => "graph_node_font",
                 "style.surface.graph.node.selected_color" => "graph_node_selected_color",
                 "style.surface.graph.node.drag_color" => "graph_node_drag_color",
                 "style.surface.graph.node.corner_radius" => "graph_node_corner_radius",
@@ -272,6 +274,10 @@ static LIST_FONT: RwLock<String> = RwLock::new(String::new());
 static LIST_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
 static TREE_FONT: RwLock<String> = RwLock::new(String::new());
 static TREE_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
+static GRAPH_FONT: RwLock<String> = RwLock::new(String::new());
+static GRAPH_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
+static GRAPH_NODE_FONT: RwLock<String> = RwLock::new(String::new());
+static GRAPH_NODE_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
 static LIST_JUSTIFICATION: RwLock<u8> = RwLock::new(0);
 static PLATE_OPACITY: RwLock<f32> = RwLock::new(1.0);
 static PAGE_OPACITY: RwLock<f32> = RwLock::new(1.0);
@@ -315,6 +321,8 @@ pub fn reload_config() {
         let mut slider_font_changed = false;
         let mut list_font_changed = false;
         let mut tree_font_changed = false;
+        let mut graph_font_changed = false;
+        let mut graph_node_font_changed = false;
         for line in content.lines() {
             let trimmed = line.trim();
             if let Some(eq_idx) = trimmed.find('=') {
@@ -938,6 +946,36 @@ pub fn reload_config() {
                     tree_font_changed = true;
                 }
             }
+            if let Some(rest) = trimmed.strip_prefix("graph_font") {
+                let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=');
+                let rest = mod_rest(rest);
+                let font = rest.trim().to_string();
+                let mut changed = false;
+                if let Ok(mut lock) = GRAPH_FONT.write() {
+                    if *lock != font {
+                        *lock = font;
+                        changed = true;
+                    }
+                }
+                if changed {
+                    graph_font_changed = true;
+                }
+            }
+            if let Some(rest) = trimmed.strip_prefix("graph_node_font") {
+                let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=');
+                let rest = mod_rest(rest);
+                let font = rest.trim().to_string();
+                let mut changed = false;
+                if let Ok(mut lock) = GRAPH_NODE_FONT.write() {
+                    if *lock != font {
+                        *lock = font;
+                        changed = true;
+                    }
+                }
+                if changed {
+                    graph_node_font_changed = true;
+                }
+            }
             if let Some(rest) = trimmed.strip_prefix("list_justification") {
                 let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
                 let val_str = rest.trim_end_matches('"').trim();
@@ -1008,6 +1046,16 @@ pub fn reload_config() {
                 *lock = None;
             }
         }
+        if graph_font_changed {
+            if let Ok(mut lock) = GRAPH_FONT_CACHED.write() {
+                *lock = None;
+            }
+        }
+        if graph_node_font_changed {
+            if let Ok(mut lock) = GRAPH_NODE_FONT_CACHED.write() {
+                *lock = None;
+            }
+        }
         crate::color::reload_colors(&content);
     }
 }
@@ -2037,6 +2085,112 @@ pub fn set_tree_font(font: &str) {
     }
 }
 
+// Graph Font
+pub fn graph_font() -> String {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        let mut font = "Outfit".to_string();
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("graph_font") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=');
+                    let rest = mod_rest(rest);
+                    font = rest.trim().to_string();
+                }
+            }
+        }
+        if let Ok(mut lock) = GRAPH_FONT.write() {
+            *lock = font;
+        }
+    });
+    let lock = GRAPH_FONT.read().unwrap();
+    if lock.is_empty() {
+        "Outfit".to_string()
+    } else {
+        lock.clone()
+    }
+}
+
+pub fn graph_font_parsed() -> (String, f32) {
+    if let Ok(lock) = GRAPH_FONT_CACHED.read() {
+        if let Some(ref val) = *lock {
+            return val.clone();
+        }
+    }
+    let font_str = graph_font();
+    let parsed = parse_font_string(&font_str);
+    let size = parsed.1.unwrap_or(12.0);
+    let val = (parsed.0, size);
+    if let Ok(mut lock) = GRAPH_FONT_CACHED.write() {
+        *lock = Some(val.clone());
+    }
+    val
+}
+
+pub fn set_graph_font(font: &str) {
+    if let Ok(mut lock) = GRAPH_FONT.write() {
+        *lock = font.to_string();
+    }
+    if let Ok(mut lock) = GRAPH_FONT_CACHED.write() {
+        *lock = None;
+    }
+}
+
+// Graph Node Font
+pub fn graph_node_font() -> String {
+    use std::sync::Once;
+    static INIT: Once = Once::new();
+    INIT.call_once(|| {
+        let mut font = "Outfit".to_string();
+        if let Some(content) = read_config() {
+            for line in content.lines() {
+                let trimmed = line.trim();
+                if let Some(rest) = trimmed.strip_prefix("graph_node_font") {
+                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=');
+                    let rest = mod_rest(rest);
+                    font = rest.trim().to_string();
+                }
+            }
+        }
+        if let Ok(mut lock) = GRAPH_NODE_FONT.write() {
+            *lock = font;
+        }
+    });
+    let lock = GRAPH_NODE_FONT.read().unwrap();
+    if lock.is_empty() {
+        "Outfit".to_string()
+    } else {
+        lock.clone()
+    }
+}
+
+pub fn graph_node_font_parsed() -> (String, f32) {
+    if let Ok(lock) = GRAPH_NODE_FONT_CACHED.read() {
+        if let Some(ref val) = *lock {
+            return val.clone();
+        }
+    }
+    let font_str = graph_node_font();
+    let parsed = parse_font_string(&font_str);
+    let size = parsed.1.unwrap_or(12.0);
+    let val = (parsed.0, size);
+    if let Ok(mut lock) = GRAPH_NODE_FONT_CACHED.write() {
+        *lock = Some(val.clone());
+    }
+    val
+}
+
+pub fn set_graph_node_font(font: &str) {
+    if let Ok(mut lock) = GRAPH_NODE_FONT.write() {
+        *lock = font.to_string();
+    }
+    if let Ok(mut lock) = GRAPH_NODE_FONT_CACHED.write() {
+        *lock = None;
+    }
+}
+
 // List Justification
 pub fn list_justification() -> u8 {
     use std::sync::Once;
diff --git a/src/widget/display/graph.rs b/src/widget/display/graph.rs
index 618a1b8..72cdcd3 100644
--- a/src/widget/display/graph.rs
+++ b/src/widget/display/graph.rs
@@ -256,6 +256,10 @@ impl Element for Graph {
 
     fn rounded_corners(&self) -> (bool, bool, bool, bool) { (false, false, true, true) }
 
+    fn widget_font(&self) -> Option<String> {
+        Some(crate::layout::graph_node_font())
+    }
+
 
 
     fn paint(&mut self, ctx: &mut UiContext) {