git.lucas.co / cce-mail
mail client (IMAP/SMTP)
git clone https://git.lucas.co/cce-mail.git

commit49de00b6b101737096627074159532a08d8131cf
parentcb0b24d062
authorLucas Galante <[email protected]>
date2026-07-23 11:37
feat: wire list keyboard scrolling + scrollbar above rows

ScrollRegion::keyboard() (ArrowUp/Down, PageUp/Down, Home/End, ctrl
n/p) existed since the Phase 6q port but was never called — the list
had wheel-only scrolling. It now runs in handle_key_input, scoped by
the region's own hover-or-focus gate, after app shortcuts and the
detail-body scroll (which wins while the detail pane is hovered), and
never while the search box is editing.

Also split ScrollRegion::push_quads into background + scrollbar halves
and emit the scrollbar after the rows: drawn before them, the thumb
passed under the opaque row panels and peeked through the 4px inter-row
gaps as dotted segments (visible whenever the list overflows — which
the 50-message fetch makes the common case).

Live-verified via keypress injection: PageDown pages 20-14 -> 12-07,
Home returns to top, thumb renders as one continuous bar over the rows.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/main.rs          | 15 +++++++++++++++
 src/scroll_region.rs | 12 ++++++++----
 2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index a78a4b1..c2accf2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2023,6 +2023,13 @@ impl Application for ClearEmailApp {
                 }
             }
         }
+        // Scrollbar after the rows so the thumb rides on top of them instead of
+        // peeking through the inter-row gaps.
+        {
+            let mut sb_quads = Vec::new();
+            self.email_list.push_scrollbar_quads(&mut sb_quads);
+            quads.extend(sb_quads);
+        }
 
         // 4. Detail View Area
         if self.current_folder == Folder::Accounts {
@@ -2675,6 +2682,14 @@ impl Application for ClearEmailApp {
                 }
             }
 
+            // Email/accounts list keyboard scrolling. ScrollRegion scopes itself to
+            // hover-or-focus (a row click focuses the region, a press elsewhere
+            // unfocuses); the search box owns the keys while editing, and a
+            // body-scroll above wins when the detail pane is hovered.
+            if !handled && !self.search_box.editing && self.email_list.keyboard(event) {
+                handled = true;
+            }
+
             if !handled && self.current_folder != Folder::Accounts && self.search_box.editing {
                 if ctx.propagate_event(&kev, self.search_box.id()) {
                     handled = true;
diff --git a/src/scroll_region.rs b/src/scroll_region.rs
index 7ac084e..17c7779 100644
--- a/src/scroll_region.rs
+++ b/src/scroll_region.rs
@@ -214,12 +214,16 @@ impl ScrollRegion {
         (self.scroll_y - old).abs() > 0.01
     }
 
-    /// The legacy frame, single-drawn: 1px rounded border (focus/hover tinted, from
-    /// `List::solid_border`), inset rounded bg, then the scrollbar track and thumb ON TOP.
-    /// The legacy `ScrollBox::extra_quads` emission: flat background, then the
-    /// scrollbar track + thumb when the content overflows.
+    /// The region's flat background (the legacy `ScrollBox::extra_quads` fill).
+    /// The scrollbar is split into [`push_scrollbar_quads`](Self::push_scrollbar_quads)
+    /// so the host can emit it AFTER the rows — drawn together, the rows paint over
+    /// the thumb and it peeks through the inter-row gaps as dotted segments.
     pub fn push_quads(&self, quads: &mut Vec<(f32, f32, f32, f32, [f32; 4])>) {
         quads.push((self.x, self.y, self.w, self.h, cce_ui::color::list_bg_color()));
+    }
+
+    /// Scrollbar track + thumb when the content overflows; emit after the rows.
+    pub fn push_scrollbar_quads(&self, quads: &mut Vec<(f32, f32, f32, f32, [f32; 4])>) {
         if self.content_h > self.viewport_h {
             let (sb_x, track_y, sb_w, track_h, thumb_y, thumb_h) = self.scrollbar_geom();
             quads.push((sb_x, track_y, sb_w, track_h, cce_ui::color::scrollbar_track_color()));