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

commitbf0e6b09c53ced9ca16d3ccf0bb76d29babb9af6
parent06830ff25a
authorLucas Galante <[email protected]>
date2026-09-09 12:52
fix(treelist): the fields are arranged in the content rect, not the block

rect_assigned hands a widget the adapter's whole block — the detached label
strip plus the content — while paint draws the well at the content rect. The
tree placed its search box, add-key button, header and scroll box from the
block, so with a label they sat one strip above the well: the search row
straddled the well's top edge over the label and the header text was clipped
away outside its bounds. Arrange them in arrange_children's content rect.
Regression test with a labelled tree.

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

 src/widget/container/treelist.rs | 32 +++++++++++++++++++++++++++++---
 1 file changed, 29 insertions(+), 3 deletions(-)

diff --git a/src/widget/container/treelist.rs b/src/widget/container/treelist.rs
index 0ce43a0..373493c 100644
--- a/src/widget/container/treelist.rs
+++ b/src/widget/container/treelist.rs
@@ -637,9 +637,14 @@ impl TreeList {
 }
 
 impl Layout for TreeList {
-    /// The legacy `set_rect` body: cache the rect on the internal base and arrange the
-    /// field widgets (search box, add-key button, popover box, scroll box).
-    fn rect_assigned(&mut self, rect: Rect) {
+    /// The legacy `set_rect` body: cache the CONTENT rect on the internal base and
+    /// arrange the field widgets (search box, add-key button, popover box, scroll box)
+    /// in it. The content rect, not `rect_assigned`'s block: the adapter's block holds
+    /// the detached label strip above the content, and `paint` draws the well at the
+    /// content rect — fields placed from the block sat one strip above the well, the
+    /// search box straddling its top edge over the label and the header row's text
+    /// clipped away outside its bounds (the gallery's labelled tree).
+    fn arrange_children(&mut self, rect: Rect, _host: *mut (dyn WidgetHost + 'static)) {
         let (x, y, w, h) = (rect.x, rect.y, rect.width, rect.height);
         self.base.x = x;
         self.base.y = y;
@@ -1419,6 +1424,27 @@ mod tests {
     use super::*;
     use crate::context::UiContext;
 
+    /// A labelled tree's fields sit in its content rect, below the label strip — where
+    /// `paint` draws the well — not at the top of the block the adapter is given.
+    #[test]
+    fn a_labelled_trees_fields_are_in_its_content_rect() {
+        let mut tree_list = TreeList::new().with_label("TreeList");
+        let strip = tree_list.label_strip();
+        assert!(strip > 0.0, "a detached label has a strip");
+        tree_list.set_rect(10.0, 52.0, 380.0, 200.0 + strip);
+        let content_y = 52.0 + strip;
+        let (_, sy, _, sh) = tree_list.search_box.rect();
+        assert_eq!(sy, content_y + 6.0, "the search box is inside the well, one margin down");
+        let (_, by, _, _) = tree_list.add_key_btn.rect();
+        assert_eq!(by, sy, "the add-key button shares the search row");
+        let header_y = content_y + sh + 12.0;
+        assert_eq!(tree_list.scroll_box.base.y, header_y + 26.0, "the rows start under the header");
+        let labels = tree_list.own_labels();
+        let key = labels.iter().find(|(l, _)| l.text == "Key").expect("a Key header");
+        assert_eq!(key.0.y, header_y + 6.0, "the header text is in the header band");
+        assert_eq!(tree_list.scroll_box.base.y + tree_list.scroll_box.base.h, 52.0 + strip + 200.0, "the rows end at the block's bottom");
+    }
+
     #[test]
     fn test_treelist_blocks_window_drag() {
         // root plate container is DELETED: dissolved windows ask `drag_allowed_at` instead — same