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

commita7b152c41626a44c34210541b01ffb89d0fb99af
parent872bc903da
authorLucas Galante <[email protected]>
date2026-07-12 00:46
refactor(widget)!: DELETE ScrollBar (moved into settings); ScrollBox demoted off Element (Phase 6av)

ScrollBar's only consumer is cce-system-settings' page scrollbar (the
dissolved Page subtree's one surviving widget) — the file moves there
verbatim (its Page-downcast write-back died with Page in 6au) and the
cce-ui type is deleted.

ScrollBox is never registered into the ctx tree by either consumer
(TreeList and cce-test-interface's panel copy drive it entirely through
concrete calls), so its Element impl was pure dyn-dispatch ballast:
demoted to a plain struct. The former Element entry points the
consumers forward stay as inherent methods with default-derived
behavior preserved exactly (cursor_moved cover-check, tick/is_dragging
constant-false parity, hit_test over the bare rect). The legacy focus
claim on scrollbar clicks becomes focus::clear_focus() — its only
observable effect was unfocusing the previous holder (nothing ever
queried focus ON the scroll box, and its own unfocus was a no-op).
The scroll-ancestor text clamps in painter/model fold to None: with
ScrollBox off the trait, no tree parent can ever be one (they never
matched at runtime anyway — ScrollBox was never a registered parent).
TreeList drops its no-op scroll_box.prepare_text call.

169 tests pass (the two Element-focus/propagation-based ScrollBox tests
rewritten against the hover gate). A/B on the live compositor:
data-editor AE=0 with live wheel-scroll + scrollbar track jump-scroll
verified on a 100-key tree; settings processes page diff = live process
rows only; test-interface sub-threshold (launch animation).

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018u7qTwzX95dd5ysAkaSCLk

 src/scene/painter.rs               |  18 +---
 src/widget/container/mod.rs        |   2 -
 src/widget/container/scroll_bar.rs | 195 -------------------------------------
 src/widget/container/scroll_box.rs | 130 +++++++++++++++----------
 src/widget/container/treelist.rs   |   1 -
 src/widget/mod.rs                  |   2 +-
 src/widget/model.rs                |  34 +------
 7 files changed, 88 insertions(+), 294 deletions(-)

diff --git a/src/scene/painter.rs b/src/scene/painter.rs
index 6897a55..ced2d4a 100644
--- a/src/scene/painter.rs
+++ b/src/scene/painter.rs
@@ -95,20 +95,10 @@ pub fn paint_legacy_leaf(
     }
 }
 
-/// The scroll-ancestor text clamp the deleted default fonted getter applied: the nearest
-/// ScrollBox/List ancestor's viewport, if any.
-pub fn scroll_ancestor_text_bounds(w: &dyn Element, ui: &UiContext) -> Option<[f32; 4]> {
-    let mut curr = w.parent(ui);
-    while let Some(parent_ptr) = curr {
-        let parent = unsafe { &*parent_ptr };
-        if let Some(scroll_box) = parent.as_any().downcast_ref::<crate::widget::ScrollBox>() {
-            let (sb_x, _, sb_w, _) = scroll_box.rect();
-            let view_min = scroll_box.viewport_y + 4.0;
-            let view_max = scroll_box.viewport_y + scroll_box.viewport_h - 4.0;
-            return Some([sb_x, view_min, sb_x + sb_w, view_max]);
-        }
-        curr = parent.parent(ui);
-    }
+/// The scroll-ancestor text clamp the deleted default fonted getter applied. Always `None`
+/// since Phase 6av: ScrollBox (the last scroll ancestor type) was demoted to a plain
+/// embedded struct — it never appeared as a tree parent, so the walk never matched.
+pub fn scroll_ancestor_text_bounds(_w: &dyn Element, _ui: &UiContext) -> Option<[f32; 4]> {
     None
 }
 
diff --git a/src/widget/container/mod.rs b/src/widget/container/mod.rs
index e1014c4..46d80a4 100644
--- a/src/widget/container/mod.rs
+++ b/src/widget/container/mod.rs
@@ -9,7 +9,6 @@ pub mod spreadsheet;
 pub mod scroll_box;
 pub mod switcher;
 pub mod paginator;
-pub mod scroll_bar;
 pub mod treelist;
 pub mod vbox;
 pub mod hbox;
@@ -25,7 +24,6 @@ pub use spreadsheet::Spreadsheet;
 pub use scroll_box::ScrollBox;
 pub use switcher::Switcher;
 pub use paginator::Paginator;
-pub use scroll_bar::ScrollBar;
 pub use treelist::{TreeList, TreeElement};
 pub use vbox::VBox;
 pub use hbox::HBox;
diff --git a/src/widget/container/scroll_bar.rs b/src/widget/container/scroll_bar.rs
deleted file mode 100644
index 3df499d..0000000
--- a/src/widget/container/scroll_bar.rs
+++ /dev/null
@@ -1,195 +0,0 @@
-use crate::widget::*;
-use crate::context::UiContext;
-
-pub struct ScrollBar {
-    pub base: Widget,
-    pub scroll_y: f32,
-    pub content_h: f32,
-    pub viewport_h: f32,
-    pub dragging: bool,
-    pub parent: Option<*mut (dyn Element + 'static)>,
-    pub children: Vec<*mut (dyn Element + 'static)>,
-}
-
-impl std::fmt::Debug for ScrollBar {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("ScrollBar")
-            .field("base", &self.base)
-            .field("scroll_y", &self.scroll_y)
-            .field("content_h", &self.content_h)
-            .field("viewport_h", &self.viewport_h)
-            .field("dragging", &self.dragging)
-            .finish()
-    }
-}
-
-impl Clone for ScrollBar {
-    fn clone(&self) -> Self {
-        Self {
-            base: self.base.clone(),
-            scroll_y: self.scroll_y,
-            content_h: self.content_h,
-            viewport_h: self.viewport_h,
-            dragging: self.dragging,
-            parent: self.parent,
-            children: self.children.clone(),
-        }
-    }
-}
-
-impl ScrollBar {
-    pub fn new() -> Self {
-        let mut base = Widget::new();
-        base.w = 6.0;
-        Self {
-            base,
-            scroll_y: 0.0,
-            content_h: 0.0,
-            viewport_h: 0.0,
-            dragging: false,
-            parent: None,
-            children: Vec::new(),
-        }
-    }
-
-    pub fn update(&mut self, scroll_y: f32, content_h: f32, viewport_h: f32) {
-        self.scroll_y = scroll_y;
-        self.content_h = content_h;
-        self.viewport_h = viewport_h;
-    }
-
-    pub fn get_thumb_rect(&self) -> Option<(f32, f32, f32, f32)> {
-        if self.content_h <= self.viewport_h || self.viewport_h <= 0.0 || self.base.h <= 0.0 {
-            return None;
-        }
-        let sb_x = self.base.x;
-        let sb_w = self.base.w;
-        let sb_track_h = self.base.h;
-        let sb_track_y = self.base.y;
-
-        let visible_ratio = self.viewport_h / self.content_h;
-        let thumb_h = if sb_track_h <= 20.0 {
-            sb_track_h
-        } else {
-            (sb_track_h * visible_ratio).clamp(20.0, sb_track_h)
-        };
-        let max_scroll = (self.content_h - self.viewport_h).max(0.0);
-        let scroll_ratio = if max_scroll > 0.0 { self.scroll_y / max_scroll } else { 0.0 };
-        let thumb_y = sb_track_y + scroll_ratio * (sb_track_h - thumb_h);
-
-        Some((sb_x, thumb_y, sb_w, thumb_h))
-    }
-}
-
-impl Element for ScrollBar {
-    crate::impl_widget_base!(ScrollBar);
-
-    fn color(&self) -> [f32; 4] {
-        [0.0, 0.0, 0.0, 0.0]
-    }
-
-    fn hit_test(&self, px: f32, py: f32, ctx: &UiContext) -> bool {
-        if ctx.is_coordinate_covered(self as *const Self as *const () as usize, px, py) {
-            return false;
-        }
-        let hit_margin = 6.0;
-        px >= self.base.x - hit_margin && px <= self.base.x + self.base.w + hit_margin && py >= self.base.y && py <= self.base.y + self.base.h
-    }
-
-    fn on_cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
-        let mut changed = false;
-        let is_hit = self.hit_test(px, py, ctx);
-        if self.base.hovered != is_hit {
-            self.base.hovered = is_hit;
-            changed = true;
-        }
-
-        if self.dragging {
-            if let Some((_, _, _, thumb_h)) = self.get_thumb_rect() {
-                let sb_track_y = self.base.y;
-                let sb_track_h = self.base.h;
-                let track_scroll_range = sb_track_h - thumb_h;
-                if track_scroll_range > 0.0 {
-                    let mouse_y_in_track = (py - sb_track_y).clamp(0.0, sb_track_h);
-                    let scroll_ratio = (mouse_y_in_track - thumb_h / 2.0) / track_scroll_range;
-                    let max_scroll = (self.content_h - self.viewport_h).max(0.0);
-                    let new_scroll_y = (scroll_ratio.clamp(0.0, 1.0) * max_scroll).clamp(0.0, max_scroll);
-                    if (self.scroll_y - new_scroll_y).abs() > 0.01 {
-                        self.scroll_y = new_scroll_y;
-                        changed = true;
-                    }
-                }
-            }
-        }
-        changed
-    }
-
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32, ctx: &mut UiContext) -> bool {
-        if button == MouseButton::Left {
-            match state {
-                ElementState::Pressed => {
-                    if self.hit_test(px, py, ctx) {
-                        self.dragging = true;
-                        self.on_cursor_moved(px, py, ctx);
-                        return true;
-                    }
-                }
-                ElementState::Released => {
-                    if self.dragging {
-                        self.dragging = false;
-                        return true;
-                    }
-                }
-            }
-        }
-        false
-    }
-
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
-        let mut quads = Vec::new();
-        if self.content_h > self.viewport_h && self.base.h > 0.0 {
-            quads.push((self.base.x, self.base.y, self.base.w, self.base.h, [0.15, 0.15, 0.20, 0.3]));
-
-            if let Some((sb_x, thumb_y, sb_w, thumb_h)) = self.get_thumb_rect() {
-                let thumb_color = if self.dragging {
-                    [0.70, 0.70, 0.75, 0.6]
-                } else if self.base.hovered {
-                    [0.65, 0.65, 0.70, 0.5]
-                } else {
-                    [0.60, 0.60, 0.65, 0.4]
-                };
-                quads.push((sb_x, thumb_y, sb_w, thumb_h, thumb_color));
-            }
-        }
-        quads
-    }
-
-    fn parent(&self, _ctx: &UiContext) -> Option<*mut (dyn Element + 'static)> {
-        self.parent
-    }
-
-    fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>, _ctx: &mut UiContext) {
-        self.parent = parent;
-    }
-
-    fn children(&self, _ctx: &UiContext) -> Vec<*mut (dyn Element + 'static)> {
-        self.children.clone()
-    }
-
-    fn add_child(&mut self, child: *mut (dyn Element + 'static), _ctx: &mut UiContext) {
-        self.children.push(child);
-    }
-
-    fn clear_children(&mut self, _ctx: &mut UiContext) {
-        self.children.clear();
-    }
-}
-
-impl Drop for ScrollBar {
-    fn drop(&mut self) {
-        clear_widget_references(self);
-    }
-}
-
-unsafe impl Send for ScrollBar {}
-unsafe impl Sync for ScrollBar {}
diff --git a/src/widget/container/scroll_box.rs b/src/widget/container/scroll_box.rs
index 19f7151..9379d95 100644
--- a/src/widget/container/scroll_box.rs
+++ b/src/widget/container/scroll_box.rs
@@ -1,3 +1,10 @@
+//! Embedded scroll-math + scrollbar-chrome helper (Phase 6av: DEMOTED from `Element` to a
+//! plain struct). Never registered into the ctx tree by either consumer — TreeList and
+//! cce-test-interface's panel copy drive it entirely through concrete calls — so the
+//! `Element` impl was pure dyn-dispatch ballast. The former Element-default entry points the
+//! consumers forward (`cursor_moved`, `tick`, drag hooks, `is_dragging`) are kept as
+//! inherent methods with the exact default-derived behavior.
+
 use crate::widget::*;
 
 #[derive(Debug, Clone)]
@@ -11,7 +18,6 @@ pub struct ScrollBox {
     viewport_offset_h: f32,
     pub show_border: bool,
     pub show_background: bool,
-    pub parent: Option<*mut (dyn Element + 'static)>,
     pub children: Vec<*mut (dyn Element + 'static)>,
     pub scrollbar_dragging: bool,
     pub drag_offset_y: f32,
@@ -29,7 +35,6 @@ impl ScrollBox {
             viewport_offset_h: 0.0,
             show_border: true,
             show_background: true,
-            parent: None,
             children: Vec::new(),
             scrollbar_dragging: false,
             drag_offset_y: 0.0,
@@ -69,12 +74,8 @@ impl ScrollBox {
     }
 }
 
-impl Element for ScrollBox {
-    crate::impl_widget_base!(ScrollBox);
-    fn is_scrollable(&self) -> bool { true }
-    fn blocks_backplate_drag(&self) -> bool { true }
-
-    fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
+impl ScrollBox {
+    pub fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
         self.base.x = x;
         self.base.y = y;
         self.base.w = w;
@@ -82,22 +83,33 @@ impl Element for ScrollBox {
         self.viewport_y = y + self.viewport_offset_y;
         self.viewport_h = h + self.viewport_offset_h;
     }
-    fn color(&self) -> [f32; 4] { crate::color::list_bg_color() }
 
-    fn corner_radius(&self) -> f32 {
-        crate::layout::list_corner_radius()
+    /// The legacy `Element` default hit test over the base rect (ScrollBox never carried a
+    /// label or row expansion, so those branches are folded away).
+    fn hit_test(&self, px: f32, py: f32, ctx: &UiContext) -> bool {
+        if ctx.is_coordinate_covered(self as *const Self as *const () as usize, px, py) {
+            return false;
+        }
+        let (x, y, w, h) = (self.base.x, self.base.y, self.base.w, self.base.h);
+        if w <= 0.0 || h <= 0.0 {
+            return false;
+        }
+        px >= x && px <= x + w && py >= y && py <= y + h
     }
 
-    fn focus(&mut self) {
-        focus::set_focused(self);
+    /// The legacy focus claim on scrollbar/list clicks: its only observable effect was
+    /// unfocusing the previously focused widget (nothing ever queried focus ON the scroll
+    /// box through the thread-local, and its own `unfocus` was a no-op) — so just release
+    /// the current holder instead of storing a pointer to a non-Element.
+    fn claim_focus(&self) {
+        focus::clear_focus();
     }
-    fn unfocus(&mut self) {}
 
-    fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+    pub fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32, ctx: &mut UiContext) -> bool {
         if button == MouseButton::Left {
             if state == ElementState::Pressed {
                 if self.hit_test_scrollbar(px, py) {
-                    self.focus();
+                    self.claim_focus();
                     self.scrollbar_dragging = true;
                     
                     let sb_track_h = self.viewport_h - 8.0;
@@ -131,7 +143,7 @@ impl Element for ScrollBox {
                     self.scrollbar_dragging = false;
                 }
                 if self.hit_test(px, py, ctx) {
-                    self.focus();
+                    self.claim_focus();
                 }
             } else if state == ElementState::Released {
                 self.scrollbar_dragging = false;
@@ -140,13 +152,19 @@ impl Element for ScrollBox {
         false
     }
 
-    fn draggable(&self) -> bool {
+    pub fn draggable(&self) -> bool {
         self.scrollbar_dragging
     }
 
-    fn drag_begin(&mut self, _px: f32, _py: f32) {}
+    /// Legacy `Element` default parity: ScrollBox never overrode `is_dragging` — TreeList
+    /// forwards it and always got `false`.
+    pub fn is_dragging(&self) -> bool {
+        false
+    }
+
+    pub fn drag_begin(&mut self, _px: f32, _py: f32) {}
 
-    fn drag_update(&mut self, _px: f32, py: f32) -> bool {
+    pub fn drag_update(&mut self, _px: f32, py: f32) -> bool {
         if !self.scrollbar_dragging {
             return false;
         }
@@ -172,11 +190,26 @@ impl Element for ScrollBox {
         (self.scroll_y - old_scroll).abs() > 0.01
     }
 
-    fn drag_end(&mut self) {
+    pub fn drag_end(&mut self) {
         self.scrollbar_dragging = false;
     }
 
-    fn on_cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+    /// The legacy `Element` default `cursor_moved` entry (cce-test-interface's panel copy
+    /// calls it): cover-check clears hover, otherwise falls into `on_cursor_moved`. The
+    /// MouseLeave dispatch the default performed was a no-op for ScrollBox.
+    pub fn cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+        ctx.set_cursor_pos(px, py);
+        if ctx.is_coordinate_covered(self as *const Self as *const () as usize, px, py) {
+            let was = self.base.hovered;
+            if was {
+                self.base.hovered = false;
+            }
+            return was;
+        }
+        self.on_cursor_moved(px, py, ctx)
+    }
+
+    pub fn on_cursor_moved(&mut self, px: f32, py: f32, ctx: &mut UiContext) -> bool {
         let mut changed = false;
         if self.scrollbar_dragging {
             let sb_track_h = self.viewport_h - 8.0;
@@ -211,7 +244,12 @@ impl Element for ScrollBox {
         changed
     }
 
-    fn mouse_wheel(&mut self, delta: &MouseScrollDelta, px: f32, py: f32, ctx: &mut UiContext) -> bool {
+    /// Legacy `Element` default parity (cce-test-interface's panel copy ticks it).
+    pub fn tick(&mut self, _dt: f32, _ctx: &mut UiContext) -> bool {
+        false
+    }
+
+    pub fn mouse_wheel(&mut self, delta: &MouseScrollDelta, px: f32, py: f32, ctx: &mut UiContext) -> bool {
         if self.hit_test(px, py, ctx) {
             let scroll_speed = 24.0;
             let dy = match delta {
@@ -227,9 +265,9 @@ impl Element for ScrollBox {
         }
     }
 
-    fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
+    pub fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
         let mut quads = Vec::new();
-        
+
         // Background
         if self.show_background {
             quads.push((self.base.x, self.base.y, self.base.w, self.base.h, crate::color::list_bg_color()));
@@ -264,7 +302,7 @@ impl Element for ScrollBox {
         quads
     }
 
-    fn keyboard_input(&mut self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
+    pub fn keyboard_input(&mut self, event: &KeyEvent, ctx: &mut UiContext) -> bool {
         let self_addr = self as *const Self as *const () as usize;
         let has_focus = ctx.is_focused_addr(self_addr) || {
             let mut current = ctx.focused_widget;
@@ -339,21 +377,6 @@ impl Element for ScrollBox {
         }
     }
 
-    fn parent(&self, _ctx: &UiContext) -> Option<*mut (dyn Element + 'static)> { self.parent }
-    fn set_parent(&mut self, parent: Option<*mut (dyn Element + 'static)>, _ctx: &mut UiContext) { self.parent = parent; }
-    fn children(&self, _ctx: &UiContext) -> Vec<*mut (dyn Element + 'static)> { self.children.clone() }
-    fn add_child(&mut self, child: *mut (dyn Element + 'static), _ctx: &mut UiContext) { self.children.push(child); }
-    fn clear_children(&mut self, _ctx: &mut UiContext) { self.children.clear(); }
-
-    fn layout_ignore(&self) -> bool {
-        true
-    }
-}
-
-impl Drop for ScrollBox {
-    fn drop(&mut self) {
-        clear_widget_references(self);
-    }
 }
 
 unsafe impl Send for ScrollBox {}
@@ -406,11 +429,12 @@ mod tests {
         let mut sb = ScrollBox::new();
         sb.set_rect(10.0, 20.0, 100.0, 100.0);
         sb.update_bounds(300.0, 20.0, 100.0); // max_scroll = 200.0
-        
+
         let mut ctx = UiContext::new();
-        // Focus the scroll box
-        ctx.set_focused(&mut sb);
-        
+        // Hover the scroll box (the focus path took a ctx-registered Element; as a plain
+        // struct the hovered branch is the live gate).
+        ctx.set_cursor_pos(50.0, 50.0);
+
         // 1. ArrowDown key
         let event_down = KeyEvent {
             state: ElementState::Pressed,
@@ -473,16 +497,12 @@ mod tests {
     }
 
     #[test]
-    fn test_scroll_box_non_focused_hovered_scrolling() {
+    fn test_scroll_box_keys_gated_on_hover() {
         let mut sb = ScrollBox::new();
         sb.set_rect(10.0, 20.0, 100.0, 100.0);
         sb.update_bounds(300.0, 20.0, 100.0); // max_scroll = 200.0
 
         let mut ctx = UiContext::new();
-        ctx.register_widget(sb.base.id(), &mut sb);
-        
-        // Set cursor position over the scroll box
-        ctx.set_cursor_pos(50.0, 50.0);
 
         let event_down = KeyEvent {
             state: ElementState::Pressed,
@@ -493,8 +513,14 @@ mod tests {
             shift: false,
         };
 
-        let root_ptr = &mut sb as *mut ScrollBox as *mut (dyn Element + 'static);
-        assert!(ctx.propagate_event(&Event::KeyInput(event_down), root_ptr));
+        // Cursor away from the box, nothing focused: keys are ignored.
+        ctx.set_cursor_pos(500.0, 500.0);
+        assert!(!sb.keyboard_input(&event_down, &mut ctx));
+        assert_eq!(sb.scroll_y, 0.0);
+
+        // Hovered: keys scroll.
+        ctx.set_cursor_pos(50.0, 50.0);
+        assert!(sb.keyboard_input(&event_down, &mut ctx));
         assert_eq!(sb.scroll_y, 24.0);
     }
 }
diff --git a/src/widget/container/treelist.rs b/src/widget/container/treelist.rs
index 9b1fd4d..06d33bb 100644
--- a/src/widget/container/treelist.rs
+++ b/src/widget/container/treelist.rs
@@ -703,7 +703,6 @@ impl Paint for TreeList {
         if self.add_key_popover_open {
             self.add_key_popover_box.prepare_text(fs);
         }
-        self.scroll_box.prepare_text(fs);
         if self.editing_key_idx.is_some() {
             self.edit_box.prepare_text(fs);
         }
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index d824809..6a81f9c 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -739,7 +739,7 @@ pub use self::container::{
     ColumnsLayout, MosaicLayout, ReverseMosaicLayout,
     Header, ContentBg, ParametersBg,
     ScrollBox, MenuBar, Spreadsheet, Breadcrumb,
-    Switcher, Paginator, ScrollBar, TreeList, TreeElement
+    Switcher, Paginator, TreeList, TreeElement
 };
 pub use self::display::{
     TextLabel, Label, StyledLabel, LabelPrim, TextItem, Svg, UsageBar,
diff --git a/src/widget/model.rs b/src/widget/model.rs
index 5150e62..26b8506 100644
--- a/src/widget/model.rs
+++ b/src/widget/model.rs
@@ -848,7 +848,7 @@ impl<W: Layout + Paint + Input + 'static> Adapted<W> {
         self.own_labels_with_prim_font(ctx, Paint::text_font(&self.inner))
     }
 
-    fn own_labels_with_prim_font(&self, ctx: &UiContext, prim_font: Option<String>) -> Vec<(TextLabel, Option<String>, Option<[f32; 4]>)> {
+    fn own_labels_with_prim_font(&self, _ctx: &UiContext, prim_font: Option<String>) -> Vec<(TextLabel, Option<String>, Option<[f32; 4]>)> {
         let base_font = Paint::widget_font(&self.inner);
         let mut fonted: Vec<(TextLabel, Option<String>)> = Vec::new();
         if self.visible() {
@@ -874,36 +874,12 @@ impl<W: Layout + Paint + Input + 'static> Adapted<W> {
                 .collect();
         }
 
-        let mut labels = fonted
+        // The legacy scroll-ancestor clamp ended here: always a no-op since Phase 6av —
+        // ScrollBox (the last scroll ancestor type) never appeared as a tree parent.
+        fonted
             .into_iter()
             .map(|(l, font)| (l, font, None::<[f32; 4]>))
-            .collect::<Vec<_>>();
-        let mut curr = Element::parent(self, ctx);
-        let mut scroll_box_bounds = None;
-        while let Some(parent_ptr) = curr {
-            let parent = unsafe { &*parent_ptr };
-            if let Some(scroll_box) = parent.as_any().downcast_ref::<crate::widget::ScrollBox>() {
-                let (sb_x, _, sb_w, _) = parent.rect();
-                let view_min = scroll_box.viewport_y + 4.0;
-                let view_max = scroll_box.viewport_y + scroll_box.viewport_h - 4.0;
-                scroll_box_bounds = Some([sb_x, view_min, sb_x + sb_w, view_max]);
-                break;
-            }
-            curr = parent.parent(ctx);
-        }
-        if let Some(sb_bounds) = scroll_box_bounds {
-            for item in &mut labels {
-                if let Some(ref mut b) = item.2 {
-                    b[0] = b[0].max(sb_bounds[0]);
-                    b[1] = b[1].max(sb_bounds[1]);
-                    b[2] = b[2].min(sb_bounds[2]);
-                    b[3] = b[3].min(sb_bounds[3]);
-                } else {
-                    item.2 = Some(sb_bounds);
-                }
-            }
-        }
-        labels
+            .collect::<Vec<_>>()
     }
 
     /// The base-label text of a *detached*-label widget — a replica of the legacy default