GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
Implement StyleRegistry, CornerRadii struct, and standardized rounded border rendering across all widgets
src/backend/window_runner.rs | 418 +++++++++++++++++++++++++++++++------------
src/layout.rs | 131 +++++++++-----
src/widget/mod.rs | 29 +++
3 files changed, 420 insertions(+), 158 deletions(-)
diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index c231b11..8dc46c9 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -327,21 +327,55 @@ pub fn rounded_rect_vertices_corners(
clip_rect: Option<(f32, f32, f32, f32)>,
) -> Vec<Vertex> {
let mut verts = Vec::new();
- push_rounded_rect_vertices_corners(x, y, ww, h, r, sw, sh, color, clip_circle, corners, clip_rect, &mut verts);
+ let radii = crate::widget::CornerRadii::new(
+ if corners.0 { r } else { 0.0 },
+ if corners.1 { r } else { 0.0 },
+ if corners.2 { r } else { 0.0 },
+ if corners.3 { r } else { 0.0 },
+ );
+ push_rounded_rect_vertices_corners(x, y, ww, h, radii, sw, sh, color, clip_circle, clip_rect, &mut verts);
verts
}
pub fn push_rounded_rect_vertices_corners(
x: f32, y: f32, ww: f32, h: f32,
- r: f32,
+ radii: crate::widget::CornerRadii,
sw: f32, sh: f32,
color: [f32; 4],
clip_circle: [f32; 3],
- corners: (bool, bool, bool, bool),
clip_rect: Option<(f32, f32, f32, f32)>,
out: &mut Vec<Vertex>,
) {
- let r = r.min(ww * 0.5).min(h * 0.5);
+ let mut r_tl = radii.top_left.max(0.0);
+ let mut r_tr = radii.top_right.max(0.0);
+ let mut r_br = radii.bottom_right.max(0.0);
+ let mut r_bl = radii.bottom_left.max(0.0);
+
+ // Simple scale clamping
+ let sum_top = r_tl + r_tr;
+ if sum_top > ww {
+ let f = ww / sum_top;
+ r_tl *= f;
+ r_tr *= f;
+ }
+ let sum_bottom = r_bl + r_br;
+ if sum_bottom > ww {
+ let f = ww / sum_bottom;
+ r_bl *= f;
+ r_br *= f;
+ }
+ let sum_left = r_tl + r_bl;
+ if sum_left > h {
+ let f = h / sum_left;
+ r_tl *= f;
+ r_bl *= f;
+ }
+ let sum_right = r_tr + r_br;
+ if sum_right > h {
+ let f = h / sum_right;
+ r_tr *= f;
+ r_br *= f;
+ }
let clamp_x = |val: f32| -> f32 {
if let Some((cx0, _, cx1, _)) = clip_rect {
@@ -381,60 +415,166 @@ pub fn push_rounded_rect_vertices_corners(
verts.push(Vertex { position: [ndc_x0, ndc_y1], color, clip_circle });
};
- if r <= 0.1 || (!corners.0 && !corners.1 && !corners.2 && !corners.3) {
+ let has_corners = r_tl > 0.1 || r_tr > 0.1 || r_br > 0.1 || r_bl > 0.1;
+ if !has_corners {
push_quad(out, x, y, ww, h);
return;
}
- // 1. Center rectangle
- push_quad(out, x + r, y, ww - 2.0 * r, h);
-
- // 2. Left rectangle
- push_quad(out, x, y + r, r, h - 2.0 * r);
-
- // 3. Right rectangle
- push_quad(out, x + ww - r, y + r, r, h - 2.0 * r);
-
- // 4. Four corners
- let corner_configs = [
- // Top-left
- (corners.0, x, y, x + r, y + r, std::f32::consts::PI, 1.5 * std::f32::consts::PI),
- // Top-right
- (corners.1, x + ww - r, y, x + ww - r, y + r, 1.5 * std::f32::consts::PI, 2.0 * std::f32::consts::PI),
- // Bottom-right
- (corners.2, x + ww - r, y + h - r, x + ww - r, y + h - r, 0.0, 0.5 * std::f32::consts::PI),
- // Bottom-left
- (corners.3, x, y + h - r, x + r, y + h - r, 0.5 * std::f32::consts::PI, std::f32::consts::PI),
- ];
+ // Body rectangles
+ let mid_x0 = r_tl.max(r_bl);
+ let mid_x1 = ww - r_tr.max(r_br);
+ if mid_x1 > mid_x0 {
+ push_quad(out, x + mid_x0, y, mid_x1 - mid_x0, h);
+ }
+ if h > r_tl + r_bl {
+ push_quad(out, x, y + r_tl, mid_x0, h - r_tl - r_bl);
+ }
+ if h > r_tr + r_br {
+ push_quad(out, x + mid_x1, y + r_tr, ww - mid_x1, h - r_tr - r_br);
+ }
+ // Corner rendering
let segments = 16;
- for &(is_rounded, sqx, sqy, cx, cy, start, end) in &corner_configs {
- if is_rounded {
- for i in 0..segments {
- let theta1 = start + (i as f32) * (end - start) / (segments as f32);
- let theta2 = start + ((i + 1) as f32) * (end - start) / (segments as f32);
-
- let x0 = clamp_x(cx);
- let y0 = clamp_y(cy);
- let x1 = clamp_x(cx + r * theta1.cos());
- let y1 = clamp_y(cy + r * theta1.sin());
- let x2 = clamp_x(cx + r * theta2.cos());
- let y2 = clamp_y(cy + r * theta2.sin());
-
- let ndc_x0 = (x0 / sw) * 2.0 - 1.0;
- let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
- let ndc_x1 = (x1 / sw) * 2.0 - 1.0;
- let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
- let ndc_x2 = (x2 / sw) * 2.0 - 1.0;
- let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
-
- out.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
- out.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
- out.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
- }
- } else {
- push_quad(out, sqx, sqy, r, r);
+
+ // Top-Left
+ if r_tl > 0.1 {
+ let cx = x + r_tl;
+ let cy = y + r_tl;
+ let start = std::f32::consts::PI;
+ let end = 1.5 * std::f32::consts::PI;
+ for i in 0..segments {
+ let theta1 = start + (i as f32) * (end - start) / (segments as f32);
+ let theta2 = start + ((i + 1) as f32) * (end - start) / (segments as f32);
+
+ let x0 = clamp_x(cx);
+ let y0 = clamp_y(cy);
+ let x1 = clamp_x(cx + r_tl * theta1.cos());
+ let y1 = clamp_y(cy + r_tl * theta1.sin());
+ let x2 = clamp_x(cx + r_tl * theta2.cos());
+ let y2 = clamp_y(cy + r_tl * theta2.sin());
+
+ let ndc_x0 = (x0 / sw) * 2.0 - 1.0;
+ let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
+ let ndc_x1 = (x1 / sw) * 2.0 - 1.0;
+ let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
+ let ndc_x2 = (x2 / sw) * 2.0 - 1.0;
+ let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
+
+ out.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
+ out.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
+ out.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
+ }
+ if mid_x0 > r_tl {
+ push_quad(out, x + r_tl, y, mid_x0 - r_tl, r_tl);
+ }
+ } else {
+ push_quad(out, x, y, mid_x0, 0.0);
+ }
+
+ // Top-Right
+ if r_tr > 0.1 {
+ let cx = x + ww - r_tr;
+ let cy = y + r_tr;
+ let start = 1.5 * std::f32::consts::PI;
+ let end = 2.0 * std::f32::consts::PI;
+ for i in 0..segments {
+ let theta1 = start + (i as f32) * (end - start) / (segments as f32);
+ let theta2 = start + ((i + 1) as f32) * (end - start) / (segments as f32);
+
+ let x0 = clamp_x(cx);
+ let y0 = clamp_y(cy);
+ let x1 = clamp_x(cx + r_tr * theta1.cos());
+ let y1 = clamp_y(cy + r_tr * theta1.sin());
+ let x2 = clamp_x(cx + r_tr * theta2.cos());
+ let y2 = clamp_y(cy + r_tr * theta2.sin());
+
+ let ndc_x0 = (x0 / sw) * 2.0 - 1.0;
+ let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
+ let ndc_x1 = (x1 / sw) * 2.0 - 1.0;
+ let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
+ let ndc_x2 = (x2 / sw) * 2.0 - 1.0;
+ let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
+
+ out.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
+ out.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
+ out.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
+ }
+ if ww - mid_x1 > r_tr {
+ push_quad(out, x + mid_x1, y, ww - mid_x1 - r_tr, r_tr);
+ }
+ } else {
+ push_quad(out, x + mid_x1, y, ww - mid_x1, 0.0);
+ }
+
+ // Bottom-Right
+ if r_br > 0.1 {
+ let cx = x + ww - r_br;
+ let cy = y + h - r_br;
+ let start = 0.0;
+ let end = 0.5 * std::f32::consts::PI;
+ for i in 0..segments {
+ let theta1 = start + (i as f32) * (end - start) / (segments as f32);
+ let theta2 = start + ((i + 1) as f32) * (end - start) / (segments as f32);
+
+ let x0 = clamp_x(cx);
+ let y0 = clamp_y(cy);
+ let x1 = clamp_x(cx + r_br * theta1.cos());
+ let y1 = clamp_y(cy + r_br * theta1.sin());
+ let x2 = clamp_x(cx + r_br * theta2.cos());
+ let y2 = clamp_y(cy + r_br * theta2.sin());
+
+ let ndc_x0 = (x0 / sw) * 2.0 - 1.0;
+ let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
+ let ndc_x1 = (x1 / sw) * 2.0 - 1.0;
+ let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
+ let ndc_x2 = (x2 / sw) * 2.0 - 1.0;
+ let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
+
+ out.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
+ out.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
+ out.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
+ }
+ if ww - mid_x1 > r_br {
+ push_quad(out, x + mid_x1, y + h - r_br, ww - mid_x1 - r_br, r_br);
+ }
+ } else {
+ push_quad(out, x + mid_x1, y + h, ww - mid_x1, 0.0);
+ }
+
+ // Bottom-Left
+ if r_bl > 0.1 {
+ let cx = x + r_bl;
+ let cy = y + h - r_bl;
+ let start = 0.5 * std::f32::consts::PI;
+ let end = std::f32::consts::PI;
+ for i in 0..segments {
+ let theta1 = start + (i as f32) * (end - start) / (segments as f32);
+ let theta2 = start + ((i + 1) as f32) * (end - start) / (segments as f32);
+
+ let x0 = clamp_x(cx);
+ let y0 = clamp_y(cy);
+ let x1 = clamp_x(cx + r_bl * theta1.cos());
+ let y1 = clamp_y(cy + r_bl * theta1.sin());
+ let x2 = clamp_x(cx + r_bl * theta2.cos());
+ let y2 = clamp_y(cy + r_bl * theta2.sin());
+
+ let ndc_x0 = (x0 / sw) * 2.0 - 1.0;
+ let ndc_y0 = 1.0 - (y0 / sh) * 2.0;
+ let ndc_x1 = (x1 / sw) * 2.0 - 1.0;
+ let ndc_y1 = 1.0 - (y1 / sh) * 2.0;
+ let ndc_x2 = (x2 / sw) * 2.0 - 1.0;
+ let ndc_y2 = 1.0 - (y2 / sh) * 2.0;
+
+ out.push(Vertex { position: [ndc_x0, ndc_y0], color, clip_circle });
+ out.push(Vertex { position: [ndc_x1, ndc_y1], color, clip_circle });
+ out.push(Vertex { position: [ndc_x2, ndc_y2], color, clip_circle });
}
+ if mid_x0 > r_bl {
+ push_quad(out, x + r_bl, y + h - r_bl, mid_x0 - r_bl, r_bl);
+ }
+ } else {
+ push_quad(out, x, y + h, mid_x0, 0.0);
}
}
@@ -445,7 +585,9 @@ pub fn rounded_rect_vertices(
color: [f32; 4],
clip_circle: [f32; 3],
) -> Vec<Vertex> {
- rounded_rect_vertices_corners(x, y, ww, h, r, sw, sh, color, clip_circle, (true, true, true, true), None)
+ let mut verts = Vec::new();
+ push_rounded_rect_vertices_corners(x, y, ww, h, crate::widget::CornerRadii::uniform(r), sw, sh, color, clip_circle, None, &mut verts);
+ verts
}
pub fn push_rounded_rect_vertices(
@@ -456,7 +598,7 @@ pub fn push_rounded_rect_vertices(
clip_circle: [f32; 3],
out: &mut Vec<Vertex>,
) {
- push_rounded_rect_vertices_corners(x, y, ww, h, r, sw, sh, color, clip_circle, (true, true, true, true), None, out);
+ push_rounded_rect_vertices_corners(x, y, ww, h, crate::widget::CornerRadii::uniform(r), sw, sh, color, clip_circle, None, out);
}
pub fn plate_bevel_vertices(
@@ -533,48 +675,96 @@ pub fn push_plate_bevel_vertices(
pub fn push_plate_solid_border_vertices(
x: f32, y: f32, ww: f32, h: f32,
- r: f32,
+ radii: crate::widget::CornerRadii,
t: f32,
sw: f32, sh: f32,
color: [f32; 4],
clip_circle: [f32; 3],
out: &mut Vec<Vertex>,
) {
- let r = r.min(ww * 0.5).min(h * 0.5);
- out.extend_from_slice(&quad_vertices_with_clip(x + r, y, ww - 2.0 * r, t, sw, sh, color, clip_circle));
- out.extend_from_slice(&quad_vertices_with_clip(x, y + r, t, h - 2.0 * r, sw, sh, color, clip_circle));
- out.extend_from_slice(&quad_vertices_with_clip(x + r, y + h - t, ww - 2.0 * r, t, sw, sh, color, clip_circle));
- out.extend_from_slice(&quad_vertices_with_clip(x + ww - t, y + r, t, h - 2.0 * r, sw, sh, color, clip_circle));
-
- let segments = 16;
+ let mut r_tl = radii.top_left.max(0.0);
+ let mut r_tr = radii.top_right.max(0.0);
+ let mut r_br = radii.bottom_right.max(0.0);
+ let mut r_bl = radii.bottom_left.max(0.0);
+
+ // Simple scale clamping
+ let sum_top = r_tl + r_tr;
+ if sum_top > ww {
+ let f = ww / sum_top;
+ r_tl *= f;
+ r_tr *= f;
+ }
+ let sum_bottom = r_bl + r_br;
+ if sum_bottom > ww {
+ let f = ww / sum_bottom;
+ r_bl *= f;
+ r_br *= f;
+ }
+ let sum_left = r_tl + r_bl;
+ if sum_left > h {
+ let f = h / sum_left;
+ r_tl *= f;
+ r_bl *= f;
+ }
+ let sum_right = r_tr + r_br;
+ if sum_right > h {
+ let f = h / sum_right;
+ r_tr *= f;
+ r_br *= f;
+ }
- push_arc_background_vertices(
- x + r, y + r, r, t,
- std::f32::consts::PI, 1.5 * std::f32::consts::PI,
- sw, sh, color, segments, clip_circle,
- out,
- );
+ out.extend_from_slice(&quad_vertices_with_clip(x + r_tl, y, ww - r_tl - r_tr, t, sw, sh, color, clip_circle));
+ out.extend_from_slice(&quad_vertices_with_clip(x, y + r_tl, t, h - r_tl - r_bl, sw, sh, color, clip_circle));
+ out.extend_from_slice(&quad_vertices_with_clip(x + r_bl, y + h - t, ww - r_bl - r_br, t, sw, sh, color, clip_circle));
+ out.extend_from_slice(&quad_vertices_with_clip(x + ww - t, y + r_tr, t, h - r_tr - r_br, sw, sh, color, clip_circle));
- push_arc_background_vertices(
- x + ww - r, y + r, r, t,
- 1.5 * std::f32::consts::PI, 2.0 * std::f32::consts::PI,
- sw, sh, color, segments, clip_circle,
- out,
- );
+ let segments = 16;
- push_arc_background_vertices(
- x + ww - r, y + h - r, r, t,
- 0.0, 0.5 * std::f32::consts::PI,
- sw, sh, color, segments, clip_circle,
- out,
- );
+ if r_tl > 0.1 {
+ push_arc_background_vertices(
+ x + r_tl, y + r_tl, r_tl, t,
+ std::f32::consts::PI, 1.5 * std::f32::consts::PI,
+ sw, sh, color, segments, clip_circle,
+ out,
+ );
+ }
+ if r_tr > 0.1 {
+ push_arc_background_vertices(
+ x + ww - r_tr, y + r_tr, r_tr, t,
+ 1.5 * std::f32::consts::PI, 2.0 * std::f32::consts::PI,
+ sw, sh, color, segments, clip_circle,
+ out,
+ );
+ }
+ if r_br > 0.1 {
+ push_arc_background_vertices(
+ x + ww - r_br, y + h - r_br, r_br, t,
+ 0.0, 0.5 * std::f32::consts::PI,
+ sw, sh, color, segments, clip_circle,
+ out,
+ );
+ }
+ if r_bl > 0.1 {
+ push_arc_background_vertices(
+ x + r_bl, y + h - r_bl, r_bl, t,
+ 0.5 * std::f32::consts::PI, std::f32::consts::PI,
+ sw, sh, color, segments, clip_circle,
+ out,
+ );
+ }
+}
- push_arc_background_vertices(
- x + r, y + h - r, r, t,
- 0.5 * std::f32::consts::PI, std::f32::consts::PI,
- sw, sh, color, segments, clip_circle,
- out,
- );
+pub fn push_plate_solid_border_vertices_legacy(
+ x: f32, y: f32, ww: f32, h: f32,
+ r: f32,
+ t: f32,
+ sw: f32, sh: f32,
+ color: [f32; 4],
+ clip_circle: [f32; 3],
+ out: &mut Vec<Vertex>,
+) {
+ let radii = crate::widget::CornerRadii::uniform(r);
+ push_plate_solid_border_vertices(x, y, ww, h, radii, t, sw, sh, color, clip_circle, out);
}
pub fn widget_vertices(w: &dyn crate::widget::Element, sw: f32, sh: f32, clip_circle: [f32; 3]) -> Vec<Vertex> {
@@ -585,15 +775,11 @@ pub fn widget_vertices(w: &dyn crate::widget::Element, sw: f32, sh: f32, clip_ci
pub fn push_widget_vertices(w: &dyn crate::widget::Element, sw: f32, sh: f32, clip_circle: [f32; 3], out: &mut Vec<Vertex>) {
let (x, y, ww, h) = w.rect();
- let corners = w.rounded_corners();
- if corners != (false, false, false, false) {
- push_rounded_rect_vertices_corners(x, y, ww, h, w.corner_radius(), sw, sh, w.color(), clip_circle, corners, None, out);
- } else {
- out.extend_from_slice(&quad_vertices_with_clip(x, y, ww, h, sw, sh, w.color(), clip_circle));
- }
+ let radii = w.corner_radii();
+ push_rounded_rect_vertices_corners(x, y, ww, h, radii, sw, sh, w.color(), clip_circle, None, out);
if let Some((color, thickness)) = w.solid_border() {
- push_plate_solid_border_vertices(x, y, ww, h, w.corner_radius(), thickness, sw, sh, color, clip_circle, out);
+ push_plate_solid_border_vertices(x, y, ww, h, radii, thickness, sw, sh, color, clip_circle, out);
}
}
@@ -747,13 +933,13 @@ pub fn push_extra_quad_vertices(
out: &mut Vec<Vertex>,
) {
let target_w = get_child_widget_for_quad(w, qx, qy, qw, qh);
- let corners = target_w.rounded_corners();
- if corners == (false, false, false, false) {
+ let radii = target_w.corner_radii();
+ if radii.top_left <= 0.1 && radii.top_right <= 0.1 && radii.bottom_right <= 0.1 && radii.bottom_left <= 0.1 {
out.extend_from_slice(&quad_vertices_with_clip(qx, qy, qw, qh, sw, sh, qc, clip_circle));
if let Some((color, thickness)) = target_w.solid_border() {
let (wx, wy, ww, wh) = target_w.rect();
if (qx - wx).abs() < 0.1 && (qy - wy).abs() < 0.1 && (qw - ww).abs() < 0.1 && (qh - wh).abs() < 0.1 {
- push_plate_solid_border_vertices(qx, qy, qw, qh, target_w.corner_radius(), thickness, sw, sh, color, clip_circle, out);
+ push_plate_solid_border_vertices(qx, qy, qw, qh, radii, thickness, sw, sh, color, clip_circle, out);
}
}
return;
@@ -763,14 +949,14 @@ pub fn push_extra_quad_vertices(
let top_room = crate::widget::label_offset(target_w);
wy += top_room;
wh -= top_room;
- let extra_corners = (
- corners.0 && qx <= wx + 1.5 && qy <= wy + 1.5,
- corners.1 && qx + qw >= wx + ww - 1.5 && qy <= wy + 1.5,
- corners.2 && qx + qw >= wx + ww - 1.5 && qy + qh >= wy + wh - 1.5,
- corners.3 && qx <= wx + 1.5 && qy + qh >= wy + wh - 1.5,
+ let extra_radii = crate::widget::CornerRadii::new(
+ if qx <= wx + 1.5 && qy <= wy + 1.5 { radii.top_left } else { 0.0 },
+ if qx + qw >= wx + ww - 1.5 && qy <= wy + 1.5 { radii.top_right } else { 0.0 },
+ if qx + qw >= wx + ww - 1.5 && qy + qh >= wy + wh - 1.5 { radii.bottom_right } else { 0.0 },
+ if qx <= wx + 1.5 && qy + qh >= wy + wh - 1.5 { radii.bottom_left } else { 0.0 },
);
- push_rounded_rect_vertices_corners(qx, qy, qw, qh, target_w.corner_radius(), sw, sh, qc, clip_circle, extra_corners, None, out);
+ push_rounded_rect_vertices_corners(qx, qy, qw, qh, extra_radii, sw, sh, qc, clip_circle, None, out);
if let Some((color, thickness)) = target_w.solid_border() {
let (rx, mut ry, rw, mut rh) = target_w.rect();
@@ -778,7 +964,7 @@ pub fn push_extra_quad_vertices(
ry += top;
rh -= top;
if (qx - rx).abs() < 0.1 && (qy - ry).abs() < 0.1 && (qw - rw).abs() < 0.1 && (qh - rh).abs() < 0.1 {
- push_plate_solid_border_vertices(qx, qy, qw, qh, target_w.corner_radius(), thickness, sw, sh, color, clip_circle, out);
+ push_plate_solid_border_vertices(qx, qy, qw, qh, radii, thickness, sw, sh, color, clip_circle, out);
}
}
}
@@ -806,8 +992,8 @@ pub fn push_extra_quad_vertices_clipped(
out: &mut Vec<Vertex>,
) {
let target_w = get_child_widget_for_quad(w, qx, qy, qw, qh);
- let corners = target_w.rounded_corners();
- if corners == (false, false, false, false) {
+ let radii = target_w.corner_radii();
+ if radii.top_left <= 0.1 && radii.top_right <= 0.1 && radii.bottom_right <= 0.1 && radii.bottom_left <= 0.1 {
let (cx0, cy0, cx1, cy1) = clip;
let ix0 = qx.max(cx0);
let iy0 = qy.max(cy0);
@@ -820,7 +1006,7 @@ pub fn push_extra_quad_vertices_clipped(
if let Some((color, thickness)) = target_w.solid_border() {
let (wx, wy, ww, wh) = target_w.rect();
if (qx - wx).abs() < 0.1 && (qy - wy).abs() < 0.1 && (qw - ww).abs() < 0.1 && (qh - wh).abs() < 0.1 {
- push_plate_solid_border_vertices(qx, qy, qw, qh, target_w.corner_radius(), thickness, sw, sh, color, clip_circle, out);
+ push_plate_solid_border_vertices(qx, qy, qw, qh, radii, thickness, sw, sh, color, clip_circle, out);
}
}
return;
@@ -830,14 +1016,14 @@ pub fn push_extra_quad_vertices_clipped(
let top_room = crate::widget::label_offset(target_w);
wy += top_room;
wh -= top_room;
- let extra_corners = (
- corners.0 && qx <= wx + 1.5 && qy <= wy + 1.5,
- corners.1 && qx + qw >= wx + ww - 1.5 && qy <= wy + 1.5,
- corners.2 && qx + qw >= wx + ww - 1.5 && qy + qh >= wy + wh - 1.5,
- corners.3 && qx <= wx + 1.5 && qy + qh >= wy + wh - 1.5,
+ let extra_radii = crate::widget::CornerRadii::new(
+ if qx <= wx + 1.5 && qy <= wy + 1.5 { radii.top_left } else { 0.0 },
+ if qx + qw >= wx + ww - 1.5 && qy <= wy + 1.5 { radii.top_right } else { 0.0 },
+ if qx + qw >= wx + ww - 1.5 && qy + qh >= wy + wh - 1.5 { radii.bottom_right } else { 0.0 },
+ if qx <= wx + 1.5 && qy + qh >= wy + wh - 1.5 { radii.bottom_left } else { 0.0 },
);
- push_rounded_rect_vertices_corners(qx, qy, qw, qh, target_w.corner_radius(), sw, sh, qc, clip_circle, extra_corners, Some(clip), out);
+ push_rounded_rect_vertices_corners(qx, qy, qw, qh, extra_radii, sw, sh, qc, clip_circle, Some(clip), out);
if let Some((color, thickness)) = target_w.solid_border() {
let (rx, mut ry, rw, mut rh) = target_w.rect();
@@ -845,7 +1031,7 @@ pub fn push_extra_quad_vertices_clipped(
ry += top;
rh -= top;
if (qx - rx).abs() < 0.1 && (qy - ry).abs() < 0.1 && (qw - rw).abs() < 0.1 && (qh - rh).abs() < 0.1 {
- push_plate_solid_border_vertices(qx, qy, qw, qh, target_w.corner_radius(), thickness, sw, sh, color, clip_circle, out);
+ push_plate_solid_border_vertices(qx, qy, qw, qh, radii, thickness, sw, sh, color, clip_circle, out);
}
}
}
@@ -1316,7 +1502,13 @@ impl<A: Application> EngineState<A> {
}
for &(qx, qy, qw, qh, qr, qc, qcorners) in &rounded_quads {
if qr > 0.1 {
- push_rounded_rect_vertices_corners(qx, qy, qw, qh, qr, logical_w, logical_h, qc, [0.0, 0.0, 0.0], qcorners, None, &mut verts);
+ let radii = crate::widget::CornerRadii::new(
+ if qcorners.0 { qr } else { 0.0 },
+ if qcorners.1 { qr } else { 0.0 },
+ if qcorners.2 { qr } else { 0.0 },
+ if qcorners.3 { qr } else { 0.0 },
+ );
+ push_rounded_rect_vertices_corners(qx, qy, qw, qh, radii, logical_w, logical_h, qc, [0.0, 0.0, 0.0], None, &mut verts);
} else {
verts.extend(quad_vertices(qx, qy, qw, qh, logical_w, logical_h, qc));
}
diff --git a/src/layout.rs b/src/layout.rs
index ec15fc2..d40dfd2 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -1,6 +1,68 @@
use crate::widget::Element;
use crate::context::UiContext;
use std::sync::RwLock;
+use std::collections::HashMap;
+use std::sync::OnceLock;
+
+#[derive(Debug)]
+pub struct StyleRegistry {
+ pub floats: HashMap<String, f32>,
+ pub strings: HashMap<String, String>,
+}
+
+impl StyleRegistry {
+ pub fn new() -> Self {
+ Self {
+ floats: HashMap::new(),
+ strings: HashMap::new(),
+ }
+ }
+
+ pub fn get_float(&self, key: &str) -> Option<f32> {
+ self.floats.get(key).copied()
+ }
+
+ pub fn get_string(&self, key: &str) -> Option<String> {
+ self.strings.get(key).cloned()
+ }
+
+ pub fn set_float(&mut self, key: &str, val: f32) {
+ self.floats.insert(key.to_string(), val);
+ }
+
+ pub fn set_string(&mut self, key: &str, val: String) {
+ self.strings.insert(key.to_string(), val);
+ }
+}
+
+pub static STYLE_REGISTRY: OnceLock<RwLock<StyleRegistry>> = OnceLock::new();
+
+pub fn get_style_registry() -> &'static RwLock<StyleRegistry> {
+ STYLE_REGISTRY.get_or_init(|| RwLock::new(StyleRegistry::new()))
+}
+
+pub fn lazy_init_style_registry() {
+ 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(eq_idx) = trimmed.find('=') {
+ let key = trimmed[..eq_idx].trim().to_string();
+ let val_str = trimmed[eq_idx + 1..].trim().trim_matches('"').trim();
+ if let Ok(mut registry) = get_style_registry().write() {
+ if let Ok(f_val) = val_str.parse::<f32>() {
+ registry.set_float(&key, f_val);
+ } else {
+ registry.set_string(&key, val_str.to_string());
+ }
+ }
+ }
+ }
+ }
+ });
+}
fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props: &mut String) {
match val {
@@ -228,6 +290,17 @@ pub fn reload_config() {
let mut list_font_changed = false;
for line in content.lines() {
let trimmed = line.trim();
+ if let Some(eq_idx) = trimmed.find('=') {
+ let key = trimmed[..eq_idx].trim().to_string();
+ let val_str = trimmed[eq_idx + 1..].trim().trim_matches('"').trim();
+ if let Ok(mut registry) = get_style_registry().write() {
+ if let Ok(f_val) = val_str.parse::<f32>() {
+ registry.set_float(&key, f_val);
+ } else {
+ registry.set_string(&key, val_str.to_string());
+ }
+ }
+ }
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();
@@ -2359,64 +2432,33 @@ pub fn breadcrumb_corner_radius() -> f32 {
}
pub fn set_breadcrumb_corner_radius(radius: f32) {
- if let Ok(mut lock) = BREADCRUMB_CORNER_RADIUS.write() {
- *lock = radius;
+ lazy_init_style_registry();
+ if let Ok(mut registry) = get_style_registry().write() {
+ registry.set_float("breadcrumb_corner_radius", radius);
}
}
pub fn list_corner_radius() -> 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("list_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) = LIST_CORNER_RADIUS.write() {
- *lock = val;
- }
- }
- }
- }
- }
- });
- *LIST_CORNER_RADIUS.read().unwrap()
+ lazy_init_style_registry();
+ get_style_registry().read().unwrap().get_float("list_corner_radius").unwrap_or(4.0)
}
pub fn set_list_corner_radius(radius: f32) {
- if let Ok(mut lock) = LIST_CORNER_RADIUS.write() {
- *lock = radius;
+ lazy_init_style_registry();
+ if let Ok(mut registry) = get_style_registry().write() {
+ registry.set_float("list_corner_radius", radius);
}
}
pub fn tree_corner_radius() -> 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("tree_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) = TREE_CORNER_RADIUS.write() {
- *lock = val;
- }
- }
- }
- }
- }
- });
- *TREE_CORNER_RADIUS.read().unwrap()
+ lazy_init_style_registry();
+ get_style_registry().read().unwrap().get_float("tree_corner_radius").unwrap_or(4.0)
}
pub fn set_tree_corner_radius(radius: f32) {
- if let Ok(mut lock) = TREE_CORNER_RADIUS.write() {
- *lock = radius;
+ lazy_init_style_registry();
+ if let Ok(mut registry) = get_style_registry().write() {
+ registry.set_float("tree_corner_radius", radius);
}
}
@@ -3670,7 +3712,6 @@ impl Radial {
}
use std::cell::RefCell;
-use std::collections::HashMap;
thread_local! {
pub static GRID_STATES: RefCell<HashMap<usize, Grid>> = RefCell::new(HashMap::new());
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index 9c4877d..e2dc580 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -610,6 +610,17 @@ pub trait Element {
fn rounded_corners(&self) -> (bool, bool, bool, bool) { (false, false, false, false) }
fn corner_radius(&self) -> f32 { 12.0 }
+
+ fn corner_radii(&self) -> CornerRadii {
+ let r = self.corner_radius();
+ let (tl, tr, br, bl) = self.rounded_corners();
+ CornerRadii::new(
+ if tl { r } else { 0.0 },
+ if tr { r } else { 0.0 },
+ if br { r } else { 0.0 },
+ if bl { r } else { 0.0 },
+ )
+ }
fn layout_ignore(&self) -> bool { false }
fn color_u8(&self) -> Option<[u8; 4]> { None }
}
@@ -757,3 +768,21 @@ pub fn label_offset(w: &dyn Element) -> f32 {
}
w.base().map_or(0.0, |b| b.label_offset())
}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct CornerRadii {
+ pub top_left: f32,
+ pub top_right: f32,
+ pub bottom_right: f32,
+ pub bottom_left: f32,
+}
+
+impl CornerRadii {
+ pub fn new(tl: f32, tr: f32, br: f32, bl: f32) -> Self {
+ Self { top_left: tl, top_right: tr, bottom_right: br, bottom_left: bl }
+ }
+
+ pub fn uniform(radius: f32) -> Self {
+ Self::new(radius, radius, radius, radius)
+ }
+}