GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
A well floor is cut into its host's material (RFC material, step 3c)
PaintCtx::well_floor and canvas_well take the host plate's Material. An
opaque host's floor stays the darkening overlay it always was — exact
at any plate alpha, where a darkened fill at the plate's own alpha would
barely darken a translucent plate. A FROSTED host's floor is
Material::floor: the host with its tint darkened, frost and finish
carried, so a well in glass blurs and compresses what is under it again
instead of being the one opaque patch in a frosted pane (RFC § 11 (3)).
Every caller passes Material::pane().
Golden: opaque-host floors byte-identical; the harness gains a frosted
host whose floor is now a promoted plate batch.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
src/scene/material.rs | 8 +++++---
src/scene/paint.rs | 33 ++++++++++++++++++++++++---------
src/widget/input/bevel_preview.rs | 2 +-
src/widget/input/ramp_preview.rs | 2 +-
src/widget/input/slider2d.rs | 2 +-
src/widget/input/trackpad.rs | 2 +-
tests/plate_golden.rs | 10 ++++++----
7 files changed, 39 insertions(+), 20 deletions(-)
diff --git a/src/scene/material.rs b/src/scene/material.rs
index 0603bd9..b3ca1b0 100644
--- a/src/scene/material.rs
+++ b/src/scene/material.rs
@@ -263,9 +263,11 @@ impl Material {
/// The well floor cut into this plate: the same material with the tint
/// darkened by `WELL_FLOOR`'s strength (`WELL_FLOOR_LIFTED`'s when
/// `lifted`, the hover cue). Frost and finish carried through — a well in
- /// glass is deeper glass (RFC § 11 (3)). Over an opaque, fully covering
- /// plate this is the darkening overlay `PaintCtx::well_floor` draws today,
- /// exactly; the emission switches to this in step 2.
+ /// glass is deeper glass (RFC § 11 (3)). `PaintCtx::well_floor` draws
+ /// this for a FROSTED host; an opaque host's floor stays the darkening
+ /// overlay, which is this exactly at plate alpha 1 and the honest
+ /// darkening at any other alpha (a darkened fill at the plate's own
+ /// alpha would barely darken a translucent plate).
pub fn floor(&self, lifted: bool) -> Material {
let overlay = if lifted { crate::color::WELL_FLOOR_LIFTED } else { crate::color::WELL_FLOOR };
let keep = 1.0 - overlay[3];
diff --git a/src/scene/paint.rs b/src/scene/paint.rs
index cc68ca6..8fb8b3e 100644
--- a/src/scene/paint.rs
+++ b/src/scene/paint.rs
@@ -1332,13 +1332,28 @@ impl PaintCtx {
}
/// A canvas well's floor — the opening you look into or draw in (a
- /// Trackpad, a Slider2D pad, a bevel or ramp preview). It darkens the plate
- /// it is cut from ([`crate::colors::WELL_FLOOR`]) rather than painting a
- /// floor of its own, so every canvas sits in the one material. `lifted` is
- /// a clickable canvas's hover cue: the floor rises toward the plate.
- pub fn well_floor(&mut self, rect: Rect, radius: f32, lifted: bool) {
- let tint = if lifted { crate::colors::WELL_FLOOR_LIFTED } else { crate::colors::WELL_FLOOR };
- self.rounded_rect(rect, radius, (true, true, true, true), tint);
+ /// Trackpad, a Slider2D pad, a bevel or ramp preview) — cut into `host`,
+ /// the material of the plate it sits on (`Material::pane()` for a pane).
+ /// `lifted` is a clickable canvas's hover cue: the floor rises toward
+ /// the plate.
+ ///
+ /// An opaque host's floor is that plate darkened, drawn as the darkening
+ /// itself ([`crate::colors::WELL_FLOOR`] over whatever the plate resolved
+ /// to — exact at any plate alpha, and what every floor drew before
+ /// materials). A FROSTED host's floor is deeper glass
+ /// ([`Material::floor`]: the host's material with the tint darkened,
+ /// frost and finish carried), so a well in glass blurs and compresses
+ /// what is under it again instead of being the one opaque patch in a
+ /// frosted pane (RFC material § 11 (3)).
+ pub fn well_floor(&mut self, rect: Rect, radius: f32, host: &Material, lifted: bool) {
+ let fill = if host.frost.is_frosted() {
+ host.floor(lifted).fill(PlateRole::Nested)
+ } else if lifted {
+ crate::colors::WELL_FLOOR_LIFTED
+ } else {
+ crate::colors::WELL_FLOOR
+ };
+ self.rounded_rect(rect, radius, (true, true, true, true), fill);
}
/// A canvas well's rim, drawn AFTER the content so the wall's shading falls
@@ -1360,8 +1375,8 @@ impl PaintCtx {
/// [`well_floor`](Self::well_floor) then [`well_rim`](Self::well_rim) in
/// one call — a canvas whose content is drawn over the rim (a Trackpad's
/// fingers). Content that should slide under the wall draws between the two.
- pub fn canvas_well(&mut self, rect: Rect, radius: f32, relief: bool, lifted: bool) {
- self.well_floor(rect, radius, lifted);
+ pub fn canvas_well(&mut self, rect: Rect, radius: f32, host: &Material, relief: bool, lifted: bool) {
+ self.well_floor(rect, radius, host, lifted);
self.well_rim(rect, radius, relief);
}
diff --git a/src/widget/input/bevel_preview.rs b/src/widget/input/bevel_preview.rs
index c97c84e..b08688f 100644
--- a/src/widget/input/bevel_preview.rs
+++ b/src/widget/input/bevel_preview.rs
@@ -77,7 +77,7 @@ impl Paint for BevelPreview {
// The opening: the shared canvas well (`PaintCtx::well_floor`), its floor
// lifted on hover (the click cue); the rim is drawn last, over the content.
let radius = crate::layout::textbox_corner_radius();
- ctx.well_floor(rect, radius, self.hovered);
+ ctx.well_floor(rect, radius, &crate::scene::Material::pane(), self.hovered);
// Mini cutaway: plateau band, the wall over a square-ish domain, then
// the floor — cce-relief's `draw_section` reduced to swatch scale.
diff --git a/src/widget/input/ramp_preview.rs b/src/widget/input/ramp_preview.rs
index c1e866c..fd992ff 100644
--- a/src/widget/input/ramp_preview.rs
+++ b/src/widget/input/ramp_preview.rs
@@ -62,7 +62,7 @@ impl Paint for RampPreview {
// The opening: the shared canvas well (`PaintCtx::well_floor`), its floor
// lifted on hover (the click cue); the rim is drawn last, over the content.
let radius = crate::layout::textbox_corner_radius();
- ctx.well_floor(rect, radius, self.hovered);
+ ctx.well_floor(rect, radius, &crate::scene::Material::pane(), self.hovered);
let m = 4.0f32;
let x_l = rect.x + m;
diff --git a/src/widget/input/slider2d.rs b/src/widget/input/slider2d.rs
index 49beac1..baba9b4 100644
--- a/src/widget/input/slider2d.rs
+++ b/src/widget/input/slider2d.rs
@@ -100,7 +100,7 @@ impl Paint for Slider2D {
// opening cut into the host's plate, rim drawn last so its shading
// falls over the content at the edges. Rounded like the text wells.
let radius = crate::layout::textbox_corner_radius();
- ctx.well_floor(rect, radius, false);
+ ctx.well_floor(rect, radius, &crate::scene::Material::pane(), false);
// Crosshair through the thumb — the pad's read of both axis values.
let (cx, cy) = self.thumb_center(rect);
diff --git a/src/widget/input/trackpad.rs b/src/widget/input/trackpad.rs
index 89234cd..17f705a 100644
--- a/src/widget/input/trackpad.rs
+++ b/src/widget/input/trackpad.rs
@@ -103,7 +103,7 @@ impl Paint for Trackpad {
// opening shares (`PaintCtx::canvas_well`), rounded like the text wells;
// the fingers draw over the rim.
let radius = crate::layout::textbox_corner_radius();
- ctx.canvas_well(area, radius, self.recessed(), false);
+ ctx.canvas_well(area, radius, &crate::scene::Material::pane(), self.recessed(), false);
// 3. Fingers
for finger in &self.fingers {
diff --git a/tests/plate_golden.rs b/tests/plate_golden.rs
index 25e86e6..615d421 100644
--- a/tests/plate_golden.rs
+++ b/tests/plate_golden.rs
@@ -114,10 +114,12 @@ fn scene(sw: f32, sh: f32) -> cce_ui::scene::paint::DisplayList {
pc.inset_plate(r(20.0, 290.0, 60.0, 24.0), rr, Material::face([0.2, 0.2, 0.24, 1.0]).as_ref(), 4.0);
pc.inset_plate(r(90.0, 290.0, 60.0, 24.0), rr, Material::face([0.0; 4]).as_ref(), 4.0);
pc.inset_plate_tinted(r(160.0, 290.0, 60.0, 24.0), rr, Material::face([0.2, 0.2, 0.24, -0.5]).as_ref(), 4.0, [1.0, 0.5, 0.2]);
- pc.well_floor(r(230.0, 290.0, 40.0, 24.0), 6.0, false);
- pc.well_floor(r(280.0, 290.0, 40.0, 24.0), 6.0, true);
- pc.canvas_well(r(330.0, 290.0, 40.0, 24.0), 6.0, true, false);
- pc.canvas_well(r(330.0, 320.0, 40.0, 24.0), 6.0, false, true);
+ let pane = Material::pane();
+ let glass = Material::opaque([0.02, 0.02, 0.03, 0.25]).with_frost(cce_ui::scene::Frost::from_style());
+ pc.well_floor(r(230.0, 290.0, 40.0, 24.0), 6.0, &pane, false);
+ pc.well_floor(r(280.0, 290.0, 40.0, 24.0), 6.0, &pane, true);
+ pc.canvas_well(r(330.0, 290.0, 40.0, 24.0), 6.0, &pane, true, false);
+ pc.canvas_well(r(330.0, 320.0, 40.0, 24.0), 6.0, &glass, false, true);
// The lit ball and the water: default spec, and one with its own finish
// and depth terms.