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

commit9b6814d6abd1ca1080993bc99e4cc67246577432
parentffd4464e78
authorLucas Galante <[email protected]>
date2026-07-03 02:02
Added style.data.textbox.placeholder_text_color configuration property key

 src/color.rs                 | 34 ++++++++++++++++++++++++++++++++++
 src/config.rs                |  6 ++++++
 src/layout.rs                |  1 +
 src/widget/input/text_box.rs |  2 +-
 4 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/src/color.rs b/src/color.rs
index 05b2302..85c70f7 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -50,6 +50,8 @@ static BACKPLATE_CORNER_RADIUS: RwLock<f32> = RwLock::new(12.0);
 
 static DROPDOWN_BACKGROUND_COLOR: RwLock<[f32; 4]> = RwLock::new([0.08, 0.08, 0.12, 1.0]);
 
+static TEXTBOX_PLACEHOLDER_TEXT_COLOR: RwLock<[u8; 3]> = RwLock::new([0x60, 0x60, 0x6a]);
+
 static BACKPLATE_MENUBAR_COLOR: RwLock<[f32; 4]> = RwLock::new([0.08, 0.08, 0.12, 1.0]);
 static BACKPLATE_MENUBAR_TEXT_COLOR: RwLock<[f32; 4]> = RwLock::new([0.90196, 0.90196, 0.94902, 1.0]);
 static BACKPLATE_MENUBAR_BLUR: RwLock<bool> = RwLock::new(false);
@@ -213,6 +215,23 @@ fn parse_and_set_colors(content: &str) {
         val.pointer(pointer).and_then(|v| v.as_str()).and_then(parse_hex)
     };
 
+    let get_color_u8 = |pointer: &str| -> Option<[u8; 3]> {
+        val.pointer(pointer).and_then(|v| v.as_str()).and_then(|hex_str| {
+            let hex = hex_str.trim_matches(|c| c == '"' || c == '\'' || c == ' ');
+            let hex = hex.trim_start_matches('#');
+            if hex.len() >= 6 {
+                if let (Ok(r), Ok(g), Ok(b)) = (
+                    u8::from_str_radix(&hex[0..2], 16),
+                    u8::from_str_radix(&hex[2..4], 16),
+                    u8::from_str_radix(&hex[4..6], 16),
+                ) {
+                    return Some([r, g, b]);
+                }
+            }
+            None
+        })
+    };
+
     if let Some(opacity) = val.pointer("/layout/menubar_opacity").and_then(|v| v.as_f64()) {
         if let Ok(mut lock) = OPACITY.write() {
             *lock = Some(opacity as f32);
@@ -271,6 +290,10 @@ fn parse_and_set_colors(content: &str) {
     if let Some(c) = get_color("/style/control/dropdown/color") {
         if let Ok(mut lock) = DROPDOWN_BACKGROUND_COLOR.write() { *lock = c; }
     }
+    if let Some(c) = get_color_u8("/style/textbox/placeholder_text_color")
+        .or_else(|| get_color_u8("/style/data/textbox/placeholder_text_color")) {
+        if let Ok(mut lock) = TEXTBOX_PLACEHOLDER_TEXT_COLOR.write() { *lock = c; }
+    }
     if let Some(c) = get_color("/layout/menubar_tab_label_color").or_else(|| get_color("/layout/paginator_tab_label_color")) {
         if let Ok(mut lock) = MENUBAR_TAB_LABEL_COLOR.write() { *lock = c; }
     }
@@ -446,6 +469,17 @@ pub fn set_dropdown_background_color(color: [f32; 4]) {
     }
 }
 
+pub fn textbox_placeholder_text_color() -> [u8; 3] {
+    load_colors_once();
+    *TEXTBOX_PLACEHOLDER_TEXT_COLOR.read().unwrap()
+}
+
+pub fn set_textbox_placeholder_text_color(color: [u8; 3]) {
+    if let Ok(mut lock) = TEXTBOX_PLACEHOLDER_TEXT_COLOR.write() {
+        *lock = color;
+    }
+}
+
 pub fn graph_wire_color() -> [f32; 4] {
     load_colors_once();
     *GRAPH_WIRE_COLOR.read().unwrap()
diff --git a/src/config.rs b/src/config.rs
index a2170e7..f7273d6 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -601,6 +601,9 @@ mod tests {
                 control {
                     dropdown color=(rgba)"#08080cff"
                 }
+                data {
+                    textbox placeholder_text_color=(rgba)"#60606aff"
+                }
             }
         "##;
         
@@ -612,6 +615,9 @@ mod tests {
         
         let dd_color = crate::color::dropdown_background_color();
         assert!(dd_color[0] > 0.0);
+        
+        let placeholder_color = crate::color::textbox_placeholder_text_color();
+        assert_eq!(placeholder_color, [0x60, 0x60, 0x6a]);
         assert_eq!(crate::color::backplate_statusbar_blur(), false);
 
         // Colors are in sRGB converted to linear, let's verify text colors
diff --git a/src/layout.rs b/src/layout.rs
index f4e67d5..86f3060 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -92,6 +92,7 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
                 "style.control.spinbox.button_padding" => "spinbox_button_padding",
                 "style.control.spinbox.corner_radius" => "spinbox_corner_radius",
                 "style.textbox.height" | "style.data.textbox.height" => "textbox_height",
+                "style.textbox.placeholder_text_color" | "style.data.textbox.placeholder_text_color" => "textbox_placeholder_text_color",
                 "style.textbox.multiline.line_wrap" | "style.data.textbox.multiline.line_wrap" => "textbox_line_wrap",
                 "style.textbox.multiline.border_width" | "style.data.textbox.multiline.border_width" => "textbox_multiline_border_width",
                 "style.control.toggle.font" => "toggle_font",
diff --git a/src/widget/input/text_box.rs b/src/widget/input/text_box.rs
index f911e64..14691b2 100644
--- a/src/widget/input/text_box.rs
+++ b/src/widget/input/text_box.rs
@@ -1317,7 +1317,7 @@ impl Element for TextBox {
         };
 
         let label_color = if is_placeholder {
-            [0x60, 0x60, 0x6a]
+            crate::colors::textbox_placeholder_text_color()
         } else if let Some(custom_color) = self.text_color {
             custom_color
         } else if self.disabled {