diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index 58ce51e95bc707dc7eb7d335bd1dafaf8cb0eb40..e3ca5c7cd95ce55f7b523399d6cecbe7be3ad3a2 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -3034,7 +3034,20 @@ struct ScrollHandleState { child_bounds: Vec>, scroll_to_bottom: bool, overflow: Point, - active_item: Option, + active_item: Option, +} + +#[derive(Default, Debug, Clone, Copy)] +struct ScrollActiveItem { + index: usize, + strategy: ScrollStrategy, +} + +#[derive(Default, Debug, Clone, Copy)] +enum ScrollStrategy { + #[default] + FirstVisible, + Top, } /// A handle to the scrollable aspects of an element. @@ -3097,26 +3110,48 @@ impl ScrollHandle { /// Update [ScrollHandleState]'s active item for scrolling to in prepaint pub fn scroll_to_item(&self, ix: usize) { let mut state = self.0.borrow_mut(); - state.active_item = Some(ix); + state.active_item = Some(ScrollActiveItem { + index: ix, + strategy: ScrollStrategy::default(), + }); + } + + /// Update [ScrollHandleState]'s active item for scrolling to in prepaint + /// This scrolls the minimal amount to ensure that the child is the first visible element + pub fn scroll_to_top_of_item(&self, ix: usize) { + let mut state = self.0.borrow_mut(); + state.active_item = Some(ScrollActiveItem { + index: ix, + strategy: ScrollStrategy::Top, + }); } - /// Scrolls the minimal amount to ensure that the child is - /// fully visible + /// Scrolls the minimal amount to either ensure that the child is + /// fully visible or the top element of the view depends on the + /// scroll strategy fn scroll_to_active_item(&self) { let mut state = self.0.borrow_mut(); - let Some(active_item_index) = state.active_item else { + let Some(active_item) = state.active_item else { return; }; - let active_item = match state.child_bounds.get(active_item_index) { + + let active_item = match state.child_bounds.get(active_item.index) { Some(bounds) => { let mut scroll_offset = state.offset.borrow_mut(); - if state.overflow.y == Overflow::Scroll { - if bounds.top() + scroll_offset.y < state.bounds.top() { + match active_item.strategy { + ScrollStrategy::FirstVisible => { + if state.overflow.y == Overflow::Scroll { + if bounds.top() + scroll_offset.y < state.bounds.top() { + scroll_offset.y = state.bounds.top() - bounds.top(); + } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() { + scroll_offset.y = state.bounds.bottom() - bounds.bottom(); + } + } + } + ScrollStrategy::Top => { scroll_offset.y = state.bounds.top() - bounds.top(); - } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() { - scroll_offset.y = state.bounds.bottom() - bounds.bottom(); } } @@ -3129,7 +3164,7 @@ impl ScrollHandle { } None } - None => Some(active_item_index), + None => Some(active_item), }; state.active_item = active_item; } diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index 8bd03cd4be9238f61a80af9c42fad0f236923137..dd08991fec8be96b3d695628ccfa759f039cab45 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -1095,7 +1095,7 @@ impl SettingsWindow { .map(|pair| pair.0) { this.scroll_handle - .scroll_to_item(section_index); + .scroll_to_top_of_item(section_index); } }