GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
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