git.lucas.co / cce-designer
graphic design tool
git clone https://git.lucas.co/cce-designer.git

commit416dd1785d6ff6d326eb68c43e9443b5009bbab6
parent63cbd85a78
authorLucas Galante <lsgalante12@gmail.com>
date2026-10-06 08:25
feat: the spreadsheet's scrollbars sink behind its plate

The render arm draws the idle copy of cce-ui's new scrollbar cross
before the spreadsheet's plate, as the params pane straddles its bar;
the widget fades the fore copy in over the cells on a scroll.

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

 CLAUDE.md     | 12 ++++++++++++
 src/render.rs | 17 +++++++++++++----
 src/slots.rs  |  6 ++++++
 3 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index dee5689..3150e47 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -2175,6 +2175,18 @@ selected row's marker, stood at the first frame while the scene beside
 them played. `rows_selected_inside_a_simnet_follow_the_simulation` is the
 test.
 
+**The spreadsheet's scrollbars are a cross behind its plate** (since
+2026-10-06; cce-ui's CLAUDE.md, "A spreadsheet's scrollbars are a cross
+behind its plate"): the vertical bar down the pane's centre line, the
+horizontal one across the body's, idling behind the frosted plate until a
+scroll raises them, held in front while the pointer is on a raised one.
+It is the params pane's straddle and the dialog's bar, which ride their
+centre lines the same way. The render arm draws the idle copy before the
+plate (`Spreadsheet::paint_scrollbars`, through `WidgetSlots::spreadsheet`);
+the widget paints the fore copy itself. A press on a sunk bar's lane is a
+press on the row under it. Checked in a shadow session: sunk, raised by a
+scroll, held by hover past the hold, sunk again once the pointer left.
+
 ### An edit is in from the next frame
 
 An edit inside a simnet's chain, or to its seed, does not restart the
diff --git a/src/render.rs b/src/render.rs
index 0c205de..eae13e8 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -376,11 +376,20 @@ impl State {
         } else if idx == SPREADSHEET_IDX {
             // Modern-paint pane: the plate from the designer (span-widened
             // radii + focus tint), then Spreadsheet::paint authors the grid —
-            // header band, zebra rows, separators, dividers, scrollbar. A
-            // subtree painter since cce-ui@f1cd939: its text passes through
-            // paint_self verbatim, carrying the per-column clamp bounds the
-            // own-labels bridge would drop; append_frame_text skips the slot.
+            // header band, zebra rows, separators, dividers, and the fore
+            // copy of its scrollbars. A subtree painter since cce-ui@f1cd939:
+            // its text passes through paint_self verbatim, carrying the
+            // per-column clamp bounds the own-labels bridge would drop;
+            // append_frame_text skips the slot.
             let (wx, wy, ww2, wh2) = w.rect();
+            // The scrollbar cross's idle copy, BEHIND the plate — the params
+            // pane's straddle: the frosted plate dims it, and the widget
+            // fades its fore copy in over the cells when a scroll raises it.
+            let sheet = self.slots.spreadsheet();
+            let sheet_rect = rect(wx, wy, ww2, wh2);
+            if sheet.scrollbars_shown(sheet_rect) {
+                sheet.paint_scrollbars(sheet_rect, pc, 1.0);
+            }
             append_widget_plate_radii(w, pc, self.plate_focus_tint(idx), self.pane_plate_radii(wx, wy, ww2, wh2));
             w.paint_self(&self.ui_context, pc);
         } else if idx == VIEWPORT_IDX {
diff --git a/src/slots.rs b/src/slots.rs
index 70b7868..6cdf8dc 100644
--- a/src/slots.rs
+++ b/src/slots.rs
@@ -187,6 +187,12 @@ impl WidgetSlots {
         self.param.as_any_mut().downcast_mut::<ParametersBg>().expect("PARAM_IDX must be a ParametersBg")
     }
 
+    /// The spreadsheet as itself, for what its controller trait does not
+    /// carry — the scrollbars the render arm straddles around the plate.
+    pub fn spreadsheet(&self) -> &Spreadsheet {
+        self.spreadsheet.as_any().downcast_ref::<Spreadsheet>().expect("SPREADSHEET_IDX must be a Spreadsheet")
+    }
+
     pub fn spreadsheet_mut(&mut self) -> &mut dyn cce_ui::widget::SpreadsheetController {
         self.spreadsheet.as_any_mut().downcast_mut::<Spreadsheet>().expect("SPREADSHEET_IDX must be a Spreadsheet")
     }