git.lucas.co / cce-gallery
widget gallery and compositor test bench

commitf6f6b433eccfdff7d24f710914e52151dee0e21e
parentd473ebb28f
authorLucas Galante <lsgalante12@gmail.com>
date2026-10-06 08:57
feat(scrollbars): the exhibit area's bar rides its centre line, behind the floor

The exhibit ScrollBox opts into sink_behind: its idle copy goes under
the well floor every frame and its fore copy fades in over the rim,
both as pills (shader-lit relief cannot fade), replacing the always-on
relief bar at the right edge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

 src/main.rs | 29 ++++++++++++++++++-----------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index c9dd27a..30f91f9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1278,6 +1278,9 @@ impl cce_ui::engine::Application for State {
                 let mut sb = ScrollBox::new();
                 sb.show_border = false;
                 sb.show_background = false;
+                // The DE's one scrollbar: down the area's centre line, idling
+                // behind the well's floor until a scroll raises it.
+                sb.sink_behind = true;
                 sb
             },
         };
@@ -1359,6 +1362,8 @@ impl cce_ui::engine::Application for State {
         if hover_animation::tick(dt) {
             changed = true;
         }
+        // True while the area's bar is held up or fading, as well as while a
+        // glide moves it, so the frames keep coming until the sink renders.
         if !self.is_child && self.exhibit_scroll.tick(dt, &mut self.ui_context) {
             changed = true;
         }
@@ -1421,7 +1426,11 @@ impl cce_ui::engine::Application for State {
         // The exhibit area is a well in the root plate: its floor under the
         // exhibits here, its rim over them below (`well_rim`), so an exhibit
         // scrolled to the edge slides under the wall rather than sitting on it.
+        // The area's scrollbar idles BEHIND the floor: its idle copy goes down
+        // first, every frame and at full strength — raised or not, since the
+        // fore copy fades in over it and dropping it at the latch would blink.
         if !self.is_child {
+            self.exhibit_scroll.paint_scrollbar_pills(&mut pc, 1.0);
             let (well, radius, _) = self.exhibit_well();
             pc.well_floor(well, radius, &cce_ui::scene::Material::pane(), false);
         }
@@ -1478,19 +1487,14 @@ impl cce_ui::engine::Application for State {
                 }
             }
         }
-        // The well's rim over the exhibits, then the area's scrollbar over the
-        // rim: the toolkit's relief scrollbar (a groove track, a raised thumb —
-        // the TreeList's), the flat quads only when relief is off.
+        // The well's rim over the exhibits, then the scrollbar's FORE copy over
+        // the rim, at the fade a scroll raised it to (nothing while it is sunk:
+        // then only the idle copy under the floor shows). Flat pills both
+        // times — shader-lit relief does not fade with a vertex alpha.
         if !self.is_child {
             let (well, radius, _) = self.exhibit_well();
             pc.well_rim(well, radius, cce_ui::layout::control_relief());
-            if cce_ui::layout::control_relief() {
-                self.exhibit_scroll.paint_scrollbar_relief(&mut pc);
-            } else {
-                for (sx, sy, sw, sh, sc) in self.exhibit_scroll.extra_quads() {
-                    pc.quad(Rect { x: sx, y: sy, width: sw, height: sh }, sc);
-                }
-            }
+            self.exhibit_scroll.paint_scrollbar_pills(&mut pc, self.exhibit_scroll.scrollbar_fade());
         }
 
         // ── Popovers: in-frame, on top of everything. PaintCtx is a
@@ -1681,7 +1685,10 @@ impl cce_ui::engine::Application for State {
 
         if state == ElementState::Pressed {
             let mut clicked_idx = None;
-            // The exhibit area's scrollbar takes a press on its track before any exhibit.
+            // The exhibit area's scrollbar takes a press on its track before any
+            // exhibit — while it is RAISED. A sunk bar is behind the well's floor
+            // and is not hit, so a press on its lane falls through to the exhibit
+            // under it (`ScrollBox::hit_test_scrollbar` gates on the latch).
             let exhibit_bar = !self.is_child && self.exhibit_scroll.mouse_input(button, state, lx, ly, &mut self.ui_context);
             if exhibit_bar {
                 changed = true;