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

commitafd1288d720976ab8091b126626b935044803bb3
parentf065703f60
authorLucas Galante <[email protected]>
date2026-09-09 11:09
feat(group): headroom — the room a titled lasso takes above its hull

A lasso is laid out by its members, so a host placing them in rows has no
way to know how much to leave above the first one for the title tab. Group
now says: `tab_height` (the tab, zero without a label) and `headroom` (the
padding plus the tab). `frame` sizes its tab through the same method.

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

 src/widget/container/group.rs | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/src/widget/container/group.rs b/src/widget/container/group.rs
index 2654c17..598ca24 100644
--- a/src/widget/container/group.rs
+++ b/src/widget/container/group.rs
@@ -99,6 +99,23 @@ impl Group {
         (fam, size.unwrap_or(14.0))
     }
 
+    /// The title tab's height — zero without a label.
+    pub fn tab_height(&self) -> f32 {
+        match self.label.as_deref().filter(|l| !l.is_empty()) {
+            Some(_) => self.title_font().1 + 8.0,
+            None => 0.0,
+        }
+    }
+
+    /// The vertical room this lasso takes ABOVE its members' hull: the padding
+    /// and the title tab. A lasso is laid out by its members, so a host that
+    /// places them in rows leaves this much between the row before and the
+    /// first member — the row a strategy would reserve for the title were the
+    /// group its child — or the tab lands on that row.
+    pub fn headroom(&self) -> f32 {
+        self.padding + self.tab_height()
+    }
+
     /// The members' hull: the union of the registered, visible, on-screen members' rects.
     fn hull(&self, ui: &UiContext) -> Option<Rect> {
         let mut hull: Option<(f32, f32, f32, f32)> = None;
@@ -137,7 +154,7 @@ impl Group {
         let mut radii = (r, r, r, r);
         let title = self.label.as_deref().filter(|l| !l.is_empty());
         let (fam, size) = self.title_font();
-        let tab_h = size + 8.0;
+        let tab_h = self.tab_height();
         if let (true, Some((plate, pr))) = (self.fit, self.plate) {
             // The frame's natural seat is one padding inside the plate's edge. A
             // side within `snap` of that seat — or past it — takes it, moving