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

commitb296286abec33b6b2478e3d59b0c003ec8e575aa
parentbd24039a9e
authorLucas Galante <[email protected]>
date2026-07-04 15:36
feat: support textbox background and background_edit color configuration keys

 src/color.rs                 | 32 ++++++++++++++++++++++++++++++++
 src/layout.rs                |  2 ++
 src/widget/editor.rs         | 23 ++++++++++++++++++++---
 src/widget/input/text_box.rs | 16 ++++++++++++----
 4 files changed, 66 insertions(+), 7 deletions(-)

diff --git a/src/color.rs b/src/color.rs
index 9610471..a20f817 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -52,6 +52,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 TEXTBOX_BACKGROUND_COLOR: RwLock<[f32; 4]> = RwLock::new([0.08, 0.08, 0.12, 1.0]);
+static TEXTBOX_BACKGROUND_EDIT_COLOR: RwLock<[f32; 4]> = RwLock::new([0.06, 0.10, 0.18, 1.0]);
 
 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]);
@@ -295,6 +297,14 @@ fn parse_and_set_colors(content: &str) {
         .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("/style/textbox/background_color")
+        .or_else(|| get_color("/style/data/textbox/background_color")) {
+        if let Ok(mut lock) = TEXTBOX_BACKGROUND_COLOR.write() { *lock = c; }
+    }
+    if let Some(c) = get_color("/style/textbox/background_edit_color")
+        .or_else(|| get_color("/style/data/textbox/background_edit_color")) {
+        if let Ok(mut lock) = TEXTBOX_BACKGROUND_EDIT_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; }
     }
@@ -486,6 +496,28 @@ pub fn set_textbox_placeholder_text_color(color: [u8; 3]) {
     }
 }
 
+pub fn textbox_background_color() -> [f32; 4] {
+    load_colors_once();
+    *TEXTBOX_BACKGROUND_COLOR.read().unwrap()
+}
+
+pub fn set_textbox_background_color(color: [f32; 4]) {
+    if let Ok(mut lock) = TEXTBOX_BACKGROUND_COLOR.write() {
+        *lock = color;
+    }
+}
+
+pub fn textbox_background_edit_color() -> [f32; 4] {
+    load_colors_once();
+    *TEXTBOX_BACKGROUND_EDIT_COLOR.read().unwrap()
+}
+
+pub fn set_textbox_background_edit_color(color: [f32; 4]) {
+    if let Ok(mut lock) = TEXTBOX_BACKGROUND_EDIT_COLOR.write() {
+        *lock = color;
+    }
+}
+
 pub fn graph_wire_color() -> [f32; 4] {
     load_colors_once();
     *GRAPH_WIRE_COLOR.read().unwrap()
diff --git a/src/layout.rs b/src/layout.rs
index 0565abe..ffb1a52 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -94,6 +94,8 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
                 "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.background_color" | "style.data.textbox.background_color" => "textbox_background_color",
+                "style.textbox.background_edit_color" | "style.data.textbox.background_edit_color" => "textbox_background_edit_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/editor.rs b/src/widget/editor.rs
index 947dab4..07c7a33 100644
--- a/src/widget/editor.rs
+++ b/src/widget/editor.rs
@@ -29,12 +29,22 @@ impl TextEditorState {
         self.all_selected = false;
     }
 
+    pub fn clamp_bounds(&mut self) {
+        let len = self.buffer.chars().count();
+        self.cursor_idx = self.cursor_idx.min(len);
+        if let Some(anchor) = self.select_anchor {
+            self.select_anchor = Some(anchor.min(len));
+        }
+    }
+
     pub fn selected_range(&self) -> Option<(usize, usize)> {
-        let anchor = self.select_anchor?;
-        if anchor == self.cursor_idx {
+        let len = self.buffer.chars().count();
+        let anchor = self.select_anchor?.min(len);
+        let cursor = self.cursor_idx.min(len);
+        if anchor == cursor {
             None
         } else {
-            Some((anchor.min(self.cursor_idx), anchor.max(self.cursor_idx)))
+            Some((anchor.min(cursor), anchor.max(cursor)))
         }
     }
 
@@ -45,6 +55,7 @@ impl TextEditorState {
     }
 
     pub fn insert_text(&mut self, text: &str) {
+        self.clamp_bounds();
         if self.all_selected {
             self.buffer.clear();
             self.cursor_idx = 0;
@@ -69,6 +80,7 @@ impl TextEditorState {
     }
 
     pub fn delete_backwards(&mut self) -> bool {
+        self.clamp_bounds();
         if self.all_selected {
             self.buffer.clear();
             self.cursor_idx = 0;
@@ -103,6 +115,7 @@ impl TextEditorState {
     }
 
     pub fn delete_forwards(&mut self) -> bool {
+        self.clamp_bounds();
         if self.all_selected {
             self.buffer.clear();
             self.cursor_idx = 0;
@@ -137,6 +150,7 @@ impl TextEditorState {
     }
 
     pub fn move_cursor_left(&mut self, select: bool) -> bool {
+        self.clamp_bounds();
         if select {
             if self.select_anchor.is_none() {
                 self.select_anchor = Some(self.cursor_idx);
@@ -162,6 +176,7 @@ impl TextEditorState {
     }
 
     pub fn move_cursor_right(&mut self, select: bool) -> bool {
+        self.clamp_bounds();
         let char_count = self.buffer.chars().count();
         if select {
             if self.select_anchor.is_none() {
@@ -188,6 +203,7 @@ impl TextEditorState {
     }
 
     pub fn move_cursor_to_start(&mut self, select: bool) -> bool {
+        self.clamp_bounds();
         if select {
             if self.select_anchor.is_none() {
                 self.select_anchor = Some(self.cursor_idx);
@@ -201,6 +217,7 @@ impl TextEditorState {
     }
 
     pub fn move_cursor_to_end(&mut self, select: bool) -> bool {
+        self.clamp_bounds();
         let char_count = self.buffer.chars().count();
         if select {
             if self.select_anchor.is_none() {
diff --git a/src/widget/input/text_box.rs b/src/widget/input/text_box.rs
index 14691b2..ec630b2 100644
--- a/src/widget/input/text_box.rs
+++ b/src/widget/input/text_box.rs
@@ -577,6 +577,14 @@ impl Element for TextBox {
             self.text = val_str.clone();
             self.edit_buffer = val_str;
             self.just_changed = true;
+            let len = self.edit_buffer.chars().count();
+            self.cursor_idx = self.cursor_idx.min(len);
+            if let Some(anchor) = self.select_anchor {
+                self.select_anchor = Some(anchor.min(len));
+            }
+            if self.cursor_idx == 0 && self.select_anchor == Some(0) {
+                self.all_selected = false;
+            }
             self.sync_editor_state();
             self.clamp_scroll();
             true
@@ -1009,9 +1017,9 @@ impl Element for TextBox {
         
         if self.draw_bg_border {
             let bg_color = if self.editing {
-                [0.06, 0.10, 0.18, 1.0]
+                crate::colors::textbox_background_edit_color()
             } else {
-                [0.08, 0.08, 0.12, 1.0]
+                crate::colors::textbox_background_color()
             };
             let border_color = if self.editing {
                 [0.20, 0.50, 0.85, 1.0]
@@ -1158,9 +1166,9 @@ impl Element for TextBox {
         let radius = self.corner_radius();
         
         let bg_color = if self.editing {
-            [0.06, 0.10, 0.18, 1.0]
+            crate::colors::textbox_background_edit_color()
         } else {
-            [0.08, 0.08, 0.12, 1.0]
+            crate::colors::textbox_background_color()
         };
         let border_color = if self.editing {
             [0.20, 0.50, 0.85, 1.0]