GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
Implement half-borders for Toggle widget and support style.control.toggle.border_color in KDL
src/backend/window_runner.rs | 4 ++
src/color.rs | 14 ++++--
src/layout.rs | 1 +
src/widget/container/control_panel.rs | 22 +++++++-
src/widget/input/checkbox.rs | 94 ++++++++++++++++++++++++++++++++++-
5 files changed, 130 insertions(+), 5 deletions(-)
diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 1177784..d4cc7bc 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -796,6 +796,10 @@ pub fn push_widget_vertices(w: &dyn crate::widget::Element, sw: f32, sh: f32, cl
if let Some((color, thickness)) = w.solid_border() {
push_plate_solid_border_vertices(x, y, ww, h, radii, thickness, sw, sh, color, clip_circle, out);
}
+
+ for (cx, cy, r, t, start, end, qc) in w.extra_arcs() {
+ push_arc_background_vertices(cx, cy, r, t, start, end, sw, sh, qc, 16, clip_circle, out);
+ }
}
pub fn extra_quad_vertices(
diff --git a/src/color.rs b/src/color.rs
index 62cff78..77caf85 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -310,11 +310,19 @@ 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/toggle/enabled_color").or_else(|| get_color("/layout/toggle_enabled_color")) {
+ let toggle_border_color = get_color("/style/control/toggle/border_color")
+ .or_else(|| get_color("/layout/toggle_border_color"));
+
+ if let Some(c) = toggle_border_color {
if let Ok(mut lock) = TOGGLE_ON_COLOR.write() { *lock = c; }
- }
- if let Some(c) = get_color("/style/control/toggle/disabled_color") {
if let Ok(mut lock) = TOGGLE_OFF_COLOR.write() { *lock = c; }
+ } else {
+ if let Some(c) = get_color("/style/control/toggle/enabled_color").or_else(|| get_color("/layout/toggle_enabled_color")) {
+ if let Ok(mut lock) = TOGGLE_ON_COLOR.write() { *lock = c; }
+ }
+ if let Some(c) = get_color("/style/control/toggle/disabled_color") {
+ if let Ok(mut lock) = TOGGLE_OFF_COLOR.write() { *lock = c; }
+ }
}
if let Some(c) = get_color("/layout/toggle_bg_color") {
if let Ok(mut lock) = TOGGLE_BG_COLOR.write() { *lock = c; }
diff --git a/src/layout.rs b/src/layout.rs
index f0f92b1..e60403d 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -103,6 +103,7 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
"style.control.toggle.height" => "toggle_height",
"style.control.toggle.border_width" => "toggle_border_width",
"style.control.toggle.disabled_color" => "toggle_disabled_color",
+ "style.control.toggle.border_color" => "toggle_border_color",
"style.control.toggle.corner_radius" => "toggle_corner_radius",
"style.control.ramp.height" => "ramp_height",
"style.status.normal_color" => "status_normal_color",
diff --git a/src/widget/container/control_panel.rs b/src/widget/container/control_panel.rs
index af0f51c..10d78d2 100644
--- a/src/widget/container/control_panel.rs
+++ b/src/widget/container/control_panel.rs
@@ -261,7 +261,7 @@ impl Element for ControlPanel {
unsafe {
for child_ptr in &self.children {
let child = &**child_ptr;
- let solid_border_opt = child.solid_border();
+ let solid_border_opt = if child.type_name() == "Toggle" { None } else { child.solid_border() };
let (cx, cy, cw, ch) = child.rect();
if let Some((b_color, _thickness)) = solid_border_opt {
@@ -383,6 +383,26 @@ impl Element for ControlPanel {
quads
}
+ fn extra_arcs(&self) -> Vec<(f32, f32, f32, f32, f32, f32, [f32; 4])> {
+ let mut arcs = Vec::new();
+ let scroll_y = self.scroll_box.scroll_y;
+ let y_start = self.base.y;
+ let y_end = self.base.y + self.base.h;
+
+ unsafe {
+ for child_ptr in &self.children {
+ let child = &**child_ptr;
+ for (cx, cy, r, t, start, end, color) in child.extra_arcs() {
+ let cy_shifted = cy - scroll_y;
+ if cy_shifted + r > y_start && cy_shifted - r < y_end {
+ arcs.push((cx, cy_shifted, r, t, start, end, color));
+ }
+ }
+ }
+ }
+ arcs
+ }
+
fn popover_rect(&self) -> Option<(f32, f32, f32, f32)> {
let scroll_y = self.scroll_box.scroll_y;
unsafe {
diff --git a/src/widget/input/checkbox.rs b/src/widget/input/checkbox.rs
index 301e60f..aaa68c9 100644
--- a/src/widget/input/checkbox.rs
+++ b/src/widget/input/checkbox.rs
@@ -293,7 +293,99 @@ impl Element for Toggle {
}
fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
- vec![(self.base.x, self.base.y, self.base.w, self.base.h, self.color())]
+ let mut quads = vec![(self.base.x, self.base.y, self.base.w, self.base.h, self.color())];
+
+ let border_w = crate::layout::toggle_border_width();
+ if border_w > 0.0 {
+ let x = self.base.x;
+ let y = self.base.y;
+ let w = self.base.w;
+ let h = self.base.h;
+ let r = crate::layout::toggle_corner_radius();
+ let t = border_w;
+
+ let border_color = if self.toggled {
+ colors::toggle_on_color()
+ } else {
+ colors::toggle_off_color()
+ };
+
+ let edge_h = ((h / 2.0) - r).max(0.0);
+
+ if self.toggled {
+ // Top edge
+ quads.push((x + r, y, w - 2.0 * r, t, border_color));
+ // Top half of left edge
+ if edge_h > 0.0 {
+ quads.push((x, y + r, t, edge_h, border_color));
+ }
+ // Top half of right edge
+ if edge_h > 0.0 {
+ quads.push((x + w - t, y + r, t, edge_h, border_color));
+ }
+ } else {
+ // Bottom edge
+ quads.push((x + r, y + h - t, w - 2.0 * r, t, border_color));
+ // Bottom half of left edge
+ if edge_h > 0.0 {
+ quads.push((x, y + h / 2.0, t, edge_h, border_color));
+ }
+ // Bottom half of right edge
+ if edge_h > 0.0 {
+ quads.push((x + w - t, y + h / 2.0, t, edge_h, border_color));
+ }
+ }
+ }
+ quads
+ }
+
+ fn extra_arcs(&self) -> Vec<(f32, f32, f32, f32, f32, f32, [f32; 4])> {
+ let mut arcs = Vec::new();
+ let border_w = crate::layout::toggle_border_width();
+ let r = crate::layout::toggle_corner_radius();
+
+ if border_w > 0.0 && r > 0.1 {
+ let x = self.base.x;
+ let y = self.base.y;
+ let w = self.base.w;
+ let h = self.base.h;
+ let t = border_w;
+
+ let border_color = if self.toggled {
+ colors::toggle_on_color()
+ } else {
+ colors::toggle_off_color()
+ };
+
+ if self.toggled {
+ // Top-Left corner arc
+ arcs.push((
+ x + r, y + r, r, t,
+ std::f32::consts::PI, 1.5 * std::f32::consts::PI,
+ border_color
+ ));
+ // Top-Right corner arc
+ arcs.push((
+ x + w - r, y + r, r, t,
+ 1.5 * std::f32::consts::PI, 2.0 * std::f32::consts::PI,
+ border_color
+ ));
+ } else {
+ // Bottom-Left corner arc
+ arcs.push((
+ x + r, y + h - r, r, t,
+ 0.5 * std::f32::consts::PI, std::f32::consts::PI,
+ border_color
+ ));
+ // Bottom-Right corner arc
+ arcs.push((
+ x + w - r, y + h - r, r, t,
+ 0.0, 0.5 * std::f32::consts::PI,
+ border_color
+ ));
+ }
+ }
+ arcs
}
fn text_labels(&self) -> Vec<TextLabel> {