Collab panel2: Now with scrolling and keyboard (#3455)
Also introducing: .track_scroll() for non-uniform lists. Release Notes: - N/A
This commit is contained in:
commit
02174084ca
3 changed files with 541 additions and 402 deletions
|
@ -12,6 +12,7 @@ use smallvec::SmallVec;
|
|||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::RefCell,
|
||||
cmp::Ordering,
|
||||
fmt::Debug,
|
||||
mem,
|
||||
rc::Rc,
|
||||
|
@ -357,6 +358,11 @@ pub trait StatefulInteractiveElement: InteractiveElement {
|
|||
self
|
||||
}
|
||||
|
||||
fn track_scroll(mut self, scroll_handle: &ScrollHandle) -> Self {
|
||||
self.interactivity().scroll_handle = Some(scroll_handle.clone());
|
||||
self
|
||||
}
|
||||
|
||||
fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
|
@ -626,6 +632,26 @@ impl Element for Div {
|
|||
let mut child_max = Point::default();
|
||||
let content_size = if element_state.child_layout_ids.is_empty() {
|
||||
bounds.size
|
||||
} else if let Some(scroll_handle) = self.interactivity.scroll_handle.as_ref() {
|
||||
let mut state = scroll_handle.0.borrow_mut();
|
||||
state.child_bounds = Vec::with_capacity(element_state.child_layout_ids.len());
|
||||
state.bounds = bounds;
|
||||
let requested = state.requested_scroll_top.take();
|
||||
|
||||
for (ix, child_layout_id) in element_state.child_layout_ids.iter().enumerate() {
|
||||
let child_bounds = cx.layout_bounds(*child_layout_id);
|
||||
child_min = child_min.min(&child_bounds.origin);
|
||||
child_max = child_max.max(&child_bounds.lower_right());
|
||||
state.child_bounds.push(child_bounds);
|
||||
|
||||
if let Some(requested) = requested.as_ref() {
|
||||
if requested.0 == ix {
|
||||
*state.offset.borrow_mut() =
|
||||
bounds.origin - (child_bounds.origin - point(px(0.), requested.1));
|
||||
}
|
||||
}
|
||||
}
|
||||
(child_max - child_min).into()
|
||||
} else {
|
||||
for child_layout_id in &element_state.child_layout_ids {
|
||||
let child_bounds = cx.layout_bounds(*child_layout_id);
|
||||
|
@ -696,6 +722,7 @@ pub struct Interactivity {
|
|||
pub key_context: KeyContext,
|
||||
pub focusable: bool,
|
||||
pub tracked_focus_handle: Option<FocusHandle>,
|
||||
pub scroll_handle: Option<ScrollHandle>,
|
||||
pub focus_listeners: FocusListeners,
|
||||
pub group: Option<SharedString>,
|
||||
pub base_style: StyleRefinement,
|
||||
|
@ -754,6 +781,10 @@ impl Interactivity {
|
|||
});
|
||||
}
|
||||
|
||||
if let Some(scroll_handle) = self.scroll_handle.as_ref() {
|
||||
element_state.scroll_offset = Some(scroll_handle.0.borrow().offset.clone());
|
||||
}
|
||||
|
||||
let style = self.compute_style(None, &mut element_state, cx);
|
||||
let layout_id = f(style, cx);
|
||||
(layout_id, element_state)
|
||||
|
@ -1206,6 +1237,7 @@ impl Default for Interactivity {
|
|||
key_context: KeyContext::default(),
|
||||
focusable: false,
|
||||
tracked_focus_handle: None,
|
||||
scroll_handle: None,
|
||||
focus_listeners: SmallVec::default(),
|
||||
// scroll_offset: Point::default(),
|
||||
group: None,
|
||||
|
@ -1429,3 +1461,83 @@ where
|
|||
self.element.children_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ScrollHandleState {
|
||||
// not great to have the nested rc's...
|
||||
offset: Rc<RefCell<Point<Pixels>>>,
|
||||
bounds: Bounds<Pixels>,
|
||||
child_bounds: Vec<Bounds<Pixels>>,
|
||||
requested_scroll_top: Option<(usize, Pixels)>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ScrollHandle(Rc<RefCell<ScrollHandleState>>);
|
||||
|
||||
impl ScrollHandle {
|
||||
pub fn new() -> Self {
|
||||
Self(Rc::default())
|
||||
}
|
||||
|
||||
pub fn offset(&self) -> Point<Pixels> {
|
||||
self.0.borrow().offset.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn top_item(&self) -> usize {
|
||||
let state = self.0.borrow();
|
||||
let top = state.bounds.top() - state.offset.borrow().y;
|
||||
|
||||
match state.child_bounds.binary_search_by(|bounds| {
|
||||
if top < bounds.top() {
|
||||
Ordering::Greater
|
||||
} else if top > bounds.bottom() {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}) {
|
||||
Ok(ix) => ix,
|
||||
Err(ix) => ix.min(state.child_bounds.len().saturating_sub(1)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
|
||||
self.0.borrow().child_bounds.get(ix).cloned()
|
||||
}
|
||||
|
||||
/// scroll_to_item scrolls the minimal amount to ensure that the item is
|
||||
/// fully visible
|
||||
pub fn scroll_to_item(&self, ix: usize) {
|
||||
let state = self.0.borrow();
|
||||
|
||||
let Some(bounds) = state.child_bounds.get(ix) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let scroll_offset = state.offset.borrow().y;
|
||||
|
||||
if bounds.top() + scroll_offset < state.bounds.top() {
|
||||
state.offset.borrow_mut().y = state.bounds.top() - bounds.top();
|
||||
} else if bounds.bottom() + scroll_offset > state.bounds.bottom() {
|
||||
state.offset.borrow_mut().y = state.bounds.bottom() - bounds.bottom();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn logical_scroll_top(&self) -> (usize, Pixels) {
|
||||
let ix = self.top_item();
|
||||
let state = self.0.borrow();
|
||||
|
||||
if let Some(child_bounds) = state.child_bounds.get(ix) {
|
||||
(
|
||||
ix,
|
||||
child_bounds.top() + state.offset.borrow().y - state.bounds.top(),
|
||||
)
|
||||
} else {
|
||||
(ix, px(0.))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_logical_scroll_top(&self, ix: usize, px: Pixels) {
|
||||
self.0.borrow_mut().requested_scroll_top = Some((ix, px));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue