GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
fix: standalone build + green tests — complete ArcShaded, fence app_chord doc
75f7132 committed model.rs's Prim::ArcShaded pass-through while the
primitive itself was still uncommitted work-in-progress, so a standalone
clone stopped compiling. Land the primitive's substrate: the Prim variant
+ PaintCtx::arc_shaded (paint.rs) and the tessellate arm +
push_arc_shaded_vertices (window_runner.rs) — the ramp widget work that
CONSUMES it stays in progress. Also fence the app_chord doc example as
text: rustdoc compiled the indented block as a doctest and it isn't Rust.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/backend/window_runner.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++
src/input.rs | 6 +++--
src/scene/paint.rs | 32 ++++++++++++++++++++++++++
3 files changed, 91 insertions(+), 2 deletions(-)
diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 6b268fa..029e766 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -1747,6 +1747,9 @@ pub fn tessellate_display_list(
Prim::Arc { cx, cy, radius, thickness, start: sa, end: ea, color } => {
push_arc_background_vertices(*cx, *cy, *radius, *thickness, *sa, *ea, sw, sh, *color, segs(*radius), no, &mut verts);
}
+ Prim::ArcShaded { cx, cy, radius, thickness, start: sa, end: ea, inner, crest, outer } => {
+ push_arc_shaded_vertices(*cx, *cy, *radius, *thickness, *sa, *ea, sw, sh, *inner, *crest, *outer, segs(*radius), no, &mut verts);
+ }
Prim::Vector { x1, y1, x2, y2, thickness, color, cap } => {
let lc = match cap {
Cap::Flat => LineCap::Flat,
@@ -2234,6 +2237,58 @@ pub fn push_arc_background_vertices(
}
}
+/// A ring band with radial Gouraud shading: two sub-bands (inner rim → crest
+/// centerline, crest → outer rim) whose vertex colors interpolate across the
+/// stroke — the rounded-bevel profile — plus the half-px alpha feathers at
+/// both true rims (colors matched to the adjacent band, so no seams).
+#[allow(clippy::too_many_arguments)]
+pub fn push_arc_shaded_vertices(
+ cx: f32, cy: f32, r: f32,
+ thickness: f32,
+ start_angle: f32, end_angle: f32,
+ sw: f32, sh: f32,
+ inner: [f32; 4], crest: [f32; 4], outer: [f32; 4],
+ segments: usize,
+ clip_circle: [f32; 3],
+ out: &mut Vec<Vertex>,
+) {
+ let f = 0.5f32.min(thickness * 0.25);
+ let r_out = r;
+ let r_in = (r - thickness).max(0.0);
+ let r_mid = (r_in + r_out) / 2.0;
+ let fade_in = [inner[0], inner[1], inner[2], 0.0];
+ let fade_out = [outer[0], outer[1], outer[2], 0.0];
+ // (inner radius, outer radius, color at inner edge, color at outer edge)
+ let bands = [
+ ((r_in - f).max(0.0), r_in + f, fade_in, inner),
+ (r_in + f, r_mid, inner, crest),
+ (r_mid, r_out - f, crest, outer),
+ (r_out - f, r_out + f, outer, fade_out),
+ ];
+ for i in 0..segments {
+ let theta1 = start_angle + (i as f32) * (end_angle - start_angle) / (segments as f32);
+ let theta2 = start_angle + ((i + 1) as f32) * (end_angle - start_angle) / (segments as f32);
+ let (c1, s1) = (theta1.cos(), theta1.sin());
+ let (c2, s2) = (theta2.cos(), theta2.sin());
+ for &(ra, rb, ca, cb) in &bands {
+ if rb <= ra {
+ continue;
+ }
+ let p = |rad: f32, c: f32, s: f32| -> [f32; 2] {
+ [((cx + rad * c) / sw) * 2.0 - 1.0, 1.0 - ((cy + rad * s) / sh) * 2.0]
+ };
+ let (i1, o1) = (p(ra, c1, s1), p(rb, c1, s1));
+ let (i2, o2) = (p(ra, c2, s2), p(rb, c2, s2));
+ out.push(Vertex { position: i1, color: ca, clip_circle });
+ out.push(Vertex { position: o1, color: cb, clip_circle });
+ out.push(Vertex { position: o2, color: cb, clip_circle });
+ out.push(Vertex { position: i1, color: ca, clip_circle });
+ out.push(Vertex { position: o2, color: cb, clip_circle });
+ out.push(Vertex { position: i2, color: ca, clip_circle });
+ }
+ }
+}
+
#[derive(Debug, Clone)]
pub struct WindowSettings {
pub title: String,
diff --git a/src/input.rs b/src/input.rs
index 5b6e6cf..7b70c76 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -353,8 +353,10 @@ pub fn cached() -> &'static InputConfig {
/// the compiled-in default. The app domain is the binary name. This is the
/// standard way for a client to resolve its shortcuts at startup:
///
-/// let open = cce_ui::input::app_chord("open_file", "enter");
-/// ... cce_ui::widget::match_key_shortcut(event, &open) ...
+/// ```text
+/// let open = cce_ui::input::app_chord("open_file", "enter");
+/// ... cce_ui::widget::match_key_shortcut(event, &open) ...
+/// ```
pub fn app_chord(name: &str, default: &str) -> String {
let app = crate::config::get_app_name().unwrap_or_default();
cached().resolve_chord(&app, name, default)
diff --git a/src/scene/paint.rs b/src/scene/paint.rs
index 097fb7f..6b9253f 100644
--- a/src/scene/paint.rs
+++ b/src/scene/paint.rs
@@ -91,6 +91,10 @@ pub enum Prim {
/// offset (the shading amplitude is the DE-wide `bevel_depth`).
Plate { rect: Rect, radii: Radii, color: [f32; 4], depth: f32 },
Arc { cx: f32, cy: f32, radius: f32, thickness: f32, start: f32, end: f32, color: [f32; 4] },
+ /// A ring band with radial color interpolation — inner rim → crest
+ /// (centerline) → outer rim — for rounded rim bevels (the Ramp's key
+ /// rings). `radius` is the stroke's outer edge, like `Arc`.
+ ArcShaded { cx: f32, cy: f32, radius: f32, thickness: f32, start: f32, end: f32, inner: [f32; 4], crest: [f32; 4], outer: [f32; 4] },
Vector { x1: f32, y1: f32, x2: f32, y2: f32, thickness: f32, color: [f32; 4], cap: Cap },
Circle { cx: f32, cy: f32, radius: f32, color: [f32; 4] },
/// A `Circle` lit as a ball: the disc is shaded per pixel as a hemisphere
@@ -521,6 +525,34 @@ impl PaintCtx {
self.push(Prim::Arc { cx: cx + ox, cy: cy + oy, radius, thickness, start, end, color });
}
+ /// A radially-shaded ring band — see [`Prim::ArcShaded`].
+ #[allow(clippy::too_many_arguments)]
+ pub fn arc_shaded(
+ &mut self,
+ cx: f32,
+ cy: f32,
+ radius: f32,
+ thickness: f32,
+ start: f32,
+ end: f32,
+ inner: [f32; 4],
+ crest: [f32; 4],
+ outer: [f32; 4],
+ ) {
+ let (ox, oy) = self.offset;
+ self.push(Prim::ArcShaded {
+ cx: cx + ox,
+ cy: cy + oy,
+ radius,
+ thickness,
+ start,
+ end,
+ inner,
+ crest,
+ outer,
+ });
+ }
+
pub fn text(&mut self, text: impl Into<String>, x: f32, y: f32, font_size: f32, color: [u8; 3]) {
self.text_with(text, x, y, font_size, color, None, None);
}