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

commit21037f0fda2c04ce55ca9a988400888ee28bf022
parent04c0484ff8
authorLucas Galante <[email protected]>
date2026-07-22 11:35
fix: collapsed sections keep their label and match the expanded tab inset

Two collapse bugs: the title's centering searched for the next visible
control ACROSS section boundaries, so a collapsed section borrowed the
next section's first row and flung its label far off-position
(vanishing); and the collapsed title box anchored 8px above where the
expanded tab sits. The label search now stops at the next section
param, and section_title_box seats a collapsed box exactly where the
expanded tab would be — carve, outline, label centering, and the
click-toggle hit zone all follow it.

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

 src/widget/container/parameters_bg.rs | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/src/widget/container/parameters_bg.rs b/src/widget/container/parameters_bg.rs
index 85dd007..11b250e 100644
--- a/src/widget/container/parameters_bg.rs
+++ b/src/widget/container/parameters_bg.rs
@@ -208,7 +208,15 @@ impl ParametersBg {
         let text_w =
             crate::widget::display::measure_text_width(&self.display_params[hdr].0, &label_family, label_size);
         let title_w = (text_w + 16.0).max(SECTION_TITLE_MIN_W).min(full_w);
-        (self.rect.x + 4.0, r_hdr.1 - TITLE_BOX_INSET, title_w, TITLE_BOX_H)
+        // Collapsed, the box sits where the EXPANDED tab sits (one title-box
+        // height above where the body's top edge would be), so collapsing
+        // doesn't jump the tab — and the click-toggle hit zone follows it.
+        let y = if self.collapsed.contains(&self.display_params[hdr].0) {
+            r_hdr.1 + r_hdr.3 + ROW_GAP - CONTENT_BOX_PAD - TITLE_BOX_H
+        } else {
+            r_hdr.1 - TITLE_BOX_INSET
+        };
+        (self.rect.x + 4.0, y, title_w, TITLE_BOX_H)
     }
 
     /// Each section as `(header index, its content-row range)` — the rows between a header
@@ -579,19 +587,27 @@ impl ParametersBg {
                 // atop the body's top edge); empty/collapsed sections keep the
                 // legacy offset.
                 let font_size = 13.0;
+                // The first control of THIS section only: a collapsed/empty
+                // section must not borrow the next section's rows.
                 let below = self
                     .display_params
                     .iter()
                     .enumerate()
                     .skip(i + 1)
-                    .find(|(j, q)| !hidden[*j] && q.2 != "section")
+                    .take_while(|(_, q)| q.2 != "section")
+                    .find(|(j, _)| !hidden[*j])
                     .map(|(j, _)| rects[j].1);
                 let y = match below {
                     Some(control_top) => {
                         let tab_top = control_top - CONTENT_BOX_PAD - TITLE_BOX_H;
                         tab_top + (TITLE_BOX_H - font_size) / 2.0
                     }
-                    _ => r.1 + 2.0,
+                    // Collapsed/empty: centered in the collapsed tab, which
+                    // sits where the expanded tab would (section_title_box).
+                    _ => {
+                        let tab_top = r.1 + r.3 + ROW_GAP - CONTENT_BOX_PAD - TITLE_BOX_H;
+                        tab_top + (TITLE_BOX_H - font_size) / 2.0
+                    }
                 };
                 labels.push(TextLabel {
                     text: name.clone(),