gpui: Add scroll_to_item_with_offset
to UniformListScrollState
(#35064)
Previously we had `ScrollStrategy::ToPosition(usize)` which lets you define the offset where you want to scroll that item to. This is the same as `ScrollStrategy::Top` but imagine some space reserved at the top. This PR removes `ScrollStrategy::ToPosition` in favor of `scroll_to_item_with_offset` which is the method to do the same. The reason to add this method is that now not just `ScrollStrategy::Top` but `ScrollStrategy::Center` can also uses this offset to center the item in the remaining unreserved space. ```rs // Before scroll_handle.scroll_to_item(index, ScrollStrategy::ToPosition(offset)); // After scroll_handle.scroll_to_item_with_offset(index, ScrollStrategy::Top, offset); // New! Centers item skipping first x items scroll_handle.scroll_to_item_with_offset(index, ScrollStrategy::Center, offset); ``` This will be useful for follow up PR. Release Notes: - N/A
This commit is contained in:
parent
66acc2698a
commit
f78a112387
2 changed files with 45 additions and 24 deletions
|
@ -88,15 +88,24 @@ pub enum ScrollStrategy {
|
||||||
/// May not be possible if there's not enough list items above the item scrolled to:
|
/// May not be possible if there's not enough list items above the item scrolled to:
|
||||||
/// in this case, the element will be placed at the closest possible position.
|
/// in this case, the element will be placed at the closest possible position.
|
||||||
Center,
|
Center,
|
||||||
/// Scrolls the element to be at the given item index from the top of the viewport.
|
}
|
||||||
ToPosition(usize),
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub struct DeferredScrollToItem {
|
||||||
|
/// The item index to scroll to
|
||||||
|
pub item_index: usize,
|
||||||
|
/// The scroll strategy to use
|
||||||
|
pub strategy: ScrollStrategy,
|
||||||
|
/// The offset in number of items
|
||||||
|
pub offset: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct UniformListScrollState {
|
pub struct UniformListScrollState {
|
||||||
pub base_handle: ScrollHandle,
|
pub base_handle: ScrollHandle,
|
||||||
pub deferred_scroll_to_item: Option<(usize, ScrollStrategy)>,
|
pub deferred_scroll_to_item: Option<DeferredScrollToItem>,
|
||||||
/// Size of the item, captured during last layout.
|
/// Size of the item, captured during last layout.
|
||||||
pub last_item_size: Option<ItemSize>,
|
pub last_item_size: Option<ItemSize>,
|
||||||
/// Whether the list was vertically flipped during last layout.
|
/// Whether the list was vertically flipped during last layout.
|
||||||
|
@ -126,7 +135,24 @@ impl UniformListScrollHandle {
|
||||||
|
|
||||||
/// Scroll the list to the given item index.
|
/// Scroll the list to the given item index.
|
||||||
pub fn scroll_to_item(&self, ix: usize, strategy: ScrollStrategy) {
|
pub fn scroll_to_item(&self, ix: usize, strategy: ScrollStrategy) {
|
||||||
self.0.borrow_mut().deferred_scroll_to_item = Some((ix, strategy));
|
self.0.borrow_mut().deferred_scroll_to_item = Some(DeferredScrollToItem {
|
||||||
|
item_index: ix,
|
||||||
|
strategy,
|
||||||
|
offset: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scroll the list to the given item index with an offset.
|
||||||
|
///
|
||||||
|
/// For ScrollStrategy::Top, the item will be placed at the offset position from the top.
|
||||||
|
///
|
||||||
|
/// For ScrollStrategy::Center, the item will be centered between offset and the last visible item.
|
||||||
|
pub fn scroll_to_item_with_offset(&self, ix: usize, strategy: ScrollStrategy, offset: usize) {
|
||||||
|
self.0.borrow_mut().deferred_scroll_to_item = Some(DeferredScrollToItem {
|
||||||
|
item_index: ix,
|
||||||
|
strategy,
|
||||||
|
offset,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the list is flipped vertically.
|
/// Check if the list is flipped vertically.
|
||||||
|
@ -139,7 +165,8 @@ impl UniformListScrollHandle {
|
||||||
pub fn logical_scroll_top_index(&self) -> usize {
|
pub fn logical_scroll_top_index(&self) -> usize {
|
||||||
let this = self.0.borrow();
|
let this = self.0.borrow();
|
||||||
this.deferred_scroll_to_item
|
this.deferred_scroll_to_item
|
||||||
.map(|(ix, _)| ix)
|
.as_ref()
|
||||||
|
.map(|deferred| deferred.item_index)
|
||||||
.unwrap_or_else(|| this.base_handle.logical_scroll_top().0)
|
.unwrap_or_else(|| this.base_handle.logical_scroll_top().0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,7 +347,8 @@ impl Element for UniformList {
|
||||||
scroll_offset.x = Pixels::ZERO;
|
scroll_offset.x = Pixels::ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((mut ix, scroll_strategy)) = shared_scroll_to_item {
|
if let Some(deferred_scroll) = shared_scroll_to_item {
|
||||||
|
let mut ix = deferred_scroll.item_index;
|
||||||
if y_flipped {
|
if y_flipped {
|
||||||
ix = self.item_count.saturating_sub(ix + 1);
|
ix = self.item_count.saturating_sub(ix + 1);
|
||||||
}
|
}
|
||||||
|
@ -329,23 +357,28 @@ impl Element for UniformList {
|
||||||
let item_top = item_height * ix + padding.top;
|
let item_top = item_height * ix + padding.top;
|
||||||
let item_bottom = item_top + item_height;
|
let item_bottom = item_top + item_height;
|
||||||
let scroll_top = -updated_scroll_offset.y;
|
let scroll_top = -updated_scroll_offset.y;
|
||||||
|
let offset_pixels = item_height * deferred_scroll.offset;
|
||||||
let mut scrolled_to_top = false;
|
let mut scrolled_to_top = false;
|
||||||
if item_top < scroll_top + padding.top {
|
|
||||||
|
if item_top < scroll_top + padding.top + offset_pixels {
|
||||||
scrolled_to_top = true;
|
scrolled_to_top = true;
|
||||||
updated_scroll_offset.y = -(item_top) + padding.top;
|
updated_scroll_offset.y = -(item_top) + padding.top + offset_pixels;
|
||||||
} else if item_bottom > scroll_top + list_height - padding.bottom {
|
} else if item_bottom > scroll_top + list_height - padding.bottom {
|
||||||
scrolled_to_top = true;
|
scrolled_to_top = true;
|
||||||
updated_scroll_offset.y = -(item_bottom - list_height) - padding.bottom;
|
updated_scroll_offset.y = -(item_bottom - list_height) - padding.bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
match scroll_strategy {
|
match deferred_scroll.strategy {
|
||||||
ScrollStrategy::Top => {}
|
ScrollStrategy::Top => {}
|
||||||
ScrollStrategy::Center => {
|
ScrollStrategy::Center => {
|
||||||
if scrolled_to_top {
|
if scrolled_to_top {
|
||||||
let item_center = item_top + item_height / 2.0;
|
let item_center = item_top + item_height / 2.0;
|
||||||
let target_scroll_top = item_center - list_height / 2.0;
|
|
||||||
|
|
||||||
if item_top < scroll_top
|
let viewport_height = list_height - offset_pixels;
|
||||||
|
let viewport_center = offset_pixels + viewport_height / 2.0;
|
||||||
|
let target_scroll_top = item_center - viewport_center;
|
||||||
|
|
||||||
|
if item_top < scroll_top + offset_pixels
|
||||||
|| item_bottom > scroll_top + list_height
|
|| item_bottom > scroll_top + list_height
|
||||||
{
|
{
|
||||||
updated_scroll_offset.y = -target_scroll_top
|
updated_scroll_offset.y = -target_scroll_top
|
||||||
|
@ -355,15 +388,6 @@ impl Element for UniformList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ScrollStrategy::ToPosition(sticky_index) => {
|
|
||||||
let target_y_in_viewport = item_height * sticky_index;
|
|
||||||
let target_scroll_top = item_top - target_y_in_viewport;
|
|
||||||
let max_scroll_top =
|
|
||||||
(content_height - list_height).max(Pixels::ZERO);
|
|
||||||
let new_scroll_top =
|
|
||||||
target_scroll_top.clamp(Pixels::ZERO, max_scroll_top);
|
|
||||||
updated_scroll_offset.y = -new_scroll_top;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
scroll_offset = *updated_scroll_offset
|
scroll_offset = *updated_scroll_offset
|
||||||
}
|
}
|
||||||
|
|
|
@ -4207,10 +4207,7 @@ impl ProjectPanel {
|
||||||
this.marked_entries.clear();
|
this.marked_entries.clear();
|
||||||
if is_sticky {
|
if is_sticky {
|
||||||
if let Some((_, _, index)) = this.index_for_entry(entry_id, worktree_id) {
|
if let Some((_, _, index)) = this.index_for_entry(entry_id, worktree_id) {
|
||||||
let strategy = sticky_index
|
this.scroll_handle.scroll_to_item_with_offset(index, ScrollStrategy::Top, sticky_index.unwrap_or(0));
|
||||||
.map(ScrollStrategy::ToPosition)
|
|
||||||
.unwrap_or(ScrollStrategy::Top);
|
|
||||||
this.scroll_handle.scroll_to_item(index, strategy);
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
// move down by 1px so that clicked item
|
// move down by 1px so that clicked item
|
||||||
// don't count as sticky anymore
|
// don't count as sticky anymore
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue