GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
Refactor and fix scale factor sidebar scaling bug
src/backend/window_runner.rs | 21 +-
src/color.rs | 245 ++++++++++++++
src/layout.rs | 563 +++++++------------------------
src/process.rs | 28 ++
src/widget/container/container_layout.rs | 43 +++
src/widget/container/hbox.rs | 69 ++++
src/widget/container/mod.rs | 2 +-
src/widget/container/parameters_bg.rs | 2 +-
src/widget/container/plate.rs | 55 ++-
src/widget/container/vbox.rs | 66 ++++
src/widget/core.rs | 6 +-
src/widget/display/label.rs | 5 +-
src/widget/display/status_bar.rs | 5 +-
src/widget/input/button.rs | 33 +-
src/widget/input/button_strip.rs | 9 +-
src/widget/input/checkbox.rs | 11 +-
src/widget/input/dropdown.rs | 65 +++-
src/widget/input/ramp.rs | 27 +-
src/widget/input/slider.rs | 144 +++++---
src/widget/input/spinbox.rs | 76 +++--
src/widget/input/text_box.rs | 60 ++--
src/widget/input/trackpad.rs | 83 +++--
src/widget/mod.rs | 81 +++--
23 files changed, 1036 insertions(+), 663 deletions(-)
diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 6637a72..3237f83 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -1294,10 +1294,10 @@ pub trait Application: Sized + 'static {
self.text_items().iter().map(|ti| {
let mut item_bounds = if let Some([l, t, r, b]) = ti.bounds {
TextBounds {
- left: (l * scale_f32).round() as i32,
- top: (t * scale_f32).round() as i32,
- right: (r * scale_f32).round() as i32,
- bottom: (b * scale_f32).round() as i32,
+ left: ((l * scale_f32).round() as i32).clamp(0, bounds.right),
+ top: ((t * scale_f32).round() as i32).clamp(0, bounds.bottom),
+ right: ((r * scale_f32).round() as i32).clamp(0, bounds.right),
+ bottom: ((b * scale_f32).round() as i32).clamp(0, bounds.bottom),
}
} else {
bounds
@@ -1641,7 +1641,16 @@ impl<A: Application> EngineState<A> {
let bounds = TextBounds { left: 0, top: 0, right: pw as i32, bottom: ph as i32 };
let areas = self.inner.as_ref().unwrap().text_areas(scale_f32, bounds);
- eprintln!("DEBUG RENDER AREAS: len = {}", areas.len());
+ eprintln!("AREAS_LEN: {}", areas.len());
+ for (idx, area) in areas.iter().enumerate() {
+ let mut text_snippet = String::new();
+ for run in area.buffer.layout_runs() {
+ text_snippet.push_str(run.text);
+ }
+ eprintln!("TEXT_AREA idx={}: text='{}', left={}, top={}, bounds=[{}, {}, {}, {}]",
+ idx, text_snippet, area.left, area.top,
+ area.bounds.left, area.bounds.top, area.bounds.right, area.bounds.bottom);
+ }
adapter.text_renderer.prepare(&adapter.device, &adapter.queue, &mut adapter.font_system, &mut adapter.text_atlas, &adapter.text_viewport, areas, &mut adapter.swash_cache).unwrap();
@@ -1973,6 +1982,7 @@ impl<A: Application> WindowHandler for EngineState<A> {
crate::scale::set_maximized(is_max);
let (w, h) = configure.new_size;
+ eprintln!("CONFIGURE_NEW_SIZE: w={:?}, h={:?}, scale={}", w, h, self.scale_factor);
if let (Some(w), Some(h)) = (w, h) {
let width = w.get();
let height = h.get();
@@ -2929,4 +2939,5 @@ pub fn run<A: Application>() {
}
}
}
+ crate::process::cleanup_spawned_processes();
}
diff --git a/src/color.rs b/src/color.rs
index 5897384..708a8cb 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -34,6 +34,10 @@ static NODE_DRAG_COLOR: RwLock<[f32; 4]> = RwLock::new(NODE_DRAG);
static SIDEBAR_BG_COLOR: RwLock<[f32; 4]> = RwLock::new(SIDEBAR_BG);
static HIGHLIGHT_PRIMARY_COLOR: RwLock<[f32; 4]> = RwLock::new(HIGHLIGHT_PRIMARY);
static MENUBAR_TAB_LABEL_COLOR: RwLock<[f32; 4]> = RwLock::new([0.90196, 0.90196, 0.94902, 1.0]); // sRGB [230, 230, 242] linear
+static CONTROL_LABEL_COLOR: RwLock<[f32; 4]> = RwLock::new([0.61206, 0.61206, 0.68666, 1.0]); // sRGB [204, 204, 212]
+static CONTROL_LABEL_COLOR_DETACHED: RwLock<[f32; 4]> = RwLock::new([0.22416, 0.22416, 0.2526, 1.0]); // sRGB [131, 131, 138]
+static CONTROL_LABEL_HOVER_COLOR: RwLock<Option<[f32; 4]>> = RwLock::new(None);
+static CONTROL_LABEL_FOCUS_COLOR: RwLock<Option<[f32; 4]>> = RwLock::new(None);
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);
@@ -79,6 +83,20 @@ static SPINBOX_TEXT_COLOR: RwLock<[f32; 4]> = RwLock::new([0.8, 0.8, 0.83, 1.0])
static CHECKBOX_BG_COLOR: RwLock<[f32; 4]> = RwLock::new(CHECKBOX_BG);
static CHECKBOX_CHECKED_COLOR: RwLock<[f32; 4]> = RwLock::new(CHECKBOX_CHECKED);
static CHECKBOX_HOVER_COLOR: RwLock<[f32; 4]> = RwLock::new(CHECKBOX_HOVER);
+static CHECKBOX_BORDER_COLOR: RwLock<[f32; 4]> = RwLock::new([0.25, 0.25, 0.30, 1.0]);
+
+static BUTTON_BORDER_COLOR: RwLock<Option<[f32; 4]>> = RwLock::new(None);
+static BUTTON_HOVER_COLOR: RwLock<Option<[f32; 4]>> = RwLock::new(None);
+
+static DROPDOWN_BORDER_COLOR: RwLock<[f32; 4]> = RwLock::new([0.18, 0.18, 0.24, 1.0]);
+static DROPDOWN_TEXT_COLOR: RwLock<[f32; 4]> = RwLock::new([0.72305, 0.72305, 0.76008, 1.0]);
+
+static SLIDER_FILL_COLOR: RwLock<Option<[f32; 4]>> = RwLock::new(None);
+static SLIDER_THUMB_COLOR: RwLock<[f32; 4]> = RwLock::new(SLIDER_THUMB);
+static SLIDER_THUMB_DRAG_COLOR: RwLock<[f32; 4]> = RwLock::new(SLIDER_THUMB_DRAG);
+
+static RANGE_SLIDER_THUMB_COLOR: RwLock<[f32; 4]> = RwLock::new(SLIDER_THUMB);
+static RANGE_SLIDER_THUMB_DRAG_COLOR: RwLock<[f32; 4]> = RwLock::new(SLIDER_THUMB_DRAG);
static TREE_BACKGROUND_COLOR: RwLock<[f32; 4]> = RwLock::new([0.08, 0.08, 0.12, 0.3]);
static TREE_BORDER_COLOR: RwLock<[f32; 4]> = RwLock::new([0.18, 0.18, 0.24, 1.0]);
@@ -327,6 +345,18 @@ fn parse_and_set_colors(content: &str) {
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; }
}
+ if let Some(c) = get_color("/style/control/label/color") {
+ if let Ok(mut lock) = CONTROL_LABEL_COLOR.write() { *lock = c; }
+ }
+ if let Some(c) = get_color("/style/control/label/color_detached") {
+ if let Ok(mut lock) = CONTROL_LABEL_COLOR_DETACHED.write() { *lock = c; }
+ }
+ if let Some(c) = get_color("/style/control/label/hover_color") {
+ if let Ok(mut lock) = CONTROL_LABEL_HOVER_COLOR.write() { *lock = Some(c); }
+ }
+ if let Some(c) = get_color("/style/control/label/focus_color") {
+ if let Ok(mut lock) = CONTROL_LABEL_FOCUS_COLOR.write() { *lock = Some(c); }
+ }
let toggle_border_color = get_color("/style/control/toggle/border_color")
.or_else(|| get_color("/layout/toggle_border_color"));
@@ -385,6 +415,46 @@ fn parse_and_set_colors(content: &str) {
if let Some(c) = get_color("/style/control/checkbox/hover_color").or_else(|| get_color("/style/checkbox/hover_color")) {
if let Ok(mut lock) = CHECKBOX_HOVER_COLOR.write() { *lock = c; }
}
+ if let Some(c) = get_color("/style/control/checkbox/border_color").or_else(|| get_color("/style/checkbox/border_color")) {
+ if let Ok(mut lock) = CHECKBOX_BORDER_COLOR.write() { *lock = c; }
+ }
+ if let Some(c) = get_color("/style/control/button/border_color").or_else(|| get_color("/style/button/border_color")) {
+ if let Ok(mut lock) = BUTTON_BORDER_COLOR.write() { *lock = Some(c); }
+ }
+ if let Some(c) = get_color("/style/control/button/hover_color").or_else(|| get_color("/style/button/hover_color")) {
+ if let Ok(mut lock) = BUTTON_HOVER_COLOR.write() { *lock = Some(c); }
+ }
+ if let Some(c) = get_color("/style/control/dropdown/border_color").or_else(|| get_color("/style/dropdown/border_color")) {
+ if let Ok(mut lock) = DROPDOWN_BORDER_COLOR.write() { *lock = c; }
+ }
+ if let Some(c) = get_color("/style/control/dropdown/text_color").or_else(|| get_color("/style/dropdown/text_color")) {
+ if let Ok(mut lock) = DROPDOWN_TEXT_COLOR.write() { *lock = c; }
+ }
+ if let Some(c) = get_color("/style/control/slider/fill_color").or_else(|| get_color("/style/slider/fill_color")) {
+ if let Ok(mut lock) = SLIDER_FILL_COLOR.write() { *lock = Some(c); }
+ }
+ if let Some(c) = get_color("/style/control/slider/thumb_color").or_else(|| get_color("/style/slider/thumb_color")) {
+ if let Ok(mut lock) = SLIDER_THUMB_COLOR.write() { *lock = c; }
+ if let Ok(mut lock_drag) = SLIDER_THUMB_DRAG_COLOR.write() {
+ *lock_drag = [
+ (c[0] + 0.15).min(1.0),
+ (c[1] + 0.15).min(1.0),
+ (c[2] + 0.15).min(1.0),
+ c[3]
+ ];
+ }
+ }
+ if let Some(c) = get_color("/style/control/rangeslider/thumb_color").or_else(|| get_color("/style/rangeslider/thumb_color")) {
+ if let Ok(mut lock) = RANGE_SLIDER_THUMB_COLOR.write() { *lock = c; }
+ if let Ok(mut lock_drag) = RANGE_SLIDER_THUMB_DRAG_COLOR.write() {
+ *lock_drag = [
+ (c[0] + 0.15).min(1.0),
+ (c[1] + 0.15).min(1.0),
+ (c[2] + 0.15).min(1.0),
+ c[3]
+ ];
+ }
+ }
if let Some(c) = get_color("/style/control/progressbar/background") {
if let Ok(mut lock) = PROGRESS_BG_COLOR.write() { *lock = c; }
}
@@ -1121,6 +1191,105 @@ pub fn set_rangeslider_fill(color: [f32; 4]) {
}
}
+pub fn checkbox_border() -> [f32; 4] {
+ load_colors_once();
+ *CHECKBOX_BORDER_COLOR.read().unwrap()
+}
+
+pub fn set_checkbox_border(color: [f32; 4]) {
+ if let Ok(mut lock) = CHECKBOX_BORDER_COLOR.write() {
+ *lock = color;
+ }
+}
+
+pub fn button_border_color() -> Option<[f32; 4]> {
+ load_colors_once();
+ *BUTTON_BORDER_COLOR.read().unwrap()
+}
+
+pub fn set_button_border_color(color: [f32; 4]) {
+ if let Ok(mut lock) = BUTTON_BORDER_COLOR.write() {
+ *lock = Some(color);
+ }
+}
+
+pub fn dropdown_border_color() -> [f32; 4] {
+ load_colors_once();
+ *DROPDOWN_BORDER_COLOR.read().unwrap()
+}
+
+pub fn set_dropdown_border_color(color: [f32; 4]) {
+ if let Ok(mut lock) = DROPDOWN_BORDER_COLOR.write() {
+ *lock = color;
+ }
+}
+
+pub fn dropdown_text_color() -> [f32; 4] {
+ load_colors_once();
+ *DROPDOWN_TEXT_COLOR.read().unwrap()
+}
+
+pub fn set_dropdown_text_color(color: [f32; 4]) {
+ if let Ok(mut lock) = DROPDOWN_TEXT_COLOR.write() {
+ *lock = color;
+ }
+}
+
+pub fn slider_fill() -> Option<[f32; 4]> {
+ load_colors_once();
+ *SLIDER_FILL_COLOR.read().unwrap()
+}
+
+pub fn set_slider_fill(color: [f32; 4]) {
+ if let Ok(mut lock) = SLIDER_FILL_COLOR.write() {
+ *lock = Some(color);
+ }
+}
+
+pub fn slider_thumb() -> [f32; 4] {
+ load_colors_once();
+ *SLIDER_THUMB_COLOR.read().unwrap()
+}
+
+pub fn set_slider_thumb(color: [f32; 4]) {
+ if let Ok(mut lock) = SLIDER_THUMB_COLOR.write() {
+ *lock = color;
+ }
+}
+
+pub fn slider_thumb_drag() -> [f32; 4] {
+ load_colors_once();
+ *SLIDER_THUMB_DRAG_COLOR.read().unwrap()
+}
+
+pub fn set_slider_thumb_drag(color: [f32; 4]) {
+ if let Ok(mut lock) = SLIDER_THUMB_DRAG_COLOR.write() {
+ *lock = color;
+ }
+}
+
+pub fn rangeslider_thumb() -> [f32; 4] {
+ load_colors_once();
+ *RANGE_SLIDER_THUMB_COLOR.read().unwrap()
+}
+
+pub fn set_rangeslider_thumb(color: [f32; 4]) {
+ if let Ok(mut lock) = RANGE_SLIDER_THUMB_COLOR.write() {
+ *lock = color;
+ }
+}
+
+pub fn rangeslider_thumb_drag() -> [f32; 4] {
+ load_colors_once();
+ *RANGE_SLIDER_THUMB_DRAG_COLOR.read().unwrap()
+}
+
+pub fn set_rangeslider_thumb_drag(color: [f32; 4]) {
+ if let Ok(mut lock) = RANGE_SLIDER_THUMB_DRAG_COLOR.write() {
+ *lock = color;
+ }
+}
+
pub fn spinbox_display() -> [f32; 4] {
load_colors_once();
*SPINBOX_DISPLAY_COLOR.read().unwrap()
@@ -1443,6 +1612,82 @@ pub fn set_plate_blur(b: bool) {
}
}
+pub fn control_label_color() -> [f32; 4] {
+ *CONTROL_LABEL_COLOR.read().unwrap()
+}
+
+pub fn control_label_color_u8() -> [u8; 3] {
+ let c = control_label_color();
+ [
+ (linear_to_srgb(c[0]) * 255.0).round() as u8,
+ (linear_to_srgb(c[1]) * 255.0).round() as u8,
+ (linear_to_srgb(c[2]) * 255.0).round() as u8,
+ ]
+}
+
+pub fn set_control_label_color(color: [f32; 4]) {
+ if let Ok(mut lock) = CONTROL_LABEL_COLOR.write() {
+ *lock = color;
+ }
+}
+
+pub fn control_label_hover_color() -> Option<[f32; 4]> {
+ *CONTROL_LABEL_HOVER_COLOR.read().unwrap()
+}
+
+pub fn control_label_focus_color() -> Option<[f32; 4]> {
+ *CONTROL_LABEL_FOCUS_COLOR.read().unwrap()
+}
+
+pub fn control_label_color_for_state(hovered: bool, focused: bool) -> [u8; 3] {
+ let c = if focused {
+ control_label_focus_color().unwrap_or_else(|| control_label_color())
+ } else if hovered {
+ control_label_hover_color().unwrap_or_else(|| control_label_color())
+ } else {
+ control_label_color()
+ };
+ [
+ (linear_to_srgb(c[0]) * 255.0).round() as u8,
+ (linear_to_srgb(c[1]) * 255.0).round() as u8,
+ (linear_to_srgb(c[2]) * 255.0).round() as u8,
+ ]
+}
+
+pub fn control_label_color_detached() -> [f32; 4] {
+ *CONTROL_LABEL_COLOR_DETACHED.read().unwrap()
+}
+
+pub fn control_label_color_detached_u8() -> [u8; 3] {
+ let c = control_label_color_detached();
+ [
+ (linear_to_srgb(c[0]) * 255.0).round() as u8,
+ (linear_to_srgb(c[1]) * 255.0).round() as u8,
+ (linear_to_srgb(c[2]) * 255.0).round() as u8,
+ ]
+}
+
+pub fn set_control_label_color_detached(color: [f32; 4]) {
+ if let Ok(mut lock) = CONTROL_LABEL_COLOR_DETACHED.write() {
+ *lock = color;
+ }
+}
+
+pub fn control_label_color_detached_for_state(hovered: bool, focused: bool) -> [u8; 3] {
+ let c = if focused {
+ control_label_focus_color().unwrap_or_else(|| control_label_color_detached())
+ } else if hovered {
+ control_label_hover_color().unwrap_or_else(|| control_label_color_detached())
+ } else {
+ control_label_color_detached()
+ };
+ [
+ (linear_to_srgb(c[0]) * 255.0).round() as u8,
+ (linear_to_srgb(c[1]) * 255.0).round() as u8,
+ (linear_to_srgb(c[2]) * 255.0).round() as u8,
+ ]
+}
+
#[cfg(test)]
mod color_tests {
use super::*;
diff --git a/src/layout.rs b/src/layout.rs
index 188a8fe..dd1b715 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -67,30 +67,29 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
"style.list.font_color" | "style.data.list.font_color" => "list_font_color",
"style.control.breadcrumb.font" => "breadcrumb_font",
"style.control.breadcrumb.corner_radius" => "breadcrumb_corner_radius",
- "style.control.button.font" => "button_font",
"style.control.button.padding" => "button_padding",
"style.control.button.height" => "button_height",
"style.control.button.corner_radius" => "button_corner_radius",
"style.list.corner_radius" | "style.data.list.corner_radius" => "list_corner_radius",
"style.control.textbox.corner_radius" | "style.textbox.corner_radius" | "style.data.textbox.corner_radius" => "textbox_corner_radius",
- "style.control.dropdown.font" => "dropdown_font",
"style.control.dropdown.color" => "dropdown_color",
"style.control.font_selector.font" => "font_selector_font",
- "style.control.slider.font" => "slider_font",
- "style.control.spinbox.font" => "spinbox_font",
"style.section.font" => "section_label_font",
- "style.control.textbox.font" | "style.textbox.font" | "style.data.textbox.font" => "textbox_font",
"style.control.button_strip.font" => "button_strip_font",
"style.control.button_strip.spacing" => "button_strip_spacing",
"style.control.dropdown.height" => "dropdown_height",
"style.control.dropdown.corner_radius" => "dropdown_corner_radius",
"style.control.font_selector.height" => "font_selector_height",
"style.control.font_selector.corner_radius" => "font_selector_corner_radius",
- "style.label.font" => "label_font",
+ "style.control.label.font" => "control_label_font",
+ "style.control.label.font_detached" => "control_label_font_detached",
+ "style.control.label.margin" => "control_label_margin",
+ "style.control.label.layout" => "control_label_layout",
"style.control.slider.height" => "slider_height",
"style.control.slider.corner_radius" => "slider_corner_radius",
"style.control.progressbar.height" => "progressbar_height",
"style.control.rangeslider.height" => "rangeslider_height",
+ "style.control.rangeslider.corner_radius" | "style.rangeslider.corner_radius" => "rangeslider_corner_radius",
"style.control.scrollbar.width" => "scrollbar_width",
"style.control.spinbox.height" => "spinbox_height",
"style.control.spinbox.button_padding" => "spinbox_button_padding",
@@ -101,7 +100,6 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
"style.control.textbox.background_edit_color" | "style.textbox.background_edit_color" | "style.data.textbox.background_edit_color" => "textbox_background_edit_color",
"style.control.textbox.multiline.line_wrap" | "style.textbox.multiline.line_wrap" | "style.data.textbox.multiline.line_wrap" => "textbox_line_wrap",
"style.control.textbox.multiline.border_width" | "style.textbox.multiline.border_width" | "style.data.textbox.multiline.border_width" => "textbox_multiline_border_width",
- "style.control.toggle.font" => "toggle_font",
"style.control.toggle.height" => "toggle_height",
"style.control.toggle.border_width" => "toggle_border_width",
"style.control.toggle.disabled_color" => "toggle_disabled_color",
@@ -268,7 +266,7 @@ static PLATE_PADDING: RwLock<f32> = RwLock::new(20.0);
static DROPDOWN_HEIGHT: RwLock<f32> = RwLock::new(44.0);
static NESTED_SECTION_LABEL_ALIGNMENT: RwLock<u8> = RwLock::new(0);
-static LABEL_MARGIN: RwLock<f32> = RwLock::new(6.0);
+
static BUTTON_CORNER_RADIUS: RwLock<f32> = RwLock::new(4.0);
static SPINBOX_CORNER_RADIUS: RwLock<f32> = RwLock::new(4.0);
static TEXTBOX_CORNER_RADIUS: RwLock<f32> = RwLock::new(4.0);
@@ -276,28 +274,21 @@ 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 RANGESLIDER_CORNER_RADIUS: RwLock<f32> = RwLock::new(4.0);
static BREADCRUMB_CORNER_RADIUS: RwLock<f32> = RwLock::new(4.0);
static LIST_CORNER_RADIUS: RwLock<f32> = RwLock::new(4.0);
static TREE_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 FONT_SELECTOR_FONT: RwLock<String> = RwLock::new(String::new());
static FONT_SELECTOR_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
static BUTTON_STRIP_FONT: RwLock<String> = RwLock::new(String::new());
static BUTTON_STRIP_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
-static BUTTON_FONT: RwLock<String> = RwLock::new(String::new());
-static BUTTON_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
-static LABEL_FONT: RwLock<String> = RwLock::new(String::new());
-static LABEL_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
-static DROPDOWN_FONT: RwLock<String> = RwLock::new(String::new());
-static DROPDOWN_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
-static TEXTBOX_FONT: RwLock<String> = RwLock::new(String::new());
-static TEXTBOX_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
-static SPINBOX_FONT: RwLock<String> = RwLock::new(String::new());
-static SPINBOX_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
-static SLIDER_FONT: RwLock<String> = RwLock::new(String::new());
-static SLIDER_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
+static CONTROL_LABEL_FONT: RwLock<String> = RwLock::new(String::new());
+static CONTROL_LABEL_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
+static CONTROL_LABEL_FONT_DETACHED: RwLock<String> = RwLock::new(String::new());
+static CONTROL_LABEL_FONT_DETACHED_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
+static CONTROL_LABEL_MARGIN: RwLock<f32> = RwLock::new(6.0);
+static CONTROL_LABEL_LAYOUT: RwLock<String> = RwLock::new(String::new());
static PLATE_CORNER_RADIUS: RwLock<f32> = RwLock::new(12.0);
static LIST_FONT: RwLock<String> = RwLock::new(String::new());
static LIST_FONT_CACHED: RwLock<Option<(String, f32)>> = RwLock::new(None);
@@ -340,15 +331,10 @@ pub fn reload_config() {
if let Some(content) = read_config() {
let mut menubar_font_changed = false;
let mut statusbar_font_changed = false;
- let mut toggle_font_changed = false;
let mut font_selector_font_changed = false;
let mut button_strip_font_changed = false;
- let mut button_font_changed = false;
let mut label_font_changed = false;
- let mut dropdown_font_changed = false;
- let mut textbox_font_changed = false;
- let mut spinbox_font_changed = false;
- let mut slider_font_changed = false;
+ let mut label_font_detached_changed = false;
let mut list_font_changed = false;
let mut tree_font_changed = false;
let mut graph_font_changed = false;
@@ -366,15 +352,22 @@ pub fn reload_config() {
}
}
}
- if let Some(rest) = trimmed.strip_prefix("label_margin") {
+ if let Some(rest) = trimmed.strip_prefix("control_label_margin") {
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) = LABEL_MARGIN.write() {
+ if let Ok(mut lock) = CONTROL_LABEL_MARGIN.write() {
*lock = val;
}
}
}
+ if let Some(rest) = trimmed.strip_prefix("control_label_layout") {
+ let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+ let val_str = rest.trim_end_matches('"').trim().to_string();
+ if let Ok(mut lock) = CONTROL_LABEL_LAYOUT.write() {
+ *lock = val_str;
+ }
+ }
if let Some(rest) = trimmed.strip_prefix("nested_section_label_alignment") {
let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
let val_str = rest.trim_end_matches('"').trim();
@@ -867,81 +860,44 @@ pub fn reload_config() {
}
}
}
- if let Some(rest) = trimmed.strip_prefix("toggle_border_width") {
+ if let Some(rest) = trimmed.strip_prefix("rangeslider_corner_radius") {
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() {
+ if let Ok(mut lock) = RANGESLIDER_CORNER_RADIUS.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 let Some(rest) = trimmed.strip_prefix("font_selector_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) = FONT_SELECTOR_FONT.write() {
- if *lock != font {
- *lock = font;
- changed = true;
- }
- }
- if changed {
- font_selector_font_changed = true;
- }
- }
- if let Some(rest) = trimmed.strip_prefix("button_strip_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) = BUTTON_STRIP_FONT.write() {
- if *lock != font {
- *lock = font;
- changed = true;
+ 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 changed {
- button_strip_font_changed = true;
- }
}
- if let Some(rest) = trimmed.strip_prefix("button_font") {
+ 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);
let font = rest.trim().to_string();
let mut changed = false;
- if let Ok(mut lock) = BUTTON_FONT.write() {
+ if let Ok(mut lock) = CONTROL_LABEL_FONT_DETACHED.write() {
if *lock != font {
*lock = font;
changed = true;
}
}
if changed {
- button_font_changed = true;
+ label_font_detached_changed = true;
}
- }
- if let Some(rest) = trimmed.strip_prefix("label_font") {
+ } else 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);
let font = rest.trim().to_string();
let mut changed = false;
- if let Ok(mut lock) = LABEL_FONT.write() {
+ if let Ok(mut lock) = CONTROL_LABEL_FONT.write() {
if *lock != font {
*lock = font;
changed = true;
@@ -951,64 +907,34 @@ pub fn reload_config() {
label_font_changed = true;
}
}
- if let Some(rest) = trimmed.strip_prefix("dropdown_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) = DROPDOWN_FONT.write() {
- if *lock != font {
- *lock = font;
- changed = true;
- }
- }
- if changed {
- dropdown_font_changed = true;
- }
- }
- if let Some(rest) = trimmed.strip_prefix("textbox_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) = TEXTBOX_FONT.write() {
- if *lock != font {
- *lock = font;
- changed = true;
- }
- }
- if changed {
- textbox_font_changed = true;
- }
- }
- if let Some(rest) = trimmed.strip_prefix("spinbox_font") {
+ if let Some(rest) = trimmed.strip_prefix("font_selector_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) = SPINBOX_FONT.write() {
+ if let Ok(mut lock) = FONT_SELECTOR_FONT.write() {
if *lock != font {
*lock = font;
changed = true;
}
}
if changed {
- spinbox_font_changed = true;
+ font_selector_font_changed = true;
}
}
- if let Some(rest) = trimmed.strip_prefix("slider_font") {
+ if let Some(rest) = trimmed.strip_prefix("button_strip_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) = SLIDER_FONT.write() {
+ if let Ok(mut lock) = BUTTON_STRIP_FONT.write() {
if *lock != font {
*lock = font;
changed = true;
}
}
if changed {
- slider_font_changed = true;
+ button_strip_font_changed = true;
}
}
if let Some(rest) = trimmed.strip_prefix("list_font") {
@@ -1091,8 +1017,8 @@ pub fn reload_config() {
*lock = None;
}
}
- if toggle_font_changed {
- if let Ok(mut lock) = TOGGLE_FONT_CACHED.write() {
+ if label_font_detached_changed {
+ if let Ok(mut lock) = CONTROL_LABEL_FONT_DETACHED_CACHED.write() {
*lock = None;
}
}
@@ -1106,36 +1032,12 @@ pub fn reload_config() {
*lock = None;
}
}
- if button_font_changed {
- if let Ok(mut lock) = BUTTON_FONT_CACHED.write() {
- *lock = None;
- }
- }
if label_font_changed {
- if let Ok(mut lock) = LABEL_FONT_CACHED.write() {
- *lock = None;
- }
- }
- if dropdown_font_changed {
- if let Ok(mut lock) = DROPDOWN_FONT_CACHED.write() {
- *lock = None;
- }
- }
- if textbox_font_changed {
- if let Ok(mut lock) = TEXTBOX_FONT_CACHED.write() {
- *lock = None;
- }
- }
- if spinbox_font_changed {
- if let Ok(mut lock) = SPINBOX_FONT_CACHED.write() {
- *lock = None;
- }
- }
- if slider_font_changed {
- if let Ok(mut lock) = SLIDER_FONT_CACHED.write() {
+ if let Ok(mut lock) = CONTROL_LABEL_FONT_CACHED.write() {
*lock = None;
}
}
+
if list_font_changed {
if let Ok(mut lock) = LIST_FONT_CACHED.write() {
*lock = None;
@@ -1171,34 +1073,33 @@ fn mod_rest(rest: &str) -> &str {
}
}
+pub fn control_label_margin() -> f32 {
+ *CONTROL_LABEL_MARGIN.read().unwrap()
+}
+
+pub fn control_label_layout() -> String {
+ let lock = CONTROL_LABEL_LAYOUT.read().unwrap();
+ if lock.is_empty() {
+ "top".to_string()
+ } else {
+ lock.clone()
+ }
+}
+
pub fn label_margin() -> 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("label_margin") {
- 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) = LABEL_MARGIN.write() {
- *lock = val;
- }
- }
- }
- }
- }
- });
- *LABEL_MARGIN.read().unwrap()
+ control_label_margin()
}
-pub fn set_label_margin(margin: f32) {
- if let Ok(mut lock) = LABEL_MARGIN.write() {
+pub fn set_control_label_margin(margin: f32) {
+ if let Ok(mut lock) = CONTROL_LABEL_MARGIN.write() {
*lock = margin;
}
}
+pub fn set_label_margin(margin: f32) {
+ set_control_label_margin(margin);
+}
+
pub fn nested_section_label_alignment() -> u8 {
use std::sync::Once;
static INIT: Once = Once::new();
@@ -1604,6 +1505,18 @@ pub fn set_slider_corner_radius(radius: f32) {
}
}
+pub fn rangeslider_corner_radius() -> f32 {
+ lazy_init_style_registry();
+ get_style_registry().read().unwrap().get_float("rangeslider_corner_radius").unwrap_or(4.0)
+}
+
+pub fn set_rangeslider_corner_radius(radius: f32) {
+ lazy_init_style_registry();
+ if let Ok(mut registry) = get_style_registry().write() {
+ registry.set_float("rangeslider_corner_radius", radius);
+ }
+}
+
pub fn plate_corner_radius() -> f32 {
lazy_init_style_registry();
let r = get_style_registry().read().unwrap();
@@ -1914,57 +1827,7 @@ pub fn set_statusbar_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 font_selector_font() -> String {
use std::sync::Once;
@@ -2071,61 +1934,10 @@ pub fn set_button_strip_font(font: &str) {
}
}
-// Button Font
-pub fn button_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("button_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) = BUTTON_FONT.write() {
- *lock = font;
- }
- });
- let lock = BUTTON_FONT.read().unwrap();
- if lock.is_empty() {
- "Outfit".to_string()
- } else {
- lock.clone()
- }
-}
-
-pub fn button_font_parsed() -> (String, f32) {
- if let Ok(lock) = BUTTON_FONT_CACHED.read() {
- if let Some(ref val) = *lock {
- return val.clone();
- }
- }
- let font_str = button_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) = BUTTON_FONT_CACHED.write() {
- *lock = Some(val.clone());
- }
- val
-}
-pub fn set_button_font(font: &str) {
- if let Ok(mut lock) = BUTTON_FONT.write() {
- *lock = font.to_string();
- }
- if let Ok(mut lock) = BUTTON_FONT_CACHED.write() {
- *lock = None;
- }
-}
-// Label Font
-pub fn label_font() -> String {
+// Control Label Font
+pub fn control_label_font() -> String {
use std::sync::Once;
static INIT: Once = Once::new();
INIT.call_once(|| {
@@ -2133,18 +1945,18 @@ pub fn label_font() -> String {
if let Some(content) = read_config() {
for line in content.lines() {
let trimmed = line.trim();
- if let Some(rest) = trimmed.strip_prefix("label_font") {
+ 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();
}
}
}
- if let Ok(mut lock) = LABEL_FONT.write() {
+ if let Ok(mut lock) = CONTROL_LABEL_FONT.write() {
*lock = font;
}
});
- let lock = LABEL_FONT.read().unwrap();
+ let lock = CONTROL_LABEL_FONT.read().unwrap();
if lock.is_empty() {
"Outfit".to_string()
} else {
@@ -2152,33 +1964,33 @@ pub fn label_font() -> String {
}
}
-pub fn label_font_parsed() -> (String, f32) {
- if let Ok(lock) = LABEL_FONT_CACHED.read() {
+pub fn control_label_font_parsed() -> (String, f32) {
+ if let Ok(lock) = CONTROL_LABEL_FONT_CACHED.read() {
if let Some(ref val) = *lock {
return val.clone();
}
}
- let font_str = label_font();
+ let font_str = control_label_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) = LABEL_FONT_CACHED.write() {
+ if let Ok(mut lock) = CONTROL_LABEL_FONT_CACHED.write() {
*lock = Some(val.clone());
}
val
}
-pub fn set_label_font(font: &str) {
- if let Ok(mut lock) = LABEL_FONT.write() {
+pub fn set_control_label_font(font: &str) {
+ if let Ok(mut lock) = CONTROL_LABEL_FONT.write() {
*lock = font.to_string();
}
- if let Ok(mut lock) = LABEL_FONT_CACHED.write() {
+ if let Ok(mut lock) = CONTROL_LABEL_FONT_CACHED.write() {
*lock = None;
}
}
-// Dropdown Font
-pub fn dropdown_font() -> String {
+// Control Label Font Detached
+pub fn control_label_font_detached() -> String {
use std::sync::Once;
static INIT: Once = Once::new();
INIT.call_once(|| {
@@ -2186,18 +1998,18 @@ pub fn dropdown_font() -> String {
if let Some(content) = read_config() {
for line in content.lines() {
let trimmed = line.trim();
- if let Some(rest) = trimmed.strip_prefix("dropdown_font") {
+ 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();
}
}
}
- if let Ok(mut lock) = DROPDOWN_FONT.write() {
+ if let Ok(mut lock) = CONTROL_LABEL_FONT_DETACHED.write() {
*lock = font;
}
});
- let lock = DROPDOWN_FONT.read().unwrap();
+ let lock = CONTROL_LABEL_FONT_DETACHED.read().unwrap();
if lock.is_empty() {
"Outfit".to_string()
} else {
@@ -2205,31 +2017,33 @@ pub fn dropdown_font() -> String {
}
}
-pub fn dropdown_font_parsed() -> (String, f32) {
- if let Ok(lock) = DROPDOWN_FONT_CACHED.read() {
+pub fn control_label_font_detached_parsed() -> (String, f32) {
+ if let Ok(lock) = CONTROL_LABEL_FONT_DETACHED_CACHED.read() {
if let Some(ref val) = *lock {
return val.clone();
}
}
- let font_str = dropdown_font();
+ let font_str = control_label_font_detached();
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) = DROPDOWN_FONT_CACHED.write() {
+ if let Ok(mut lock) = CONTROL_LABEL_FONT_DETACHED_CACHED.write() {
*lock = Some(val.clone());
}
val
}
-pub fn set_dropdown_font(font: &str) {
- if let Ok(mut lock) = DROPDOWN_FONT.write() {
+pub fn set_control_label_font_detached(font: &str) {
+ if let Ok(mut lock) = CONTROL_LABEL_FONT_DETACHED.write() {
*lock = font.to_string();
}
- if let Ok(mut lock) = DROPDOWN_FONT_CACHED.write() {
+ if let Ok(mut lock) = CONTROL_LABEL_FONT_DETACHED_CACHED.write() {
*lock = None;
}
}
+
+
// List Font
pub fn list_font() -> String {
use std::sync::Once;
@@ -2471,164 +2285,7 @@ pub fn set_list_justification(just: u8) {
}
}
-// Textbox Font
-pub fn textbox_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("textbox_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) = TEXTBOX_FONT.write() {
- *lock = font;
- }
- });
- let lock = TEXTBOX_FONT.read().unwrap();
- if lock.is_empty() {
- "Outfit".to_string()
- } else {
- lock.clone()
- }
-}
-
-pub fn textbox_font_parsed() -> (String, f32) {
- if let Ok(lock) = TEXTBOX_FONT_CACHED.read() {
- if let Some(ref val) = *lock {
- return val.clone();
- }
- }
- let font_str = textbox_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) = TEXTBOX_FONT_CACHED.write() {
- *lock = Some(val.clone());
- }
- val
-}
-
-pub fn set_textbox_font(font: &str) {
- if let Ok(mut lock) = TEXTBOX_FONT.write() {
- *lock = font.to_string();
- }
- if let Ok(mut lock) = TEXTBOX_FONT_CACHED.write() {
- *lock = None;
- }
-}
-
-// Spinbox Font
-pub fn spinbox_font() -> String {
- use std::sync::Once;
- static INIT: Once = Once::new();
- INIT.call_once(|| {
- let mut font = "monospace".to_string();
- if let Some(content) = read_config() {
- for line in content.lines() {
- let trimmed = line.trim();
- if let Some(rest) = trimmed.strip_prefix("spinbox_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) = SPINBOX_FONT.write() {
- *lock = font;
- }
- });
- let lock = SPINBOX_FONT.read().unwrap();
- if lock.is_empty() {
- "monospace".to_string()
- } else {
- lock.clone()
- }
-}
-
-pub fn spinbox_font_parsed() -> (String, f32) {
- if let Ok(lock) = SPINBOX_FONT_CACHED.read() {
- if let Some(ref val) = *lock {
- return val.clone();
- }
- }
- let font_str = spinbox_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) = SPINBOX_FONT_CACHED.write() {
- *lock = Some(val.clone());
- }
- val
-}
-
-pub fn set_spinbox_font(font: &str) {
- if let Ok(mut lock) = SPINBOX_FONT.write() {
- *lock = font.to_string();
- }
- if let Ok(mut lock) = SPINBOX_FONT_CACHED.write() {
- *lock = None;
- }
-}
-
-// Slider Font
-pub fn slider_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("slider_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) = SLIDER_FONT.write() {
- *lock = font;
- }
- });
- let lock = SLIDER_FONT.read().unwrap();
- if lock.is_empty() {
- "Outfit".to_string()
- } else {
- lock.clone()
- }
-}
-
-pub fn slider_font_parsed() -> (String, f32) {
- if let Ok(lock) = SLIDER_FONT_CACHED.read() {
- if let Some(ref val) = *lock {
- return val.clone();
- }
- }
- let font_str = slider_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) = SLIDER_FONT_CACHED.write() {
- *lock = Some(val.clone());
- }
- val
-}
-pub fn set_slider_font(font: &str) {
- if let Ok(mut lock) = SLIDER_FONT.write() {
- *lock = font.to_string();
- }
- if let Ok(mut lock) = SLIDER_FONT_CACHED.write() {
- *lock = None;
- }
-}
pub fn section_label_font() -> String {
@@ -5541,6 +5198,10 @@ mod tests {
fn test_vstack_flow() {
let orig_margin = label_margin();
set_label_margin(6.0);
+ let orig_font = control_label_font();
+ set_control_label_font("Outfit 12");
+ let orig_font_detached = control_label_font_detached();
+ set_control_label_font_detached("Outfit 12");
let _ = section_padding();
set_section_padding(8.0);
let mut mock_pc = MockRenderTarget { rects: Vec::new() };
@@ -5568,9 +5229,13 @@ mod tests {
let mut w3 = MockWidgetWithLabel { base };
stack.add_widget(&mut w3, 70.0, 50.0, &mut dummy);
- // Third widget has label, so its y should be shifted by 18.0
- assert_eq!(w3.base.y, start_y + 30.0 + 10.0 + 40.0 + 10.0 + 18.0);
+ let offset = w3.base.label_offset();
+
+ // Third widget has label, so its y should be shifted by offset
+ assert_eq!(w3.base.y, start_y + 30.0 + 10.0 + 40.0 + 10.0 + offset);
set_label_margin(orig_margin);
+ set_control_label_font(&orig_font);
+ set_control_label_font_detached(&orig_font_detached);
}
#[test]
diff --git a/src/process.rs b/src/process.rs
index f942c17..9851e38 100644
--- a/src/process.rs
+++ b/src/process.rs
@@ -1,4 +1,7 @@
use std::process::Command;
+use std::sync::Mutex;
+
+static TRACKED_PROCESSES: Mutex<Vec<std::process::Child>> = Mutex::new(Vec::new());
/// Spawns a process detached and automatically reaps it when it exits.
///
@@ -20,6 +23,24 @@ pub fn spawn_detached(mut cmd: Command) -> std::io::Result<()> {
Ok(())
}
+/// Spawns a process and registers it to be automatically killed when the application exits.
+pub fn spawn_tracked(mut cmd: Command) -> std::io::Result<()> {
+ let child = cmd.spawn()?;
+ if let Ok(mut lock) = TRACKED_PROCESSES.lock() {
+ lock.push(child);
+ }
+ Ok(())
+}
+
+/// Kills all spawned and tracked child processes. Called automatically on application exit.
+pub fn cleanup_spawned_processes() {
+ if let Ok(mut lock) = TRACKED_PROCESSES.lock() {
+ for mut child in lock.drain(..) {
+ let _ = child.kill();
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -29,4 +50,11 @@ mod tests {
let cmd = Command::new("true");
assert!(spawn_detached(cmd).is_ok());
}
+
+ #[test]
+ fn test_spawn_tracked() {
+ let cmd = Command::new("true");
+ assert!(spawn_tracked(cmd).is_ok());
+ cleanup_spawned_processes();
+ }
}
diff --git a/src/widget/container/container_layout.rs b/src/widget/container/container_layout.rs
index b099704..83fbb33 100644
--- a/src/widget/container/container_layout.rs
+++ b/src/widget/container/container_layout.rs
@@ -67,6 +67,49 @@ impl ContainerLayout for OverlayLayout {
}
}
+#[derive(Debug, Clone, Copy, Default, PartialEq)]
+pub struct ManualLayout {
+ left: f32,
+ top: f32,
+ width: f32,
+ height: f32,
+}
+
+impl crate::layout::LayoutStrategy for ManualLayout {
+ fn init(&mut self, left: f32, top: f32, width: f32, height: f32) {
+ self.left = left;
+ self.top = top;
+ self.width = width;
+ self.height = height;
+ }
+
+ fn allocate(&mut self, _ww: f32, _wh: f32) -> (f32, f32, f32, f32) {
+ (self.left, self.top, self.width, self.height)
+ }
+
+ fn layout(&self, _x: f32, _y: f32, _w: f32, _h: f32, _children: &[*mut (dyn Element + 'static)], _ctx: &mut UiContext) -> f32 {
+ self.height
+ }
+
+ fn measure(&self, constraints: LayoutConstraints, _children: &[*mut (dyn Element + 'static)], _ctx: &UiContext) -> Size {
+ Size {
+ width: self.width.clamp(constraints.min_width, constraints.max_width),
+ height: self.height.clamp(constraints.min_height, constraints.max_height),
+ }
+ }
+
+ fn box_clone(&self) -> Box<dyn crate::layout::LayoutStrategy> {
+ Box::new(*self)
+ }
+}
+
+impl ContainerLayout for ManualLayout {
+ fn box_clone_container(&self) -> Box<dyn ContainerLayout> {
+ Box::new(*self)
+ }
+}
+
+
#[derive(Debug, Clone, Copy)]
pub struct VerticalLayout {
pub padding_x: f32,
diff --git a/src/widget/container/hbox.rs b/src/widget/container/hbox.rs
new file mode 100644
index 0000000..42823bf
--- /dev/null
+++ b/src/widget/container/hbox.rs
@@ -0,0 +1,69 @@
+use crate::widget::*;
+
+pub struct HBox {
+ pub base: Widget,
+ pub children: Vec<*mut (dyn Element + 'static)>,
+ pub parent: Option<*mut (dyn Element + 'static)>,
+ pub gap: f32,
+ pub margin: f32,
+}
+
+impl HBox {
+ pub fn new(gap: f32, margin: f32) -> Self {
+ Self {
+ base: Widget::new(),
+ children: Vec::new(),
+ parent: None,
+ gap,
+ margin,
+ }
+ }
+
+ pub fn add_child(&mut self, child: *mut (dyn Element + 'static)) {
+ self.children.push(child);
+ }
+}
+
+impl Element for HBox {
+ crate::impl_widget_base!(HBox);
+
+ fn color(&self) -> [f32; 4] {
+ [0.0, 0.0, 0.0, 0.0]
+ }
+
+ fn children(&self, _ctx: &UiContext) -> Vec<*mut (dyn Element + 'static)> {
+ self.children.clone()
+ }
+
+ fn parent(&self, _ctx: &UiContext) -> Option<*mut (dyn Element + 'static)> {
+ self.parent
+ }
+
+ fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>, _ctx: &mut UiContext) {
+ self.parent = parent;
+ }
+
+ fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
+ self.base.x = x;
+ self.base.y = y;
+ self.base.w = w;
+ self.base.h = h;
+
+ let count = self.children.len();
+ if count == 0 {
+ return;
+ }
+
+ let total_w = w - 2.0 * self.margin;
+ let child_w = (total_w - (count as f32 - 1.0) * self.gap) / count as f32;
+ let child_h = h - 2.0 * self.margin;
+ let mut current_x = x + self.margin;
+
+ for &child_ptr in &self.children {
+ unsafe {
+ (*child_ptr).set_rect(current_x, y + self.margin, child_w, child_h);
+ current_x += child_w + self.gap;
+ }
+ }
+ }
+}
diff --git a/src/widget/container/mod.rs b/src/widget/container/mod.rs
index 92e1b12..09089e8 100644
--- a/src/widget/container/mod.rs
+++ b/src/widget/container/mod.rs
@@ -24,7 +24,7 @@ pub mod hbox;
pub use container::Container;
pub use control_panel::ControlPanel;
-pub use container_layout::{ContainerLayout, OverlayLayout, VerticalLayout, GridLayout, AdaptiveGridLayout, ColumnsLayout, MosaicLayout, ReverseMosaicLayout};
+pub use container_layout::{ContainerLayout, OverlayLayout, ManualLayout, VerticalLayout, GridLayout, AdaptiveGridLayout, ColumnsLayout, MosaicLayout, ReverseMosaicLayout};
pub use section_container::SectionContainer;
pub use header::Header;
pub use content_bg::ContentBg;
diff --git a/src/widget/container/parameters_bg.rs b/src/widget/container/parameters_bg.rs
index f48fe4a..c09f245 100644
--- a/src/widget/container/parameters_bg.rs
+++ b/src/widget/container/parameters_bg.rs
@@ -285,7 +285,7 @@ impl Element for ParametersBg {
crate::impl_widget_base!(ParametersBg);
fn widget_font(&self) -> Option<String> {
- Some(crate::layout::label_font())
+ Some(crate::layout::control_label_font())
}
fn rounded_corners(&self) -> (bool, bool, bool, bool) { (true, true, true, true) }
diff --git a/src/widget/container/plate.rs b/src/widget/container/plate.rs
index 6e02746..7589a17 100644
--- a/src/widget/container/plate.rs
+++ b/src/widget/container/plate.rs
@@ -224,6 +224,7 @@ impl Element for Plate {
}
fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
+ let label_off = self.base.base.label_offset();
let (clamped_x, clamped_y, clamped_w, clamped_h) = if let Some(parent_ptr) = self.base.parent {
let (px, py, pw, ph) = unsafe { (*parent_ptr).rect() };
let cx = x.clamp(px, px + pw.max(0.0));
@@ -238,7 +239,7 @@ impl Element for Plate {
self.base.base.x = clamped_x;
self.base.base.y = clamped_y;
self.base.base.w = clamped_w;
- self.base.base.h = clamped_h;
+ self.base.base.h = clamped_h + label_off;
if !self.base.visible {
return;
@@ -249,7 +250,7 @@ impl Element for Plate {
let padding_y = pad;
let left_x = clamped_x + padding_x;
let available_w = (clamped_w - 2.0 * padding_x).max(1.0);
- let start_y = clamped_y + padding_y;
+ let start_y = clamped_y + label_off + padding_y;
let available_h = (clamped_h - 2.0 * padding_y).max(1.0);
let center_x = left_x + available_w / 2.0;
@@ -287,7 +288,7 @@ impl Element for Plate {
let cx = (center_x - use_w / 2.0).clamp(left_x, (left_x + available_w - use_w).max(left_x));
let cy = (center_y - use_h / 2.0).clamp(start_y, (start_y + available_h - use_h).max(start_y));
let cw = use_w.min(clamped_x + clamped_w - padding_x - cx);
- let ch = use_h.min(clamped_y + clamped_h - padding_y - cy);
+ let ch = use_h.min(clamped_y + label_off + clamped_h - padding_y - cy);
w.set_rect(cx, cy, cw, ch);
} else {
let mut ring = 1;
@@ -309,7 +310,7 @@ impl Element for Plate {
let cx = raw_x.clamp(left_x, (left_x + available_w - use_w).max(left_x));
let cy = raw_y.clamp(start_y, (start_y + available_h - use_h).max(start_y));
let cw = use_w.min(clamped_x + clamped_w - padding_x - cx);
- let ch = use_h.min(clamped_y + clamped_h - padding_y - cy);
+ let ch = use_h.min(clamped_y + label_off + clamped_h - padding_y - cy);
w.set_rect(cx, cy, cw, ch);
placed = true;
@@ -366,6 +367,28 @@ impl Element for Plate {
ctx.clear_children_ids(id);
}
+ fn all_rounded_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, f32, [f32; 4], (bool, bool, bool, bool))> {
+ if !self.visible {
+ return Vec::new();
+ }
+ let mut quads = Vec::new();
+ let (r1, r2, r3, r4) = self.rounded_corners();
+ if r1 || r2 || r3 || r4 {
+ let (x, y, w, h) = self.rect();
+ let label_off = self.base.base.label_offset();
+ let radius = self.corner_radius();
+ let c = self.color();
+ if c[3].abs() > 0.001 {
+ quads.push((x, y + label_off, w, h - label_off, radius, c, (r1, r2, r3, r4)));
+ }
+ }
+ for &child_ptr in &self.base.children {
+ let widget = unsafe { &*child_ptr };
+ quads.extend(widget.all_rounded_quads(ctx));
+ }
+ quads
+ }
+
fn all_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
if !self.visible {
return Vec::new();
@@ -374,7 +397,8 @@ impl Element for Plate {
let (px, py, pw, ph) = self.rect();
let has_rounded = self.rounded_corners() != (false, false, false, false);
if !has_rounded {
- quads.push((px, py, pw, ph, self.color()));
+ let label_off = self.base.base.label_offset();
+ quads.push((px, py + label_off, pw, ph - label_off, self.color()));
}
for &child_ptr in &self.base.children {
@@ -397,12 +421,13 @@ impl Element for Plate {
}
let mut labels = Vec::new();
if let Some(ref label) = self.base.base.label {
+ let (_, font_size) = crate::layout::control_label_font_parsed();
labels.push(TextLabel {
text: label.clone(),
x: self.base.base.x,
- y: self.base.base.y - (12.0 + crate::layout::label_margin()),
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
+ y: self.base.base.y,
+ font_size,
+ color: colors::control_label_color_u8(),
});
}
for &child_ptr in &self.base.children {
@@ -418,13 +443,14 @@ impl Element for Plate {
}
let mut result = Vec::new();
if let Some(ref label) = self.base.base.label {
+ let (_, font_size) = crate::layout::control_label_font_parsed();
result.push((
TextLabel {
text: label.clone(),
x: self.base.base.x,
- y: self.base.base.y - (12.0 + crate::layout::label_margin()),
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
+ y: self.base.base.y,
+ font_size,
+ color: colors::control_label_color_u8(),
},
None,
));
@@ -442,13 +468,14 @@ impl Element for Plate {
}
let mut result = Vec::new();
if let Some(ref label) = self.base.base.label {
+ let (_, font_size) = crate::layout::control_label_font_parsed();
result.push((
TextLabel {
text: label.clone(),
x: self.base.base.x,
- y: self.base.base.y - (12.0 + crate::layout::label_margin()),
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
+ y: self.base.base.y,
+ font_size,
+ color: colors::control_label_color_u8(),
},
None,
None,
diff --git a/src/widget/container/vbox.rs b/src/widget/container/vbox.rs
new file mode 100644
index 0000000..d218ab0
--- /dev/null
+++ b/src/widget/container/vbox.rs
@@ -0,0 +1,66 @@
+use crate::widget::*;
+
+pub struct VBox {
+ pub base: Widget,
+ pub children: Vec<*mut (dyn Element + 'static)>,
+ pub parent: Option<*mut (dyn Element + 'static)>,
+ pub gap: f32,
+ pub margin: f32,
+}
+
+impl VBox {
+ pub fn new(gap: f32, margin: f32) -> Self {
+ Self {
+ base: Widget::new(),
+ children: Vec::new(),
+ parent: None,
+ gap,
+ margin,
+ }
+ }
+
+ pub fn add_child(&mut self, child: *mut (dyn Element + 'static)) {
+ self.children.push(child);
+ }
+}
+
+impl Element for VBox {
+ crate::impl_widget_base!(VBox);
+
+ fn color(&self) -> [f32; 4] {
+ [0.0, 0.0, 0.0, 0.0]
+ }
+
+ fn children(&self, _ctx: &UiContext) -> Vec<*mut (dyn Element + 'static)> {
+ self.children.clone()
+ }
+
+ fn parent(&self, _ctx: &UiContext) -> Option<*mut (dyn Element + 'static)> {
+ self.parent
+ }
+
+ fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>, _ctx: &mut UiContext) {
+ self.parent = parent;
+ }
+
+ fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
+ self.base.x = x;
+ self.base.y = y;
+ self.base.w = w;
+ self.base.h = h;
+
+ let mut current_y = y + self.margin;
+ let child_w = w - 2.0 * self.margin;
+
+ for &child_ptr in &self.children {
+ unsafe {
+ let preferred_h = (*child_ptr).preferred_height().unwrap_or(24.0);
+ let label_off = (*child_ptr).base().map_or(0.0, |b| b.label_offset());
+ let total_h = preferred_h + label_off;
+
+ (*child_ptr).set_rect(x + self.margin, current_y, child_w, preferred_h);
+ current_y += total_h + self.gap;
+ }
+ }
+ }
+}
diff --git a/src/widget/core.rs b/src/widget/core.rs
index ef84086..ab63a0d 100644
--- a/src/widget/core.rs
+++ b/src/widget/core.rs
@@ -756,8 +756,12 @@ impl Widget {
}
pub fn label_offset(&self) -> f32 {
+ if crate::layout::control_label_layout() == "side" {
+ return 0.0;
+ }
if self.label.is_some() {
- 12.0 + crate::layout::label_margin()
+ let (_, font_size) = crate::layout::control_label_font_detached_parsed();
+ font_size + crate::layout::control_label_margin()
} else {
0.0
}
diff --git a/src/widget/display/label.rs b/src/widget/display/label.rs
index 3a9a5ad..b0b9520 100644
--- a/src/widget/display/label.rs
+++ b/src/widget/display/label.rs
@@ -12,10 +12,11 @@ impl Label {
pub fn new(text: &str) -> Self {
let mut base = Widget::new();
base.label = Some(text.to_string());
+ let (_, font_size) = crate::layout::control_label_font_parsed();
Self {
base,
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
+ font_size,
+ color: colors::control_label_color_u8(),
}
}
diff --git a/src/widget/display/status_bar.rs b/src/widget/display/status_bar.rs
index fa5ca32..01a6104 100644
--- a/src/widget/display/status_bar.rs
+++ b/src/widget/display/status_bar.rs
@@ -94,7 +94,10 @@ impl Element for StatusBar {
fn color(&self) -> [f32; 4] {
if let Some(p_ptr) = self.parent {
if unsafe { (*p_ptr).is_backplate() } {
- return crate::colors::backplate_statusbar_color();
+ let theme_color = crate::colors::backplate_statusbar_color();
+ if theme_color[3] > 0.001 {
+ return theme_color;
+ }
}
}
self.bg_color.unwrap_or(colors::STATUS_BG)
diff --git a/src/widget/input/button.rs b/src/widget/input/button.rs
index ec36e6f..22fddff 100644
--- a/src/widget/input/button.rs
+++ b/src/widget/input/button.rs
@@ -258,7 +258,7 @@ impl Element for Button {
if self.selected { [230, 230, 242] }
else { [178, 178, 191] }
}
- _ => [0xcc, 0xcc, 0xd4]
+ _ => colors::control_label_color_u8()
}
};
let justify = if self.kind == ButtonKind::ListRow {
@@ -289,10 +289,37 @@ impl Element for Button {
self.selected = selected;
}
+ fn all_rounded_quads(&self, ctx: &UiContext) -> Vec<(f32, f32, f32, f32, f32, [f32; 4], (bool, bool, bool, bool))> {
+ let mut quads = Vec::new();
+ let (r1, r2, r3, r4) = self.rounded_corners();
+ if r1 || r2 || r3 || r4 {
+ let radius = self.corner_radius();
+ let c = self.color();
+ if let Some(bc) = colors::button_border_color() {
+ quads.push((self.base.x, self.base.y, self.base.w, self.base.h, radius, bc, (r1, r2, r3, r4)));
+ quads.push((self.base.x + 1.0, self.base.y + 1.0, self.base.w - 2.0, self.base.h - 2.0, (radius - 1.0).max(0.0), c, (r1, r2, r3, r4)));
+ } else {
+ if c[3].abs() > 0.001 {
+ quads.push((self.base.x, self.base.y, self.base.w, self.base.h, radius, c, (r1, r2, r3, r4)));
+ }
+ }
+ }
+ for &child_ptr in &self.children(ctx) {
+ let widget = unsafe { &*child_ptr };
+ quads.extend(widget.all_rounded_quads(ctx));
+ }
+ quads
+ }
+
fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
let mut quads = Vec::new();
if self.corner_radius() <= 0.0 {
- quads.push((self.base.x, self.base.y, self.base.w, self.base.h, self.color()));
+ if let Some(bc) = colors::button_border_color() {
+ quads.push((self.base.x, self.base.y, self.base.w, self.base.h, bc));
+ quads.push((self.base.x + 1.0, self.base.y + 1.0, self.base.w - 2.0, self.base.h - 2.0, self.color()));
+ } else {
+ quads.push((self.base.x, self.base.y, self.base.w, self.base.h, self.color()));
+ }
}
if let Some(ref svg) = self.svg {
let svg_x = self.base.x + (self.base.w - svg.w) / 2.0;
@@ -319,7 +346,7 @@ impl Element for Button {
if self.kind == ButtonKind::ListRow {
Some(crate::layout::list_font())
} else {
- Some(crate::layout::button_font())
+ Some(crate::layout::control_label_font())
}
}
diff --git a/src/widget/input/button_strip.rs b/src/widget/input/button_strip.rs
index aca3e20..0a740ed 100644
--- a/src/widget/input/button_strip.rs
+++ b/src/widget/input/button_strip.rs
@@ -15,6 +15,7 @@ pub struct ButtonStrip {
pub tab_quads_cache: std::collections::HashMap<String, Vec<(f32, f32, f32, f32, [f32; 4])>>,
pub last_padding: Option<f32>,
pub last_font: Option<String>,
+ pub last_scale: Option<f32>,
}
impl ButtonStrip {
@@ -31,6 +32,7 @@ impl ButtonStrip {
tab_quads_cache: std::collections::HashMap::new(),
last_padding: None,
last_font: None,
+ last_scale: None,
}
}
@@ -94,6 +96,7 @@ impl ButtonStrip {
let (font_fam, font_size) = crate::layout::button_strip_font_parsed();
let scale = crate::scale::scale_factor().max(1.0);
+ self.last_scale = Some(scale);
for (i, page_name) in self.buttons.iter().enumerate() {
let color = if self.selected == Some(i) {
@@ -262,7 +265,11 @@ impl Element for ButtonStrip {
let mut changed = false;
let current_padding = crate::layout::button_padding();
let current_font = crate::layout::button_strip_font();
- if self.last_padding != Some(current_padding) || self.last_font.as_ref() != Some(¤t_font) {
+ let current_scale = crate::scale::scale_factor().max(1.0);
+ if self.last_padding != Some(current_padding)
+ || self.last_font.as_ref() != Some(¤t_font)
+ || self.last_scale != Some(current_scale)
+ {
self.generate_rotated_labels();
changed = true;
}
diff --git a/src/widget/input/checkbox.rs b/src/widget/input/checkbox.rs
index b3f390c..285d5b7 100644
--- a/src/widget/input/checkbox.rs
+++ b/src/widget/input/checkbox.rs
@@ -156,14 +156,14 @@ impl Element for Checkbox {
fn text_labels(&self) -> Vec<TextLabel> {
let mut labels = Vec::new();
if let Some(ref label) = self.base.label {
- let font_size = 12.0;
+ let (_, font_size) = crate::layout::control_label_font_parsed();
let y = crate::layout::align_text_y(self.base.y, self.base.h, font_size, 0.0);
labels.push(TextLabel {
text: label.clone(),
x: self.base.x + 8.0,
y,
font_size,
- color: [0xcc, 0xcc, 0xd4],
+ color: colors::control_label_color_for_state(self.base.hovered, self.base.focused),
});
}
labels
@@ -274,7 +274,7 @@ impl Element for Toggle {
}
fn widget_font(&self) -> Option<String> {
- Some(crate::layout::toggle_font())
+ Some(crate::layout::control_label_font())
}
fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32, ctx: &mut UiContext) -> bool {
@@ -394,15 +394,14 @@ impl Element for Toggle {
fn text_labels(&self) -> Vec<TextLabel> {
let mut labels = Vec::new();
if let Some(ref label) = self.base.label {
- let font_size = 12.0;
- let font_fam = crate::layout::toggle_font_parsed().0;
+ let (font_fam, font_size) = crate::layout::control_label_font_parsed();
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,
y: crate::layout::align_text_y(self.base.y, self.base.h, font_size, 0.0),
font_size,
- color: [0xcc, 0xcc, 0xd4],
+ color: colors::control_label_color_for_state(self.base.hovered, self.base.focused),
});
}
labels
diff --git a/src/widget/input/dropdown.rs b/src/widget/input/dropdown.rs
index 85d8e91..d52b246 100644
--- a/src/widget/input/dropdown.rs
+++ b/src/widget/input/dropdown.rs
@@ -71,7 +71,7 @@ impl Dropdown {
pub fn popover_width(&self) -> f32 {
let mut w = self.base.w;
- let font_setting = crate::layout::dropdown_font();
+ let font_setting = crate::layout::control_label_font_detached();
let (font_family, font_size_opt) = crate::layout::parse_font_string(&font_setting);
let font_size = font_size_opt.unwrap_or(12.0);
for opt in &self.options {
@@ -88,8 +88,9 @@ impl Dropdown {
let rh = self.options.len() as f32 * 24.0;
let open_upward = self.open_upward.unwrap_or_else(|| self.base.y > 400.0);
+ let label_x = self.label_x_offset();
- let mut rx = self.base.x;
+ let mut rx = self.base.x + label_x;
let mut ry = if open_upward {
let label_offset = self.base.label_offset();
self.base.y + label_offset - rh
@@ -297,16 +298,21 @@ impl Element for Dropdown {
let border_color = if self.open {
[0.30, 0.50, 0.32, 1.0]
} else if self.base.hovered {
- [0.25, 0.25, 0.35, 1.0]
+ let bc = colors::dropdown_border_color();
+ [(bc[0] + 0.15).min(1.0), (bc[1] + 0.15).min(1.0), (bc[2] + 0.15).min(1.0), bc[3]]
} else {
- [0.18, 0.18, 0.24, 1.0]
+ colors::dropdown_border_color()
};
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
+
// Draw border
- quads.push((self.base.x, self.base.y + top, self.base.w, visual_h, radius, border_color, (r1, r2, r3, r4)));
+ quads.push((x, self.base.y + top, w, visual_h, radius, border_color, (r1, r2, r3, r4)));
// Draw background (slightly inset to show border)
let inner_radius = (radius - 1.0).max(0.0);
- quads.push((self.base.x + 1.0, self.base.y + top + 1.0, self.base.w - 2.0, visual_h - 2.0, inner_radius, bg_color, (r1, r2, r3, r4)));
+ quads.push((x + 1.0, self.base.y + top + 1.0, w - 2.0, visual_h - 2.0, inner_radius, bg_color, (r1, r2, r3, r4)));
}
for &child_ptr in &self.children(ctx) {
let widget = unsafe { &*child_ptr };
@@ -316,7 +322,7 @@ impl Element for Dropdown {
}
fn widget_font(&self) -> Option<String> {
- Some(crate::layout::dropdown_font())
+ Some(crate::layout::control_label_font_detached())
}
fn hit_test(&self, px: f32, py: f32, ctx: &UiContext) -> bool {
@@ -497,13 +503,18 @@ impl Element for Dropdown {
let border_color = if self.open {
[0.30, 0.50, 0.32, 1.0]
} else if self.base.hovered {
- [0.25, 0.25, 0.35, 1.0]
+ let bc = colors::dropdown_border_color();
+ [(bc[0] + 0.15).min(1.0), (bc[1] + 0.15).min(1.0), (bc[2] + 0.15).min(1.0), bc[3]]
} else {
- [0.18, 0.18, 0.24, 1.0]
+ colors::dropdown_border_color()
};
- quads.push((self.base.x, self.base.y + top, self.base.w, visual_h, border_color));
- quads.push((self.base.x + 1.0, self.base.y + top + 1.0, self.base.w - 2.0, visual_h - 2.0, bg_color));
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
+
+ quads.push((x, self.base.y + top, w, visual_h, border_color));
+ quads.push((x + 1.0, self.base.y + top + 1.0, w - 2.0, visual_h - 2.0, bg_color));
quads
}
@@ -523,12 +534,20 @@ impl Element for Dropdown {
self.options.get(self.selected).cloned().unwrap_or_default()
};
- let font_family = crate::layout::dropdown_font_parsed().0;
- let start_x = self.base.x + 8.0;
- let right_limit = self.base.x + self.base.w - 28.0; // 10px margin before the arrow
+ let font_family = crate::layout::control_label_font_detached_parsed().0;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
+ let start_x = x + 8.0;
+ let right_limit = x + w - 28.0; // 10px margin before the arrow
let fade_start_x = (right_limit - 24.0).max(start_x); // Fade out over the last 24px
let text_y = crate::layout::align_text_y(self.base.y, self.base.h, 12.0, top);
- let default_color = [0xdd, 0xdd, 0xe2];
+ let tc = colors::dropdown_text_color();
+ let default_color = [
+ (colors::linear_to_srgb(tc[0]) * 255.0).round() as u8,
+ (colors::linear_to_srgb(tc[1]) * 255.0).round() as u8,
+ (colors::linear_to_srgb(tc[2]) * 255.0).round() as u8,
+ ];
let bg_color = colors::dropdown_background_color();
let mut parent_color = colors::page_color();
if let Some(parent_ptr) = self.parent {
@@ -643,9 +662,13 @@ impl Element for Dropdown {
}
}
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
+
labels.push(TextLabel {
text: "â–¼".to_string(),
- x: self.base.x + self.base.w - 18.0,
+ x: x + w - 18.0,
y: crate::layout::align_text_y(self.base.y, self.base.h, 10.0, top),
font_size: 10.0,
color: [0x83, 0x83, 0x8a],
@@ -835,8 +858,14 @@ mod tests {
let first_char = &labels[0];
let last_char = &labels[last_char_idx];
- assert_eq!(first_char.color, [0xdd, 0xdd, 0xe2]);
- assert_ne!(last_char.color, [0xdd, 0xdd, 0xe2]); // color has shifted towards background
+ let tc = colors::dropdown_text_color();
+ let expected_color = [
+ (colors::linear_to_srgb(tc[0]) * 255.0).round() as u8,
+ (colors::linear_to_srgb(tc[1]) * 255.0).round() as u8,
+ (colors::linear_to_srgb(tc[2]) * 255.0).round() as u8,
+ ];
+ assert_eq!(first_char.color, expected_color);
+ assert_ne!(last_char.color, expected_color); // color has shifted towards background
}
}
diff --git a/src/widget/input/ramp.rs b/src/widget/input/ramp.rs
index 346752f..be7d8a8 100644
--- a/src/widget/input/ramp.rs
+++ b/src/widget/input/ramp.rs
@@ -808,18 +808,14 @@ impl Element for Ramp {
}
let mut quads = Vec::new();
- let track_x = self.base.x + 10.0;
- let track_w = self.base.w - 20.0;
- let ch = self.base.h - 20.0;
-
let border_color = colors::ramp_border_color();
let bg_color = [0.15, 0.15, 0.18, 1.0];
let radius = 6.0f32;
- // Draw container border (rounded quad)
- quads.push((track_x - 1.0, self.base.y + 10.0 - 1.0, track_w + 2.0, ch + 2.0, radius, border_color, (true, true, true, true)));
- // Draw container background (rounded quad)
- quads.push((track_x, self.base.y + 10.0, track_w, ch, (radius - 1.0).max(0.0), bg_color, (true, true, true, true)));
+ // Draw container border (rounded quad) using full widget bounds
+ quads.push((self.base.x - 1.0, self.base.y - 1.0, self.base.w + 2.0, self.base.h + 2.0, radius, border_color, (true, true, true, true)));
+ // Draw container background (rounded quad) using full widget bounds
+ quads.push((self.base.x, self.base.y, self.base.w, self.base.h, (radius - 1.0).max(0.0), bg_color, (true, true, true, true)));
// Extend children's rounded quads
for &child_ptr in &self.children(ctx) {
@@ -1013,13 +1009,16 @@ impl Element for Ramp {
(track_w - gap) / 2.0
};
+ let (_, font_size) = crate::layout::control_label_font_detached_parsed();
+ let label_color = colors::control_label_color_detached_u8();
+
labels.push((
TextLabel {
text: "Preset".to_string(),
x: track_x + 4.0,
y: sy - 2.0,
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
+ font_size,
+ color: label_color,
},
self.preset_dropdown.widget_font(),
None,
@@ -1030,8 +1029,8 @@ impl Element for Ramp {
text: "Line Type".to_string(),
x: track_x + col_w + gap + 4.0,
y: sy - 2.0,
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
+ font_size,
+ color: label_color,
},
self.line_type_dropdown.widget_font(),
None,
@@ -1043,8 +1042,8 @@ impl Element for Ramp {
text: "Value".to_string(),
x: track_x + 2.0 * (col_w + gap) + 4.0,
y: sy - 2.0,
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
+ font_size,
+ color: label_color,
},
self.val_slider.widget_font(),
None,
diff --git a/src/widget/input/slider.rs b/src/widget/input/slider.rs
index 626a54c..f6b31af 100644
--- a/src/widget/input/slider.rs
+++ b/src/widget/input/slider.rs
@@ -121,7 +121,7 @@ impl Element for Slider {
}
fn widget_font(&self) -> Option<String> {
- Some(crate::layout::slider_font())
+ Some(crate::layout::control_label_font_detached())
}
fn get_value_string(&self) -> Option<String> {
@@ -183,13 +183,16 @@ impl Element for Slider {
fn drag_update(&mut self, px: f32, _py: f32) -> bool {
let top = self.base.label_offset();
let visual_h = self.base.h - top;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
let (track_x, track_w) = if self.show_readout {
let readout_w = 60.0;
let gap = 8.0;
- let tw = (self.base.w - readout_w - gap).max(10.0);
- (self.base.x, tw)
+ let tw = (w - readout_w - gap).max(10.0);
+ (x, tw)
} else {
- (self.base.x, self.base.w)
+ (x, w)
};
let thumb_size = visual_h * 0.9;
let range = track_w - thumb_size;
@@ -213,13 +216,16 @@ impl Element for Slider {
self.dragging = true;
let top = self.base.label_offset();
let visual_h = self.base.h - top;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
let (track_x, track_w) = if self.show_readout {
let readout_w = 60.0;
let gap = 8.0;
- let tw = (self.base.w - readout_w - gap).max(10.0);
- (self.base.x, tw)
+ let tw = (w - readout_w - gap).max(10.0);
+ (x, tw)
} else {
- (self.base.x, self.base.w)
+ (x, w)
};
let thumb_size = visual_h * 0.9;
let thumb_x = track_x + self.value * (track_w - thumb_size);
@@ -273,10 +279,13 @@ impl Element for Slider {
let top = self.base.label_offset();
let visual_h = self.base.h - top;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
if self.show_readout {
let readout_w = 60.0;
- let rx = self.base.x + self.base.w - readout_w;
+ let rx = x + w - readout_w;
if px >= rx && px <= rx + readout_w && py >= self.base.y + top && py <= self.base.y + top + visual_h {
if state == ElementState::Pressed {
@@ -296,10 +305,10 @@ impl Element for Slider {
let (track_x, track_w) = if self.show_readout {
let readout_w = 60.0;
let gap = 8.0;
- let tw = (self.base.w - readout_w - gap).max(10.0);
- (self.base.x, tw)
+ let tw = (w - readout_w - gap).max(10.0);
+ (x, tw)
} else {
- (self.base.x, self.base.w)
+ (x, w)
};
let thumb_size = visual_h * 0.9;
let thumb_x = track_x + self.value * (track_w - thumb_size);
@@ -390,15 +399,18 @@ impl Element for Slider {
let mut quads = Vec::new();
let top = self.base.label_offset();
let visual_h = self.base.h - top;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
let (track_x, track_w) = if self.show_readout {
let readout_w = 60.0;
let gap = 8.0;
- let tw = (self.base.w - readout_w - gap).max(10.0);
+ let tw = (w - readout_w - gap).max(10.0);
- quads.push((self.base.x, self.base.y + top, tw, visual_h, colors::slider_track()));
+ quads.push((x, self.base.y + top, tw, visual_h, colors::slider_track()));
- let rx = self.base.x + self.base.w - readout_w;
+ let rx = x + w - readout_w;
let bg_color = if self.editing {
[0.06, 0.10, 0.18, 1.0]
} else {
@@ -415,19 +427,25 @@ impl Element for Slider {
quads.push((rx + readout_w - border_t, self.base.y + top, border_t, visual_h, border_color));
}
- (self.base.x, tw)
+ (x, tw)
} else {
- quads.push((self.base.x, self.base.y + top, self.base.w, visual_h, colors::slider_track()));
- (self.base.x, self.base.w)
+ quads.push((x, self.base.y + top, w, visual_h, colors::slider_track()));
+ (x, w)
};
let thumb_size = visual_h * 0.9;
let thumb_x = track_x + self.value * (track_w - thumb_size);
+
+ if let Some(fill_color) = colors::slider_fill() {
+ let fill_w = (thumb_x + thumb_size / 2.0 - track_x).max(0.0).min(track_w);
+ quads.push((track_x, self.base.y + top, fill_w, visual_h, fill_color));
+ }
+
let thumb_y = self.base.y + top + (visual_h - thumb_size) / 2.0;
let thumb_color = if self.dragging {
- colors::SLIDER_THUMB_DRAG
+ colors::slider_thumb_drag()
} else {
- colors::SLIDER_THUMB
+ colors::slider_thumb()
};
quads.push((thumb_x, thumb_y, thumb_size, thumb_size, thumb_color));
@@ -444,15 +462,18 @@ impl Element for Slider {
let top = self.base.label_offset();
let visual_h = self.base.h - top;
let radius = self.corner_radius();
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
let (track_x, track_w) = if self.show_readout {
let readout_w = 60.0;
let gap = 8.0;
- let tw = (self.base.w - readout_w - gap).max(10.0);
+ let tw = (w - readout_w - gap).max(10.0);
- quads.push((self.base.x, self.base.y + top, tw, visual_h, radius, colors::slider_track(), (r1, r2, r3, r4)));
+ quads.push((x, self.base.y + top, tw, visual_h, radius, colors::slider_track(), (r1, r2, r3, r4)));
- let rx = self.base.x + self.base.w - readout_w;
+ let rx = x + w - readout_w;
let bg_color = if self.editing {
[0.06, 0.10, 0.18, 1.0]
} else {
@@ -468,19 +489,25 @@ impl Element for Slider {
quads.push((rx, self.base.y + top, readout_w, visual_h, radius, bg_color, (r1, r2, r3, r4)));
}
- (self.base.x, tw)
+ (x, tw)
} else {
- quads.push((self.base.x, self.base.y + top, self.base.w, visual_h, radius, colors::slider_track(), (r1, r2, r3, r4)));
- (self.base.x, self.base.w)
+ quads.push((x, self.base.y + top, w, visual_h, radius, colors::slider_track(), (r1, r2, r3, r4)));
+ (x, w)
};
let thumb_size = visual_h * 0.9;
let thumb_x = track_x + self.value * (track_w - thumb_size);
+
+ if let Some(fill_color) = colors::slider_fill() {
+ let fill_w = (thumb_x + thumb_size / 2.0 - track_x).max(0.0).min(track_w);
+ quads.push((track_x, self.base.y + top, fill_w, visual_h, radius.min(visual_h / 2.0), fill_color, (true, true, true, true)));
+ }
+
let thumb_y = self.base.y + top + (visual_h - thumb_size) / 2.0;
let thumb_color = if self.dragging {
- colors::SLIDER_THUMB_DRAG
+ colors::slider_thumb_drag()
} else {
- colors::SLIDER_THUMB
+ colors::slider_thumb()
};
quads.push((thumb_x, thumb_y, thumb_size, thumb_size, thumb_size / 2.0, thumb_color, (true, true, true, true)));
@@ -491,6 +518,9 @@ impl Element for Slider {
let mut labels = Vec::new();
let top = self.base.label_offset();
let _visual_h = self.base.h - top;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
if let Some(lbl) = self.control_label() {
labels.push(lbl);
@@ -498,7 +528,7 @@ impl Element for Slider {
if self.show_readout {
let readout_w = 60.0;
- let rx = self.base.x + self.base.w - readout_w;
+ let rx = x + w - readout_w;
let ry = crate::layout::align_text_y(self.base.y, self.base.h, 12.0, top);
let text = if self.editing {
@@ -597,7 +627,7 @@ impl Element for RangeSlider {
}
fn rounded_corners(&self) -> (bool, bool, bool, bool) {
- let r = crate::layout::slider_corner_radius();
+ let r = crate::layout::rangeslider_corner_radius();
if r > 0.0 {
(true, true, true, true)
} else {
@@ -606,7 +636,7 @@ impl Element for RangeSlider {
}
fn corner_radius(&self) -> f32 {
- crate::layout::slider_corner_radius()
+ crate::layout::rangeslider_corner_radius()
}
fn draggable(&self) -> bool { true }
@@ -616,11 +646,14 @@ impl Element for RangeSlider {
let Some(active) = self.active_thumb else { return false; };
let top = self.base.label_offset();
let visual_h = self.base.h - top;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
let thumb_size = visual_h * 0.9;
- let range = self.base.w - thumb_size;
+ let range = w - thumb_size;
if range <= 0.0 { return false; }
- let new_val = ((px - self.drag_offset - self.base.x) / range).clamp(0.0, 1.0);
+ let new_val = ((px - self.drag_offset - x) / range).clamp(0.0, 1.0);
match active {
ActiveThumb::Low => {
let constrained = new_val.min(self.value_high);
@@ -643,10 +676,13 @@ impl Element for RangeSlider {
fn drag_begin(&mut self, px: f32, _py: f32) {
let top = self.base.label_offset();
let visual_h = self.base.h - top;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
let thumb_size = visual_h * 0.9;
- let range = self.base.w - thumb_size;
- let thumb_low_x = self.base.x + self.value_low * range;
- let thumb_high_x = self.base.x + self.value_high * range;
+ let range = w - thumb_size;
+ let thumb_low_x = x + self.value_low * range;
+ let thumb_high_x = x + self.value_high * range;
let center_low = thumb_low_x + thumb_size / 2.0;
let center_high = thumb_high_x + thumb_size / 2.0;
@@ -744,9 +780,12 @@ impl Element for RangeSlider {
let top = self.base.label_offset();
let visual_h = self.base.h - top;
let thumb_size = visual_h * 0.9;
- let range = self.base.w - thumb_size;
- let thumb_low_x = self.base.x + self.value_low * range;
- let thumb_high_x = self.base.x + self.value_high * range;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
+ let range = w - thumb_size;
+ let thumb_low_x = x + self.value_low * range;
+ let thumb_high_x = x + self.value_high * range;
let thumb_y = self.base.y + top + (visual_h - thumb_size) / 2.0;
@@ -757,19 +796,19 @@ impl Element for RangeSlider {
let highlight_h = visual_h * 0.3;
let low_color = if self.active_thumb == Some(ActiveThumb::Low) {
- colors::SLIDER_THUMB_DRAG
+ colors::rangeslider_thumb_drag()
} else {
- colors::SLIDER_THUMB
+ colors::rangeslider_thumb()
};
let high_color = if self.active_thumb == Some(ActiveThumb::High) {
- colors::SLIDER_THUMB_DRAG
+ colors::rangeslider_thumb_drag()
} else {
- colors::SLIDER_THUMB
+ colors::rangeslider_thumb()
};
vec![
- (self.base.x, self.base.y + top, self.base.w, visual_h, colors::rangeslider_track()),
+ (x, self.base.y + top, w, visual_h, colors::rangeslider_track()),
(highlight_x, highlight_y, highlight_w, highlight_h, colors::rangeslider_fill()),
(thumb_low_x, thumb_y, thumb_size, thumb_size, low_color),
(thumb_high_x, thumb_y, thumb_size, thumb_size, high_color),
@@ -788,9 +827,12 @@ impl Element for RangeSlider {
let radius = self.corner_radius();
let thumb_size = visual_h * 0.9;
- let range = self.base.w - thumb_size;
- let thumb_low_x = self.base.x + self.value_low * range;
- let thumb_high_x = self.base.x + self.value_high * range;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
+ let range = w - thumb_size;
+ let thumb_low_x = x + self.value_low * range;
+ let thumb_high_x = x + self.value_high * range;
let thumb_y = self.base.y + top + (visual_h - thumb_size) / 2.0;
@@ -801,19 +843,19 @@ impl Element for RangeSlider {
let highlight_h = visual_h * 0.3;
let low_color = if self.active_thumb == Some(ActiveThumb::Low) {
- colors::SLIDER_THUMB_DRAG
+ colors::rangeslider_thumb_drag()
} else {
- colors::SLIDER_THUMB
+ colors::rangeslider_thumb()
};
let high_color = if self.active_thumb == Some(ActiveThumb::High) {
- colors::SLIDER_THUMB_DRAG
+ colors::rangeslider_thumb_drag()
} else {
- colors::SLIDER_THUMB
+ colors::rangeslider_thumb()
};
// Track background
- quads.push((self.base.x, self.base.y + top, self.base.w, visual_h, radius, colors::rangeslider_track(), (r1, r2, r3, r4)));
+ quads.push((x, self.base.y + top, w, visual_h, radius, colors::rangeslider_track(), (r1, r2, r3, r4)));
// Progress fill (highlight track)
quads.push((highlight_x, highlight_y, highlight_w, highlight_h, radius.min(highlight_h / 2.0), colors::rangeslider_fill(), (true, true, true, true)));
// Low thumb
diff --git a/src/widget/input/spinbox.rs b/src/widget/input/spinbox.rs
index 1dcb672..b319ef9 100644
--- a/src/widget/input/spinbox.rs
+++ b/src/widget/input/spinbox.rs
@@ -144,7 +144,7 @@ impl Element for Spinbox {
fn color(&self) -> [f32; 4] { [0.0, 0.0, 0.0, 0.0] }
fn value(&self) -> i32 { self.value }
- fn widget_font(&self) -> Option<String> { Some(crate::layout::spinbox_font()) }
+ fn widget_font(&self) -> Option<String> { Some(crate::layout::control_label_font_detached()) }
fn on_cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
let was = self.base.hovered;
@@ -160,11 +160,14 @@ impl Element for Spinbox {
let p = crate::layout::spinbox_button_padding();
let btn_y = self.base.y + top + p;
let btn_h = (visual_h - 2.0 * p).max(0.0);
- let split_dec = self.base.x + self.base.w * 0.55;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
+ let split_dec = x + w * 0.55;
let in_y = py >= btn_y && py < btn_y + btn_h;
- let hd = in_y && px >= split_dec + p && px < self.base.x + self.base.w * 0.775;
- let hi = in_y && px >= self.base.x + self.base.w * 0.775 && px < self.base.x + self.base.w - p;
+ let hd = in_y && px >= split_dec + p && px < x + w * 0.775;
+ let hi = in_y && px >= x + w * 0.775 && px < x + w - p;
let changed = hd != self.hover_dec || hi != self.hover_inc;
self.hover_dec = hd;
self.hover_inc = hi;
@@ -187,17 +190,20 @@ impl Element for Spinbox {
let p = crate::layout::spinbox_button_padding();
let btn_y = self.base.y + top + p;
let btn_h = (visual_h - 2.0 * p).max(0.0);
- let split_dec = self.base.x + self.base.w * 0.55;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
+ let split_dec = x + w * 0.55;
let in_y = py >= btn_y && py < btn_y + btn_h;
- if in_y && px >= split_dec + p && px < self.base.x + self.base.w * 0.775 {
+ if in_y && px >= split_dec + p && px < x + w * 0.775 {
let old_val = self.value;
self.value = (self.value - self.step).max(self.min);
if self.value != old_val {
self.just_changed = true;
}
true
- } else if in_y && px >= self.base.x + self.base.w * 0.775 && px < self.base.x + self.base.w - p {
+ } else if in_y && px >= x + w * 0.775 && px < x + w - p {
let old_val = self.value;
self.value = (self.value + self.step).min(self.max);
if self.value != old_val {
@@ -213,7 +219,7 @@ impl Element for Spinbox {
self.edit_buffer = self.value.to_string();
}
let char_width = 8.4;
- let click_idx = (((px - (self.base.x + 4.0)) / char_width).round() as isize)
+ let click_idx = (((px - (x + 4.0)) / char_width).round() as isize)
.max(0)
.min(self.edit_buffer.chars().count() as isize) as usize;
self.cursor_idx = click_idx;
@@ -350,10 +356,13 @@ impl Element for Spinbox {
let top = self.base.label_offset();
let visual_h = self.base.h - top;
- let display_w = self.base.w * 0.55;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
+ let display_w = w * 0.55;
let p = crate::layout::spinbox_button_padding();
- let split_dec = self.base.x + display_w;
+ let split_dec = x + display_w;
let inc_col = if self.hover_inc { colors::spinbox_button_hover() } else { colors::spinbox_button() };
let dec_col = if self.hover_dec { colors::spinbox_button_hover() } else { colors::spinbox_button() };
@@ -363,11 +372,11 @@ impl Element for Spinbox {
colors::spinbox_display()
};
- quads.push((self.base.x, self.base.y + top, self.base.w, visual_h, display_bg));
+ quads.push((x, self.base.y + top, w, visual_h, display_bg));
let btn_y = self.base.y + top + p;
let btn_h = (visual_h - 2.0 * p).max(0.0);
- let pair_w = (self.base.w * 0.45 - 2.0 * p).max(0.0);
+ let pair_w = (w * 0.45 - 2.0 * p).max(0.0);
let btn_w_padded = pair_w / 2.0;
if btn_h > 0.0 && btn_w_padded > 0.0 {
@@ -377,14 +386,14 @@ impl Element for Spinbox {
if self.editing {
let border_color = [0.20, 0.50, 0.85, 1.0];
- quads.push((self.base.x, self.base.y + top, self.base.w, 1.0, border_color));
- quads.push((self.base.x, self.base.y + top + visual_h - 1.0, self.base.w, 1.0, border_color));
- quads.push((self.base.x, self.base.y + top, 1.0, visual_h, border_color));
- quads.push((self.base.x + self.base.w - 1.0, self.base.y + top, 1.0, visual_h, border_color));
+ quads.push((x, self.base.y + top, w, 1.0, border_color));
+ quads.push((x, self.base.y + top + visual_h - 1.0, w, 1.0, border_color));
+ quads.push((x, self.base.y + top, 1.0, visual_h, border_color));
+ quads.push((x + w - 1.0, self.base.y + top, 1.0, visual_h, border_color));
let char_width = 8.4;
- let cursor_x = self.base.x + 4.0 + (self.cursor_idx as f32 * char_width);
- let max_cursor_x = self.base.x + display_w - 4.0;
+ let cursor_x = x + 4.0 + (self.cursor_idx as f32 * char_width);
+ let max_cursor_x = x + display_w - 4.0;
let final_cursor_x = cursor_x.min(max_cursor_x);
let cursor_y = self.base.y + top + (visual_h - 14.0) / 2.0;
quads.push((final_cursor_x, cursor_y, 1.5, 14.0, [0.80, 0.80, 0.85, 1.0]));
@@ -411,7 +420,7 @@ impl Element for Spinbox {
colors::spinbox_display()
};
- let display_w = self.base.w * 0.55;
+
let border_color = if self.editing {
[0.20, 0.50, 0.85, 1.0]
} else if self.base.hovered {
@@ -420,17 +429,22 @@ impl Element for Spinbox {
[0.18, 0.18, 0.24, 1.0]
};
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
+ let display_w = w * 0.55;
+
// Draw display border (outer) and background (inner)
- quads.push((self.base.x, self.base.y + top, self.base.w, visual_h, radius, border_color, (r1, r2, r3, r4)));
- quads.push((self.base.x + 1.0, self.base.y + top + 1.0, self.base.w - 2.0, visual_h - 2.0, radius - 1.0, display_bg, (r1, r2, r3, r4)));
+ quads.push((x, self.base.y + top, w, visual_h, radius, border_color, (r1, r2, r3, r4)));
+ quads.push((x + 1.0, self.base.y + top + 1.0, w - 2.0, visual_h - 2.0, radius - 1.0, display_bg, (r1, r2, r3, r4)));
// Increment/decrement buttons
let p = crate::layout::spinbox_button_padding();
let btn_y = self.base.y + top + p;
let btn_h = (visual_h - 2.0 * p).max(0.0);
- let pair_w = (self.base.w * 0.45 - 2.0 * p).max(0.0);
+ let pair_w = (w * 0.45 - 2.0 * p).max(0.0);
let btn_w_padded = pair_w / 2.0;
- let split_dec = self.base.x + display_w;
+ let split_dec = x + display_w;
let inc_col = if self.hover_inc { colors::spinbox_button_hover() } else { colors::spinbox_button() };
let dec_col = if self.hover_dec { colors::spinbox_button_hover() } else { colors::spinbox_button() };
@@ -444,8 +458,8 @@ impl Element for Spinbox {
// Draw cursor if editing and rounded
if self.editing {
let char_width = 8.4;
- let cursor_x = self.base.x + 4.0 + (self.cursor_idx as f32 * char_width);
- let max_cursor_x = self.base.x + display_w - 4.0;
+ let cursor_x = x + 4.0 + (self.cursor_idx as f32 * char_width);
+ let max_cursor_x = x + display_w - 4.0;
let final_cursor_x = cursor_x.min(max_cursor_x);
let cursor_y = self.base.y + top + (visual_h - 14.0) / 2.0;
quads.push((final_cursor_x, cursor_y, 1.5, 14.0, 0.0, [0.80, 0.80, 0.85, 1.0], (false, false, false, false)));
@@ -469,14 +483,16 @@ impl Element for Spinbox {
};
let top = self.base.label_offset();
- let _visual_h = self.base.h - top;
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
let tc = colors::spinbox_text_color();
let text_color_u8 = [(tc[0]*255.0) as u8, (tc[1]*255.0) as u8, (tc[2]*255.0) as u8];
labels.push(TextLabel {
text: value_text,
- x: self.base.x + 4.0,
+ x: x + 4.0,
y: crate::layout::align_text_y(self.base.y, self.base.h, 14.0, top),
font_size: 14.0,
color: text_color_u8,
@@ -484,7 +500,7 @@ impl Element for Spinbox {
if let Some(ref unit) = self.unit {
labels.push(TextLabel {
text: unit.clone(),
- x: self.base.x + 4.0 + 36.0,
+ x: x + 4.0 + 36.0,
y: crate::layout::align_text_y(self.base.y, self.base.h, 11.0, top),
font_size: 11.0,
color: [0x73, 0x73, 0x7a],
@@ -492,8 +508,8 @@ impl Element for Spinbox {
}
let p = crate::layout::spinbox_button_padding();
- let split_dec = self.base.x + self.base.w * 0.55;
- let pair_w = (self.base.w * 0.45 - 2.0 * p).max(0.0);
+ let split_dec = x + w * 0.55;
+ let pair_w = (w * 0.45 - 2.0 * p).max(0.0);
let btn_w_padded = pair_w / 2.0;
if btn_w_padded > 0.0 {
diff --git a/src/widget/input/text_box.rs b/src/widget/input/text_box.rs
index ec630b2..4c32a8b 100644
--- a/src/widget/input/text_box.rs
+++ b/src/widget/input/text_box.rs
@@ -50,7 +50,7 @@ pub struct TextBox {
impl TextBox {
pub fn new(text: String) -> Self {
- let (style_family, style_size) = crate::layout::textbox_font_parsed();
+ let (style_family, style_size) = crate::layout::control_label_font_detached_parsed();
let editor_state = TextEditorState::new(text.clone());
Self {
base: Widget::new(),
@@ -99,7 +99,8 @@ impl TextBox {
}
fn map_x_to_idx(&self, click_x: f32) -> usize {
- let relative_x = click_x - (self.base.x + 8.0) + self.scroll_x;
+ let label_x = self.label_x_offset();
+ let relative_x = click_x - (self.base.x + label_x + 8.0) + self.scroll_x;
if self.glyph_positions.is_empty() {
let char_width = self.char_width();
return ((relative_x / char_width).round() as isize)
@@ -497,7 +498,7 @@ impl Element for TextBox {
crate::impl_widget_base!(TextBox);
fn prepare_text(&mut self, fs: &mut glyphon::FontSystem) {
- let (style_family, style_size) = crate::layout::textbox_font_parsed();
+ let (style_family, style_size) = crate::layout::control_label_font_detached_parsed();
if self.font_size == self.default_font_size {
self.font_size = style_size;
}
@@ -721,7 +722,7 @@ impl Element for TextBox {
fn draggable(&self) -> bool { !self.disabled }
fn is_dragging(&self) -> bool { self.dragging }
- fn widget_font(&self) -> Option<String> { Some(crate::layout::textbox_font()) }
+ fn widget_font(&self) -> Option<String> { Some(crate::layout::control_label_font_detached()) }
fn drag_begin(&mut self, _px: f32, _py: f32) {
if self.disabled || !self.editing { return; }
@@ -784,16 +785,17 @@ impl Element for TextBox {
} else {
let char_width = self.char_width();
let top = self.base.label_offset();
+ let label_x = self.label_x_offset();
let idx = if self.multiline {
let line_height = self.line_height();
let max_chars = if self.line_wrap_enabled() {
- (((self.base.w - 16.0) / char_width).floor() as usize).max(1)
+ ((((self.base.w - label_x) - 16.0) / char_width).floor() as usize).max(1)
} else {
999999
};
let (lines, index_map) = self.wrap_text(max_chars);
let click_line = (((py - (self.base.y + top + 8.0) + self.scroll_y) / line_height).floor() as isize).max(0) as usize;
- let click_col = (((px - (self.base.x + 8.0) + self.scroll_x) / char_width).round() as isize).max(0) as usize;
+ let click_col = (((px - (self.base.x + label_x + 8.0) + self.scroll_x) / char_width).round() as isize).max(0) as usize;
self.map_2d_to_1d(&index_map, click_line, click_col, lines.len() - 1)
} else {
self.map_x_to_idx(px)
@@ -1178,10 +1180,14 @@ impl Element for TextBox {
[0.18, 0.18, 0.24, 1.0]
};
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
+
if self.draw_bg_border {
let border_w = self.border_width();
- quads.push((self.base.x, self.base.y + top, self.base.w, visual_h, radius, border_color, (r1, r2, r3, r4)));
- quads.push((self.base.x + border_w, self.base.y + top + border_w, self.base.w - 2.0 * border_w, visual_h - 2.0 * border_w, (radius - border_w).max(0.0), bg_color, (r1, r2, r3, r4)));
+ quads.push((x, self.base.y + top, w, visual_h, radius, border_color, (r1, r2, r3, r4)));
+ quads.push((x + border_w, self.base.y + top + border_w, w - 2.0 * border_w, visual_h - 2.0 * border_w, (radius - border_w).max(0.0), bg_color, (r1, r2, r3, r4)));
}
if self.editing || self.select_anchor.is_some() {
@@ -1200,7 +1206,7 @@ impl Element for TextBox {
if self.multiline {
let max_chars = if self.line_wrap_enabled() {
- (((self.base.w - 16.0) / char_width).floor() as usize).max(1)
+ (((w - 16.0) / char_width).floor() as usize).max(1)
} else {
999999
};
@@ -1230,13 +1236,13 @@ impl Element for TextBox {
}
}
if let (Some(sc), Some(ec)) = (line_start_col, line_end_col) {
- let highlight_x = self.base.x + 8.0 + (sc as f32 * char_width) - self.scroll_x;
+ let highlight_x = x + 8.0 + (sc as f32 * char_width) - self.scroll_x;
let highlight_w = (ec - sc + 1) as f32 * char_width;
let highlight_y = self.base.y + top + 8.0 + (line_idx as f32 * line_height) - self.scroll_y;
let clipped_y = highlight_y.max(view_top);
let clipped_bottom = (highlight_y + line_height).min(view_bottom);
- let h_left = highlight_x.max(self.base.x + 8.0);
- let h_right = (highlight_x + highlight_w).min(self.base.x + self.base.w - 8.0);
+ let h_left = highlight_x.max(x + 8.0);
+ let h_right = (highlight_x + highlight_w).min(x + w - 8.0);
if h_left < h_right && clipped_y < clipped_bottom {
quads.push((h_left, clipped_y, h_right - h_left, clipped_bottom - clipped_y, 0.0, highlight_color, (false, false, false, false)));
}
@@ -1247,11 +1253,11 @@ impl Element for TextBox {
if self.editing {
let caret_h = self.font_size * 1.15;
let (cursor_l, cursor_c) = index_map[self.cursor_idx.min(index_map.len() - 1)];
- let cursor_x = self.base.x + 8.0 + (cursor_c as f32 * char_width) - self.scroll_x;
+ let cursor_x = x + 8.0 + (cursor_c as f32 * char_width) - self.scroll_x;
let cursor_y = self.base.y + top + 8.0 + (cursor_l as f32 * line_height) + (line_height - caret_h) / 2.0 - self.scroll_y;
let clipped_y = cursor_y.max(view_top);
let clipped_bottom = (cursor_y + caret_h).min(view_bottom);
- if cursor_x >= self.base.x + 8.0 && cursor_x <= self.base.x + self.base.w - 8.0 {
+ if cursor_x >= x + 8.0 && cursor_x <= x + w - 8.0 {
if clipped_y < clipped_bottom {
quads.push((cursor_x, clipped_y, 1.5, clipped_bottom - clipped_y, 0.0, cursor_color, (false, false, false, false)));
}
@@ -1262,9 +1268,9 @@ impl Element for TextBox {
if start != end {
let h_left_offset = self.glyph_positions.get(start).copied().unwrap_or_else(|| start as f32 * char_width);
let h_right_offset = self.glyph_positions.get(end).copied().unwrap_or_else(|| end as f32 * char_width);
- let highlight_x = self.base.x + 8.0 + h_left_offset - self.scroll_x;
- let h_left = highlight_x.max(self.base.x + 8.0);
- let h_right = (self.base.x + 8.0 + h_right_offset - self.scroll_x).min(self.base.x + self.base.w - 8.0);
+ let highlight_x = x + 8.0 + h_left_offset - self.scroll_x;
+ let h_left = highlight_x.max(x + 8.0);
+ let h_right = (x + 8.0 + h_right_offset - self.scroll_x).min(x + w - 8.0);
if h_left < h_right {
quads.push((
h_left,
@@ -1284,8 +1290,8 @@ impl Element for TextBox {
} else {
self.cursor_x_offset
};
- let cursor_x = self.base.x + 8.0 + offset - self.scroll_x;
- if cursor_x >= self.base.x + 8.0 && cursor_x <= self.base.x + self.base.w - 8.0 {
+ let cursor_x = x + 8.0 + offset - self.scroll_x;
+ if cursor_x >= x + 8.0 && cursor_x <= x + w - 8.0 {
let text_y = crate::layout::align_text_y(self.base.y, self.base.h, self.font_size, top);
let cursor_y = text_y + (self.font_size - caret_h) / 2.0;
quads.push((cursor_x, cursor_y, 1.5, caret_h, 0.0, cursor_color, (false, false, false, false)));
@@ -1338,11 +1344,15 @@ impl Element for TextBox {
[0xcc, 0xcc, 0xd4]
};
+ let label_x = self.label_x_offset();
+ let x = self.base.x + label_x;
+ let w = self.base.w - label_x;
+
if self.multiline {
let char_width = self.char_width();
let line_height = self.line_height();
let max_chars = if self.line_wrap_enabled() {
- (((self.base.w - 16.0) / char_width).floor() as usize).max(1)
+ (((w - 16.0) / char_width).floor() as usize).max(1)
} else {
999999
};
@@ -1372,7 +1382,7 @@ impl Element for TextBox {
for (line_idx, line_text) in lines_to_draw.iter().enumerate() {
labels.push(TextLabel {
text: line_text.clone(),
- x: self.base.x + 8.0 - self.scroll_x,
+ x: x + 8.0 - self.scroll_x,
y: self.base.y + top + 8.0 + (line_idx as f32 * line_height) + (line_height - self.font_size) / 2.0 - self.scroll_y,
font_size: self.font_size,
color: label_color,
@@ -1381,7 +1391,7 @@ impl Element for TextBox {
} else {
labels.push(TextLabel {
text: display_text,
- x: self.base.x + 8.0 - self.scroll_x,
+ x: x + 8.0 - self.scroll_x,
y: crate::layout::align_text_y(self.base.y, self.base.h, self.font_size, top),
font_size: self.font_size,
color: label_color,
@@ -1391,13 +1401,15 @@ impl Element for TextBox {
}
fn text_labels_with_bounds(&self, _ctx: &UiContext) -> Vec<(TextLabel, Option<[f32; 4]>)> {
- let bounds = Some([self.base.x, self.base.y, self.base.x + self.base.w, self.base.y + self.base.h]);
+ let label_x = self.label_x_offset();
+ let bounds = Some([self.base.x + label_x, self.base.y, self.base.x + self.base.w, self.base.y + self.base.h]);
self.text_labels().into_iter().map(|l| (l, bounds)).collect()
}
fn text_labels_with_font_and_bounds(&self, _ctx: &UiContext) -> Vec<(TextLabel, Option<String>, Option<[f32; 4]>)> {
let font = self.widget_font();
- let bounds = Some([self.base.x, self.base.y, self.base.x + self.base.w, self.base.y + self.base.h]);
+ let label_x = self.label_x_offset();
+ let bounds = Some([self.base.x + label_x, self.base.y, self.base.x + self.base.w, self.base.y + self.base.h]);
self.text_labels().into_iter().map(|l| (l, font.clone(), bounds)).collect()
}
diff --git a/src/widget/input/trackpad.rs b/src/widget/input/trackpad.rs
index dcf8c74..33a5fcc 100644
--- a/src/widget/input/trackpad.rs
+++ b/src/widget/input/trackpad.rs
@@ -31,8 +31,12 @@ impl Trackpad {
}
pub fn label_offset(&self) -> f32 {
+ if crate::layout::control_label_layout() == "side" {
+ return 0.0;
+ }
if self.base.label.is_some() {
- 12.0 + crate::layout::label_margin()
+ let (_, font_size) = crate::layout::control_label_font_detached_parsed();
+ font_size + crate::layout::control_label_margin()
} else {
0.0
}
@@ -42,30 +46,42 @@ impl Trackpad {
impl Element for Trackpad {
crate::impl_widget_base!(Trackpad);
+ fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
+ self.base.x = x;
+ self.base.y = y;
+ self.base.w = w;
+ self.base.h = h + self.label_offset();
+ }
+
fn color(&self) -> [f32; 4] {
[0.11, 0.11, 0.16, 0.85]
}
fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
- let (x, y, w, h) = self.rect();
+ let (rx_rect, y, rw_rect, h) = self.rect();
+ let label_x = self.label_x_offset();
+ let x = rx_rect + label_x;
+ let w = rw_rect - label_x;
+ let top = self.label_offset();
+ let visual_h = h - top;
let mut quads = Vec::new();
// 1. Background
- quads.push((x, y, w, h, [0.11, 0.11, 0.16, 0.85]));
+ quads.push((x, y + top, w, visual_h, [0.11, 0.11, 0.16, 0.85]));
// 2. Borders
let border_color = [0.28, 0.28, 0.38, 1.0];
- quads.push((x, y, w, 1.0, border_color)); // Top
- quads.push((x, y + h - 1.0, w, 1.0, border_color)); // Bottom
- quads.push((x, y, 1.0, h, border_color)); // Left
- quads.push((x + w - 1.0, y, 1.0, h, border_color)); // Right
+ quads.push((x, y + top, w, 1.0, border_color)); // Top
+ quads.push((x, y + top + visual_h - 1.0, w, 1.0, border_color)); // Bottom
+ quads.push((x, y + top, 1.0, visual_h, border_color)); // Left
+ quads.push((x + w - 1.0, y + top, 1.0, visual_h, border_color)); // Right
// 3. Fingers
for finger in &self.fingers {
let rx = finger.x.clamp(0.0, 1.0);
let ry = finger.y.clamp(0.0, 1.0);
let fx = x + rx * w;
- let fy = y + ry * h;
+ let fy = y + top + ry * visual_h;
let dot_size = 12.0;
// Render glow (outer light blue rectangle)
@@ -90,26 +106,32 @@ impl Element for Trackpad {
}
fn text_labels(&self) -> Vec<TextLabel> {
- let (x, y, _w, h) = self.rect();
+ let (rx_rect, y, rw_rect, h) = self.rect();
+ let label_x = self.label_x_offset();
+ let x = rx_rect + label_x;
+ let _w = rw_rect - label_x;
+ let top = self.label_offset();
+ let visual_h = h - top;
let mut labels = Vec::new();
// Render "Touchpad Area" label
labels.push(TextLabel {
text: "Touchpad Area".to_string(),
x: x + 12.0,
- y: y + h - 22.0,
+ y: y + top + visual_h - 22.0,
font_size: 11.0,
color: [0x73, 0x73, 0x8c],
});
// Optional widget-base label on top
if let Some(ref label) = self.base.label {
+ let (_, font_size) = crate::layout::control_label_font_detached_parsed();
labels.push(TextLabel {
text: label.clone(),
- x,
- y: y - (12.0 + crate::layout::label_margin()),
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
+ x: rx_rect,
+ y,
+ font_size,
+ color: colors::control_label_color_detached_for_state(self.base.hovered, self.base.focused),
});
}
@@ -120,19 +142,29 @@ impl Element for Trackpad {
fn is_dragging(&self) -> bool { !self.fingers.is_empty() }
fn drag_begin(&mut self, px: f32, py: f32) {
- let (x, y, w, h) = self.rect();
- if w > 0.0 && h > 0.0 {
+ let (rx_rect, y, rw_rect, h) = self.rect();
+ let label_x = self.label_x_offset();
+ let x = rx_rect + label_x;
+ let w = rw_rect - label_x;
+ let top = self.label_offset();
+ let visual_h = h - top;
+ if w > 0.0 && visual_h > 0.0 {
let rx = ((px - x) / w).clamp(0.0, 1.0);
- let ry = ((py - y) / h).clamp(0.0, 1.0);
+ let ry = ((py - (y + top)) / visual_h).clamp(0.0, 1.0);
self.fingers = vec![Finger { slot: 0, x: rx, y: ry }];
}
}
fn drag_update(&mut self, px: f32, py: f32) -> bool {
- let (x, y, w, h) = self.rect();
- if w > 0.0 && h > 0.0 {
+ let (rx_rect, y, rw_rect, h) = self.rect();
+ let label_x = self.label_x_offset();
+ let x = rx_rect + label_x;
+ let w = rw_rect - label_x;
+ let top = self.label_offset();
+ let visual_h = h - top;
+ if w > 0.0 && visual_h > 0.0 {
let rx = ((px - x) / w).clamp(0.0, 1.0);
- let ry = ((py - y) / h).clamp(0.0, 1.0);
+ let ry = ((py - (y + top)) / visual_h).clamp(0.0, 1.0);
self.fingers = vec![Finger { slot: 0, x: rx, y: ry }];
true
} else {
@@ -146,11 +178,16 @@ impl Element for Trackpad {
fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32, _ctx: &mut UiContext) -> bool {
if button == MouseButton::Left {
- let (x, y, w, h) = self.rect();
- if px >= x && px <= x + w && py >= y && py <= y + h {
+ let (rx_rect, y, rw_rect, h) = self.rect();
+ let label_x = self.label_x_offset();
+ let x = rx_rect + label_x;
+ let w = rw_rect - label_x;
+ let top = self.label_offset();
+ let visual_h = h - top;
+ if px >= x && px <= x + w && py >= y + top && py <= y + top + visual_h {
if state == ElementState::Pressed {
let rx = ((px - x) / w).clamp(0.0, 1.0);
- let ry = ((py - y) / h).clamp(0.0, 1.0);
+ let ry = ((py - (y + top)) / visual_h).clamp(0.0, 1.0);
self.fingers = vec![Finger { slot: 0, x: rx, y: ry }];
return true;
} else {
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index 92c73d5..2011bad 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -332,7 +332,7 @@ pub trait Element {
if w <= 0.0 || h <= 0.0 {
return false;
}
- let (hx, hw) = if let Some(b) = self.base() {
+ let (mut hx, mut hw) = if let Some(b) = self.base() {
if b.row_w > 0.0 {
(b.row_x, b.row_w)
} else {
@@ -341,6 +341,9 @@ pub trait Element {
} else {
(x, w)
};
+ let label_x = self.label_x_offset();
+ hx += label_x;
+ hw -= label_x;
px >= hx && px <= hx + hw && py >= y && py <= y + h
}
@@ -409,13 +412,14 @@ pub trait Element {
fn highlight_quad(&self, ctx: &UiContext) -> Option<(f32, f32, f32, f32, [f32; 4])> {
let hc = self.highlight_color(ctx)?;
+ let label_x = self.label_x_offset();
if let Some(b) = self.base() {
- let hx = if b.row_w > 0.0 { b.row_x } else { b.x };
- let hw = if b.row_w > 0.0 { b.row_w } else { b.w };
+ let hx = if b.row_w > 0.0 { b.row_x } else { b.x } + label_x;
+ let hw = if b.row_w > 0.0 { b.row_w } else { b.w } - label_x;
Some((hx, b.y, hw, b.h, hc))
} else {
let (x, y, w, h) = self.rect();
- Some((x, y, w, h, hc))
+ Some((x + label_x, y, w - label_x, h, hc))
}
}
@@ -430,6 +434,18 @@ pub trait Element {
fn take_click(&mut self) -> bool { false }
fn draggable(&self) -> bool { false }
+ fn label_x_offset(&self) -> f32 {
+ let name = self.type_name();
+ if name == "Label" || name == "Button" || name == "Checkbox" || name == "Toggle" || name == "Plate" || name == "Ramp" {
+ return 0.0;
+ }
+ if crate::layout::control_label_layout() == "side" && self.base().map_or(false, |b| b.label.is_some()) {
+ 90.0
+ } else {
+ 0.0
+ }
+ }
+
fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> { Vec::new() }
fn extra_arcs(&self) -> Vec<(f32, f32, f32, f32, f32, f32, [f32; 4])> { Vec::new() }
fn extra_circles(&self) -> Vec<(f32, f32, f32, [f32; 4])> { Vec::new() }
@@ -476,12 +492,27 @@ pub trait Element {
fn text_labels(&self) -> Vec<TextLabel> {
if let Some(b) = self.base() {
if let Some(ref label) = b.label {
+ let (_, font_size) = crate::layout::control_label_font_detached_parsed();
+ let color = colors::control_label_color_detached_for_state(b.hovered, b.focused);
+ if crate::layout::control_label_layout() == "side" {
+ let label_x = self.label_x_offset();
+ if label_x > 0.0 {
+ let y_pos = crate::layout::align_text_y(b.y, b.h, font_size, 0.0);
+ return vec![TextLabel {
+ text: label.clone(),
+ x: b.x + 4.0,
+ y: y_pos,
+ font_size,
+ color,
+ }];
+ }
+ }
return vec![TextLabel {
text: label.clone(),
x: b.x,
y: b.y,
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
+ font_size,
+ color,
}];
}
}
@@ -649,19 +680,31 @@ pub trait Control: Element {
let label = b.label.as_ref()?;
let name = self.type_name();
- let x_offset = if name == "Slider" || name == "RangeSlider" {
- 0.0
+ let (_, font_size) = crate::layout::control_label_font_detached_parsed();
+ let color = colors::control_label_color_detached_for_state(b.hovered, b.focused);
+ if crate::layout::control_label_layout() == "side" {
+ let y_pos = crate::layout::align_text_y(b.y, b.h, font_size, 0.0);
+ Some(TextLabel {
+ text: label.clone(),
+ x: b.x + 4.0,
+ y: y_pos,
+ font_size,
+ color,
+ })
} else {
- 4.0
- };
-
- Some(TextLabel {
- text: label.clone(),
- x: b.x + x_offset,
- y: b.y,
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
- })
+ let x_offset = if name == "Slider" || name == "RangeSlider" {
+ 0.0
+ } else {
+ 4.0
+ };
+ Some(TextLabel {
+ text: label.clone(),
+ x: b.x + x_offset,
+ y: b.y,
+ font_size,
+ color,
+ })
+ }
}
}
@@ -684,7 +727,7 @@ pub use self::input::{
KeybindsControl, KeybindRow, KeybindRecorder, Ramp, RampKey, ColorRamp, ColorRampKey
};
pub use self::container::{
- Container, ContainerLayout, OverlayLayout, VerticalLayout, GridLayout, AdaptiveGridLayout,
+ Container, ContainerLayout, OverlayLayout, ManualLayout, VerticalLayout, GridLayout, AdaptiveGridLayout,
ColumnsLayout, MosaicLayout, ReverseMosaicLayout,
SectionContainer, Header, ContentBg, ViewportBg, ParametersBg, List,
ScrollBox, Menu, MenuBar, Spreadsheet, Breadcrumb, Plate,