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

commitd08519918cc4a8c5926b343eed49289ddf57cbd6
parent81d5d66907
authorLucas Galante <[email protected]>
date2026-07-06 21:22
Complete config exact key matching, dynamic list row auto-scaling, and font database validator warnings

 src/layout.rs                          | 88 ++++++++++++++--------------------
 src/lib.rs                             | 38 +++++++++++++++
 src/widget/container/scrolling_list.rs | 20 +++++---
 3 files changed, 88 insertions(+), 58 deletions(-)

diff --git a/src/layout.rs b/src/layout.rs
index 0550346..128c944 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -218,6 +218,26 @@ fn read_config() -> Option<String> {
     None
 }
 
+pub fn read_config_value(target_key: &str) -> Option<String> {
+    let path = crate::config::get_config_path();
+    if let Ok(content) = std::fs::read_to_string(&path) {
+        let val = crate::config::parse_kdl_to_json(&content);
+        let mut flat_props = String::new();
+        flatten_json_to_flat_props(&val, "", &mut flat_props);
+        for line in flat_props.lines() {
+            let trimmed = line.trim();
+            if let Some(eq_idx) = trimmed.find('=') {
+                let key = trimmed[..eq_idx].trim();
+                if key == target_key {
+                    let val_str = trimmed[eq_idx + 1..].trim().trim_matches('"').trim();
+                    return Some(val_str.to_string());
+                }
+            }
+        }
+    }
+    None
+}
+
 pub fn parse_font_string(s: &str) -> (String, Option<f32>) {
     let s = s.trim();
     if let Some(last_space_idx) = s.rfind(' ') {
@@ -345,9 +365,11 @@ pub fn reload_config() {
         let mut graph_node_font_changed = false;
         for line in content.lines() {
             let trimmed = line.trim();
+            let mut key = String::new();
+            let mut val_str = "";
             if let Some(eq_idx) = trimmed.find('=') {
-                let key = trimmed[..eq_idx].trim().to_string();
-                let val_str = trimmed[eq_idx + 1..].trim().trim_matches('"').trim();
+                key = trimmed[..eq_idx].trim().to_string();
+                val_str = trimmed[eq_idx + 1..].trim().trim_matches('"').trim();
                 if let Ok(mut registry) = get_style_registry().write() {
                     if let Ok(f_val) = val_str.parse::<f32>() {
                         registry.set_float(&key, f_val);
@@ -957,22 +979,18 @@ pub fn reload_config() {
                     button_strip_font_changed = true;
                 }
             }
-            if let Some(rest) = trimmed.strip_prefix("list_font") {
-                if !rest.starts_with('_') {
-                    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) = LIST_FONT.write() {
-                        if *lock != font {
-                            *lock = font;
-                            changed = true;
-                        }
-                    }
-                    if changed {
-                        list_font_changed = true;
+            if key == "list_font" {
+                let font = val_str.to_string();
+                let mut changed = false;
+                if let Ok(mut lock) = LIST_FONT.write() {
+                    if *lock != font {
+                        *lock = font;
+                        changed = true;
                     }
                 }
+                if changed {
+                    list_font_changed = true;
+                }
             }
             if let Some(rest) = trimmed.strip_prefix("tree_font") {
                 let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=');
@@ -1968,17 +1986,7 @@ pub fn control_label_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("control_label_font") {
-                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=');
-                    let rest = mod_rest(rest);
-                    font = rest.trim().to_string();
-                }
-            }
-        }
+        let font = read_config_value("control_label_font").unwrap_or_else(|| "Outfit".to_string());
         if let Ok(mut lock) = CONTROL_LABEL_FONT.write() {
             *lock = font;
         }
@@ -2021,17 +2029,7 @@ pub fn control_label_font_detached() -> 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("control_label_font_detached") {
-                    let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=');
-                    let rest = mod_rest(rest);
-                    font = rest.trim().to_string();
-                }
-            }
-        }
+        let font = read_config_value("control_label_font_detached").unwrap_or_else(|| "Outfit".to_string());
         if let Ok(mut lock) = CONTROL_LABEL_FONT_DETACHED.write() {
             *lock = font;
         }
@@ -2076,19 +2074,7 @@ pub fn list_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("list_font") {
-                    if !rest.starts_with('_') {
-                        let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=');
-                        let rest = mod_rest(rest);
-                        font = rest.trim().to_string();
-                    }
-                }
-            }
-        }
+        let font = read_config_value("list_font").unwrap_or_else(|| "Outfit".to_string());
         if let Ok(mut lock) = LIST_FONT.write() {
             *lock = font;
         }
diff --git a/src/lib.rs b/src/lib.rs
index 666e0b1..e7344e9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -26,6 +26,44 @@ pub fn create_font_system() -> glyphon::FontSystem {
     if std::env::var("CCE_LOAD_SYSTEM_FONTS").is_ok() {
         db.load_system_fonts();
     }
+
+    // Validate configured custom fonts
+    let font_getters = vec![
+        ("list_font", crate::layout::list_font_parsed().0),
+        ("menubar_font", crate::layout::menubar_font_parsed().0),
+        ("statusbar_font", crate::layout::statusbar_font_parsed().0),
+        ("font_selector_font", crate::layout::font_selector_font_parsed().0),
+        ("button_strip_font", crate::layout::button_strip_font_parsed().0),
+        ("control_label_font", crate::layout::control_label_font_parsed().0),
+        ("control_label_font_detached", crate::layout::control_label_font_detached_parsed().0),
+        ("tree_font", crate::layout::tree_font_parsed().0),
+        ("graph_font", crate::layout::graph_font_parsed().0),
+        ("graph_node_font", crate::layout::graph_node_font_parsed().0),
+    ];
+
+    for (name, family) in font_getters {
+        if !family.is_empty() && family != "Outfit" && family != "sans-serif" {
+            let mut found = false;
+            for face in db.faces() {
+                for (fam, _) in &face.families {
+                    if fam.to_lowercase() == family.to_lowercase() {
+                        found = true;
+                        break;
+                    }
+                }
+                if found {
+                    break;
+                }
+            }
+            if !found {
+                eprintln!(
+                    "WARNING: Configured font family '{}' for property '{}' was not found in the fonts database. Falling back to default font.",
+                    family, name
+                );
+            }
+        }
+    }
+
     glyphon::FontSystem::new_with_locale_and_db("en-US".to_string(), db)
 }
 
diff --git a/src/widget/container/scrolling_list.rs b/src/widget/container/scrolling_list.rs
index 07ad75c..e3794ab 100644
--- a/src/widget/container/scrolling_list.rs
+++ b/src/widget/container/scrolling_list.rs
@@ -42,10 +42,12 @@ pub struct List {
 
 impl List {
     pub fn new(item_height: f32, item_gap: f32) -> Self {
+        let (_, font_size) = crate::layout::list_font_parsed();
+        let adjusted_item_height = item_height.max(font_size + 14.0);
         Self {
             base: Widget::new(),
             scroll_box: ScrollBox::new(),
-            item_height,
+            item_height: adjusted_item_height,
             item_gap,
             columns: None,
             rows: Vec::new(),
@@ -451,8 +453,12 @@ impl Element for List {
                 let row_fg = if row.selected { fg } else { [178, 178, 191] };
                 let row_dim = if row.selected { fg } else { text_dim };
 
-                let y_text_12 = crate::layout::center_text_y(draw_y, self.item_height, 12.0);
-                let y_text_11 = crate::layout::center_text_y(draw_y, self.item_height, 11.0);
+                let (_, config_size) = crate::layout::list_font_parsed();
+                let primary_size = config_size;
+                let secondary_size = (config_size - 1.0).max(8.0);
+
+                let y_primary = crate::layout::center_text_y(draw_y, self.item_height, primary_size);
+                let y_secondary = crate::layout::center_text_y(draw_y, self.item_height, secondary_size);
 
                 let mut start_text_offset = 8.0;
                 if let Some(ref icon) = row.icon {
@@ -461,8 +467,8 @@ impl Element for List {
                             TextLabel {
                                 text: icon.clone(),
                                 x: x + col_bounds[0].0 + 12.0,
-                                y: y_text_12,
-                                font_size: 12.0,
+                                y: y_primary,
+                                font_size: primary_size,
                                 color: row_fg,
                             },
                             list_font.clone(),
@@ -482,8 +488,8 @@ impl Element for List {
                     }
 
                     let cell_color = if c_idx == 0 { row_fg } else { row_dim };
-                    let cell_y = if c_idx == 0 { y_text_12 } else { y_text_11 };
-                    let cell_size = if c_idx == 0 { 12.0 } else { 11.0 };
+                    let cell_y = if c_idx == 0 { y_primary } else { y_secondary };
+                    let cell_size = if c_idx == 0 { primary_size } else { secondary_size };
 
                     let cell_draw_x = if c_idx == 0 {
                         x + col_x + start_text_offset