Forms: lists reveal their cursor once laid out

- Select, Combobox and MultiSelect opened a long list at its top, the
  choice out of view: gpui skips a scroll asked before the list's first
  frame. Picker and Suggestions share forms::options::reveal, which Popup
  runs from a canvas inside the list
- test: a_long_list_opens_with_its_choice_in_view
- AGENTS: the scroll rule names the reveal
This commit is contained in:
ZacharyZhang-NY committed 2026-09-26 07:25:13 -04:00
1 parent 7f4bd8415c
commit 247f836073
7 files changed
+129 -28

No files matched your search

+27 -3
View File
@@ -1,9 +1,10 @@
use std::rc::Rc;
use gpui::{
Animation, AnimationExt, AnyElement, App, Bounds, Corner, Div, ElementId, InteractiveElement,
IntoElement, MouseButton, ParentElement, Pixels, ScrollHandle, SharedString, Stateful,
StatefulInteractiveElement, Styled, Window, anchored, canvas, deferred, div, prelude::*,
Animation, AnimationExt, AnyElement, App, Bounds, Corner, Div, ElementId, Entity,
InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels, ScrollHandle,
SharedString, Stateful, StatefulInteractiveElement, Styled, Window, anchored, canvas, deferred,
div, prelude::*,
};
use crate::{
@@ -211,6 +212,29 @@ pub(crate) fn float_height(
.into_any_element()
}
/// The row a list scrolls into view: `at` while `shown` and not yet revealed there. A hidden list forgets.
pub(crate) fn reveal<T: 'static>(
state: &Entity<T>,
revealed: fn(&mut T) -> &mut Option<usize>,
shown: bool,
at: usize,
cx: &mut App,
) -> Option<(usize, Run)> {
let last = state.update(cx, |state, _| {
let last = revealed(state);
if !shown {
*last = None;
}
*last
});
(shown && last != Some(at)).then(|| {
let state = state.clone();
let mark: Run =
Rc::new(move |_, cx| state.update(cx, |state, _| *revealed(state) = Some(at)));
(at, mark)
})
}
/// A list floating under `anchor`, at least as wide. Rows leave focus where it was.
pub(crate) struct Popup<'a> {
pub id: ElementId,