git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit6b55ee1a3c4d0816b581e2b9dafeb85cd94a2a81
parent600f5944df
authorLucas Galante <[email protected]>
date2026-09-20 15:05
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.