From 7b9afc8c454607222eaf751bbc38159ececc1f7a Mon Sep 17 00:00:00 2001 From: Finn Eitreim <48069764+feitreim@users.noreply.github.com> Date: Thu, 12 Mar 2026 20:12:31 -0400 Subject: [PATCH] gpui: Recalculate list layout after the window has been resized (#51414) Closes #51417 I noticed this bug in the settings menu where when I opened the settings menu, I could not scroll down through all the available options, eg. on the initial page I wasn't able to scroll down to privacy. When I saw that no one else had reported this issue, I figured it may be due to my setup, and it turns out that using Aerospace, the window manager I use, was what made this bug visible to me. Because aerospace resizes the window right after it launches, the originally computed heights for the list are incorrect, meaning the scroll bar is the wrong size as well. in the relevant code there was a comment that says "If the width of the list has changed, invalidate all cached item heights" which wasn't incorrect per-se, but it just invalidated them without triggering any re-computation, causing incorrect scroll bars. My intuition is that window resizes/events that change the width of the list bounds are fairly rare, so there shouldn't be a large performance hit from the change. Also implemented a test that directly showcases the behavior, if you run the test without the change it fails, as the max_offset_for_scrollbar will be wrong. Videos: Before https://github.com/user-attachments/assets/2b680222-7071-4098-863f-519361f0756a After: https://github.com/user-attachments/assets/1222a299-23d7-4007-8e88-55d2daccce64 [x] Tests [x] Video of behavior Release Notes: - gpui: fixed list height re-computation when the list width changes. --- crates/gpui/src/elements/list.rs | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index 92b5389fecf219c0c113f682463498902df4c07d..b84241e9e0f79fe5cf8a24514cbf57982247a76b 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -1103,6 +1103,7 @@ impl Element for List { ); state.items = new_items; + state.measuring_behavior.reset(); } let padding = style @@ -1348,6 +1349,41 @@ mod test { assert_eq!(offset.offset_in_item, px(0.)); } + #[gpui::test] + fn test_measure_all_after_width_change(cx: &mut TestAppContext) { + let cx = cx.add_empty_window(); + + let state = ListState::new(10, crate::ListAlignment::Top, px(0.)).measure_all(); + + struct TestView(ListState); + impl Render for TestView { + fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { + list(self.0.clone(), |_, _, _| { + div().h(px(50.)).w_full().into_any() + }) + .w_full() + .h_full() + } + } + + let view = cx.update(|_, cx| cx.new(|_| TestView(state.clone()))); + + // First draw at width 100: all 10 items measured (total 500px). + // Viewport is 200px, so max scroll offset should be 300px. + cx.draw(point(px(0.), px(0.)), size(px(100.), px(200.)), |_, _| { + view.clone().into_any_element() + }); + assert_eq!(state.max_offset_for_scrollbar().y, px(300.)); + + // Second draw at a different width: items get invalidated. + // Without the fix, max_offset would drop because unmeasured items + // contribute 0 height. + cx.draw(point(px(0.), px(0.)), size(px(200.), px(200.)), |_, _| { + view.into_any_element() + }); + assert_eq!(state.max_offset_for_scrollbar().y, px(300.)); + } + #[gpui::test] fn test_remeasure(cx: &mut TestAppContext) { let cx = cx.add_empty_window();