GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
Customize Toggle widget background and border style
src/color.rs | 15 ++++++
src/layout.rs | 113 +++++++++++++++++++++++++++++++++++++++++++
src/widget/input/checkbox.rs | 25 +++++++---
3 files changed, 147 insertions(+), 6 deletions(-)
diff --git a/src/color.rs b/src/color.rs
index 306d99f..9ca5b43 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -36,6 +36,7 @@ static OPACITY: RwLock<Option<f32>> = RwLock::new(None);
static BACKPLATE_OPACITY: RwLock<Option<f32>> = RwLock::new(None);
static TOGGLE_ON_COLOR: RwLock<[f32; 4]> = RwLock::new(TOGGLE_ON);
static TOGGLE_OFF_COLOR: RwLock<[f32; 4]> = RwLock::new(TOGGLE_OFF);
+static TOGGLE_BG_COLOR: RwLock<[f32; 4]> = RwLock::new([0.18, 0.18, 0.22, 1.0]);
static SCROLLINGLIST_BG_COLOR: RwLock<[f32; 4]> = RwLock::new([0.08, 0.08, 0.12, 0.3]);
static SCROLLINGLIST_ENTRY_BG_COLOR: RwLock<[f32; 4]> = RwLock::new([1.0, 1.0, 1.0, 0.04]);
static SCROLLINGLIST_ENTRY_HIGHLIGHT_COLOR: RwLock<[f32; 4]> = RwLock::new([1.0, 1.0, 1.0, 0.8]);
@@ -175,6 +176,9 @@ fn parse_and_set_colors(content: &str) {
if let Some(c) = get_color("/layout/toggle_disabled_color") {
if let Ok(mut lock) = TOGGLE_OFF_COLOR.write() { *lock = c; }
}
+ if let Some(c) = get_color("/layout/toggle_bg_color") {
+ if let Ok(mut lock) = TOGGLE_BG_COLOR.write() { *lock = c; }
+ }
let parsed_scrollinglist_bg = get_color("/layout/scrollinglist_bg_color");
let parsed_breadcrumb_bg = get_color("/layout/breadcrumb_bg_color");
let parsed_popover_bg = get_color("/layout/popover_bg_color");
@@ -424,6 +428,17 @@ pub fn set_toggle_off_color(color: [f32; 4]) {
}
}
+pub fn toggle_bg_color() -> [f32; 4] {
+ load_colors_once();
+ *TOGGLE_BG_COLOR.read().unwrap()
+}
+
+pub fn set_toggle_bg_color(color: [f32; 4]) {
+ if let Ok(mut lock) = TOGGLE_BG_COLOR.write() {
+ *lock = color;
+ }
+}
+
pub fn scrollinglist_bg_color() -> [f32; 4] {
load_colors_once();
*SCROLLINGLIST_BG_COLOR.read().unwrap()
diff --git a/src/layout.rs b/src/layout.rs
index 7171c7b..ea48d39 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -103,6 +103,9 @@ static FONT_SELECTOR_CORNER_RADIUS: RwLock<f32> = RwLock::new(4.0);
static DROPDOWN_CORNER_RADIUS: RwLock<f32> = RwLock::new(4.0);
static TOGGLE_CORNER_RADIUS: RwLock<f32> = RwLock::new(4.0);
static SLIDER_CORNER_RADIUS: RwLock<f32> = RwLock::new(4.0);
+static TOGGLE_BORDER_WIDTH: RwLock<f32> = RwLock::new(1.0);
+static TOGGLE_FONT: RwLock<String> = RwLock::new(String::new());
+static TOGGLE_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
static PLATE_CORNER_RADIUS: RwLock<f32> = RwLock::new(12.0);
static PLATE_OPACITY: RwLock<f32> = RwLock::new(1.0);
static PAGE_OPACITY: RwLock<f32> = RwLock::new(1.0);
@@ -133,6 +136,7 @@ pub fn align_text_y(y: f32, height: f32, font_size: f32, top_offset: f32) -> f32
pub fn reload_config() {
if let Some(content) = read_config() {
let mut menubar_font_changed = false;
+ let mut toggle_font_changed = false;
for line in content.lines() {
let trimmed = line.trim();
if let Some(rest) = trimmed.strip_prefix("label_margin") {
@@ -491,12 +495,41 @@ pub fn reload_config() {
}
}
}
+ if let Some(rest) = trimmed.strip_prefix("toggle_border_width") {
+ let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+ let val_str = rest.trim_end_matches('"').trim();
+ if let Ok(val) = val_str.parse::<f32>() {
+ if let Ok(mut lock) = TOGGLE_BORDER_WIDTH.write() {
+ *lock = val;
+ }
+ }
+ }
+ if let Some(rest) = trimmed.strip_prefix("toggle_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) = TOGGLE_FONT.write() {
+ if *lock != font {
+ *lock = font;
+ changed = true;
+ }
+ }
+ if changed {
+ toggle_font_changed = true;
+ }
+ }
}
if menubar_font_changed {
if let Ok(mut lock) = MENUBAR_FONT_CACHED.write() {
*lock = None;
}
}
+ if toggle_font_changed {
+ if let Ok(mut lock) = TOGGLE_FONT_CACHED.write() {
+ *lock = None;
+ }
+ }
crate::color::reload_colors(&content);
}
}
@@ -827,6 +860,34 @@ pub fn set_toggle_corner_radius(radius: f32) {
}
}
+pub fn toggle_border_width() -> f32 {
+ use std::sync::Once;
+ static INIT: Once = Once::new();
+ INIT.call_once(|| {
+ if let Some(content) = read_config() {
+ for line in content.lines() {
+ let trimmed = line.trim();
+ if let Some(rest) = trimmed.strip_prefix("toggle_border_width") {
+ let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+ let val_str = rest.trim_end_matches('"').trim();
+ if let Ok(val) = val_str.parse::<f32>() {
+ if let Ok(mut lock) = TOGGLE_BORDER_WIDTH.write() {
+ *lock = val;
+ }
+ }
+ }
+ }
+ }
+ });
+ *TOGGLE_BORDER_WIDTH.read().unwrap()
+}
+
+pub fn set_toggle_border_width(width: f32) {
+ if let Ok(mut lock) = TOGGLE_BORDER_WIDTH.write() {
+ *lock = width;
+ }
+}
+
pub fn slider_corner_radius() -> f32 {
use std::sync::Once;
static INIT: Once = Once::new();
@@ -1121,6 +1182,58 @@ pub fn set_menubar_font(font: &str) {
}
}
+pub fn toggle_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("toggle_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) = TOGGLE_FONT.write() {
+ *lock = font;
+ }
+ });
+ let lock = TOGGLE_FONT.read().unwrap();
+ if lock.is_empty() {
+ "Outfit".to_string()
+ } else {
+ lock.clone()
+ }
+}
+
+pub fn toggle_font_parsed() -> (String, f32) {
+ if let Ok(lock) = TOGGLE_FONT_CACHED.read() {
+ if let Some(ref val) = *lock {
+ return val.clone();
+ }
+ }
+ let font_str = toggle_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) = TOGGLE_FONT_CACHED.write() {
+ *lock = Some(val.clone());
+ }
+ val
+}
+
+pub fn set_toggle_font(font: &str) {
+ if let Ok(mut lock) = TOGGLE_FONT.write() {
+ *lock = font.to_string();
+ }
+ if let Ok(mut lock) = TOGGLE_FONT_CACHED.write() {
+ *lock = None;
+ }
+}
+
pub fn section_label_font() -> String {
use std::sync::Once;
static INIT: Once = Once::new();
diff --git a/src/widget/input/checkbox.rs b/src/widget/input/checkbox.rs
index b79c926..301e60f 100644
--- a/src/widget/input/checkbox.rs
+++ b/src/widget/input/checkbox.rs
@@ -256,15 +256,27 @@ impl Element for Toggle {
}
fn color(&self) -> [f32; 4] {
- if self.toggled {
- colors::TOGGLE_ON
- } else if self.base.hovered {
- colors::TOGGLE_HOVER
+ colors::toggle_bg_color()
+ }
+
+ fn solid_border(&self) -> Option<([f32; 4], f32)> {
+ let border_color = if self.toggled {
+ colors::toggle_on_color()
+ } else {
+ colors::toggle_off_color()
+ };
+ let border_w = crate::layout::toggle_border_width();
+ if border_w > 0.0 {
+ Some((border_color, border_w))
} else {
- colors::TOGGLE_OFF
+ None
}
}
+ fn widget_font(&self) -> Option<String> {
+ Some(crate::layout::toggle_font())
+ }
+
fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32, ctx: &mut UiContext) -> bool {
if button != MouseButton::Left { return false; }
match state {
@@ -288,7 +300,8 @@ impl Element for Toggle {
let mut labels = Vec::new();
if let Some(ref label) = self.base.label {
let font_size = 12.0;
- let est_w = crate::widget::display::measure_text(label, font_size);
+ let font_fam = crate::layout::toggle_font_parsed().0;
+ let est_w = crate::widget::display::measure_text_width(label, &font_fam, font_size);
labels.push(TextLabel {
text: label.clone(),
x: self.base.x + (self.base.w - est_w) / 2.0,