mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-29 12:38:02 +00:00
Implement list scroll tracking
Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
parent
1d34b7b9fe
commit
742180a3a8
3 changed files with 75 additions and 29 deletions
|
@ -1,11 +1,12 @@
|
|||
use crate::{
|
||||
point, px, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId,
|
||||
ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Size, StatefulInteractive,
|
||||
StatefulInteractivity, StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled,
|
||||
ViewContext,
|
||||
ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size,
|
||||
StatefulInteractive, StatefulInteractivity, StatelessInteractive, StatelessInteractivity,
|
||||
StyleRefinement, Styled, ViewContext,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use smallvec::SmallVec;
|
||||
use std::{cmp, ops::Range};
|
||||
use std::{cmp, ops::Range, sync::Arc};
|
||||
use taffy::style::Overflow;
|
||||
|
||||
pub fn list<Id, V, C>(
|
||||
|
@ -30,6 +31,7 @@ where
|
|||
.collect()
|
||||
}),
|
||||
interactivity: id.into(),
|
||||
scroll_handle: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +47,37 @@ pub struct List<V: 'static> {
|
|||
) -> SmallVec<[AnyElement<V>; 64]>,
|
||||
>,
|
||||
interactivity: StatefulInteractivity<V>,
|
||||
scroll_handle: Option<ListScrollHandle>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ListScrollHandle(Arc<Mutex<Option<ListScrollHandleState>>>);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct ListScrollHandleState {
|
||||
item_height: Pixels,
|
||||
list_height: Pixels,
|
||||
scroll_offset: Arc<Mutex<Point<Pixels>>>,
|
||||
}
|
||||
|
||||
impl ListScrollHandle {
|
||||
pub fn new() -> Self {
|
||||
Self(Arc::new(Mutex::new(None)))
|
||||
}
|
||||
|
||||
pub fn scroll_to_item(&self, ix: usize) {
|
||||
if let Some(state) = &*self.0.lock() {
|
||||
let mut scroll_offset = state.scroll_offset.lock();
|
||||
let item_top = state.item_height * ix;
|
||||
let item_bottom = item_top + state.item_height;
|
||||
let scroll_top = -scroll_offset.y;
|
||||
if item_top < scroll_top {
|
||||
scroll_offset.y = -item_top;
|
||||
} else if item_bottom > scroll_top + state.list_height {
|
||||
scroll_offset.y = -(item_bottom - state.list_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -107,6 +140,13 @@ impl<V: 'static> Element<V> for List<V> {
|
|||
let content_size;
|
||||
if self.item_count > 0 {
|
||||
let item_height = self.measure_item_height(view_state, padded_bounds, cx);
|
||||
if let Some(scroll_handle) = self.scroll_handle.clone() {
|
||||
scroll_handle.0.lock().replace(ListScrollHandleState {
|
||||
item_height,
|
||||
list_height: padded_bounds.size.height,
|
||||
scroll_offset: element_state.interactive.track_scroll_offset(),
|
||||
});
|
||||
}
|
||||
let visible_item_count =
|
||||
(padded_bounds.size.height / item_height).ceil() as usize + 1;
|
||||
let scroll_offset = element_state
|
||||
|
@ -187,6 +227,11 @@ impl<V> List<V> {
|
|||
);
|
||||
cx.layout_bounds(layout_id).size.height
|
||||
}
|
||||
|
||||
pub fn track_scroll(mut self, handle: ListScrollHandle) -> Self {
|
||||
self.scroll_handle = Some(handle);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> StatelessInteractive<V> for List<V> {
|
||||
|
|
|
@ -900,6 +900,12 @@ impl InteractiveElementState {
|
|||
.as_ref()
|
||||
.map(|offset| offset.lock().clone())
|
||||
}
|
||||
|
||||
pub fn track_scroll_offset(&mut self) -> Arc<Mutex<Point<Pixels>>> {
|
||||
self.scroll_offset
|
||||
.get_or_insert_with(|| Arc::new(Mutex::new(Default::default())))
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Default for StatelessInteractivity<V> {
|
||||
|
|
|
@ -1,28 +1,8 @@
|
|||
// use editor::Editor;
|
||||
// use gpui::{
|
||||
// elements::*,
|
||||
// geometry::vector::{vec2f, Vector2F},
|
||||
// keymap_matcher::KeymapContext,
|
||||
// platform::{CursorStyle, MouseButton},
|
||||
// AnyElement, AnyViewHandle, AppContext, Axis, Entity, MouseState, Task, View, ViewContext,
|
||||
// ViewHandle,
|
||||
// };
|
||||
// use menu::{Cancel, Confirm, SecondaryConfirm, SelectFirst, SelectLast, SelectNext, SelectPrev};
|
||||
// use parking_lot::Mutex;
|
||||
// use std::{cmp, sync::Arc};
|
||||
// use util::ResultExt;
|
||||
// use workspace::Modal;
|
||||
|
||||
// #[derive(Clone, Copy)]
|
||||
// pub enum PickerEvent {
|
||||
// Dismiss,
|
||||
// }
|
||||
|
||||
use std::cmp;
|
||||
|
||||
use gpui::{
|
||||
div, list, Component, ElementId, FocusHandle, Focusable, ParentElement, StatelessInteractive,
|
||||
Styled, ViewContext,
|
||||
div, list, Component, ElementId, FocusHandle, Focusable, ListScrollHandle, ParentElement,
|
||||
StatelessInteractive, Styled, ViewContext,
|
||||
};
|
||||
|
||||
// pub struct Picker<D> {
|
||||
|
@ -99,6 +79,7 @@ impl<V: PickerDelegate> Picker<V> {
|
|||
impl<V: 'static + PickerDelegate> Picker<V> {
|
||||
pub fn render(self, view: &mut V, _cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
let id = self.id.clone();
|
||||
let scroll_handle = ListScrollHandle::new();
|
||||
div()
|
||||
.size_full()
|
||||
.id(self.id.clone())
|
||||
|
@ -112,36 +93,49 @@ impl<V: 'static + PickerDelegate> Picker<V> {
|
|||
})
|
||||
.on_action({
|
||||
let id = id.clone();
|
||||
let scroll_handle = scroll_handle.clone();
|
||||
move |view: &mut V, _: &menu::SelectNext, cx| {
|
||||
let count = view.match_count(id.clone());
|
||||
if count > 0 {
|
||||
let index = view.selected_index(id.clone());
|
||||
view.set_selected_index(cmp::min(index + 1, count - 1), id.clone(), cx);
|
||||
let ix = cmp::min(index + 1, count - 1);
|
||||
view.set_selected_index(ix, id.clone(), cx);
|
||||
scroll_handle.scroll_to_item(ix);
|
||||
}
|
||||
}
|
||||
})
|
||||
.on_action({
|
||||
let id = id.clone();
|
||||
let scroll_handle = scroll_handle.clone();
|
||||
move |view, _: &menu::SelectPrev, cx| {
|
||||
let count = view.match_count(id.clone());
|
||||
if count > 0 {
|
||||
let index = view.selected_index(id.clone());
|
||||
view.set_selected_index(index.saturating_sub(1), id.clone(), cx);
|
||||
let ix = index.saturating_sub(1);
|
||||
view.set_selected_index(ix, id.clone(), cx);
|
||||
scroll_handle.scroll_to_item(ix);
|
||||
}
|
||||
}
|
||||
})
|
||||
.on_action({
|
||||
let id = id.clone();
|
||||
let scroll_handle = scroll_handle.clone();
|
||||
move |view: &mut V, _: &menu::SelectFirst, cx| {
|
||||
view.set_selected_index(0, id.clone(), cx);
|
||||
let count = view.match_count(id.clone());
|
||||
if count > 0 {
|
||||
view.set_selected_index(0, id.clone(), cx);
|
||||
scroll_handle.scroll_to_item(0);
|
||||
}
|
||||
}
|
||||
})
|
||||
.on_action({
|
||||
let id = id.clone();
|
||||
let scroll_handle = scroll_handle.clone();
|
||||
move |view: &mut V, _: &menu::SelectLast, cx| {
|
||||
let count = view.match_count(id.clone());
|
||||
if count > 0 {
|
||||
view.set_selected_index(count - 1, id.clone(), cx);
|
||||
scroll_handle.scroll_to_item(count - 1);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -174,6 +168,7 @@ impl<V: 'static + PickerDelegate> Picker<V> {
|
|||
.collect()
|
||||
},
|
||||
)
|
||||
.track_scroll(scroll_handle.clone())
|
||||
.size_full(),
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue