Implement scrolling and painting for List

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2021-08-23 16:41:48 +02:00
parent bd89dc4114
commit 3543aceff3

View file

@ -1,7 +1,11 @@
use crate::{
geometry::{rect::RectF, vector::Vector2F},
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
json::json,
sum_tree::{self, Bias, SumTree},
Element,
DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
};
use parking_lot::Mutex;
use std::{ops::Range, sync::Arc};
@ -19,6 +23,7 @@ struct StateInner {
last_layout_width: f32,
elements: Vec<ElementBox>,
heights: SumTree<ElementHeight>,
scroll_top: f32,
}
#[derive(Clone, Debug)]
@ -47,6 +52,25 @@ impl List {
pub fn new(state: ListState) -> Self {
Self { state }
}
fn scroll(
&self,
_: Vector2F,
delta: Vector2F,
precise: bool,
scroll_max: f32,
cx: &mut EventContext,
) -> bool {
if !precise {
todo!("still need to handle non-precise scroll events from a mouse wheel");
}
let mut state = self.state.0.lock();
state.scroll_top = (state.scroll_top - delta.y()).max(0.0).min(scroll_max);
cx.notify();
true
}
}
impl Element for List {
@ -56,8 +80,8 @@ impl Element for List {
fn layout(
&mut self,
constraint: crate::SizeConstraint,
cx: &mut crate::LayoutContext,
constraint: SizeConstraint,
cx: &mut LayoutContext,
) -> (Vector2F, Self::LayoutState) {
let state = &mut *self.state.0.lock();
let mut item_constraint = constraint;
@ -100,34 +124,69 @@ impl Element for List {
(constraint.max, ())
}
fn paint(
&mut self,
bounds: RectF,
layout: &mut Self::LayoutState,
cx: &mut crate::PaintContext,
) -> Self::PaintState {
todo!()
fn paint(&mut self, bounds: RectF, _: &mut (), cx: &mut PaintContext) {
let state = &mut *self.state.0.lock();
let visible_range = state.visible_range(bounds.height());
let mut item_top = {
let mut cursor = state.heights.cursor::<Count, Height>();
cursor.seek(&Count(visible_range.start), Bias::Right, &());
cursor.sum_start().0
};
for element in &mut state.elements[visible_range] {
let origin = bounds.origin() + vec2f(0., item_top) - state.scroll_top;
element.paint(origin, cx);
item_top += element.size().y();
}
}
fn dispatch_event(
&mut self,
event: &crate::Event,
event: &Event,
bounds: RectF,
layout: &mut Self::LayoutState,
paint: &mut Self::PaintState,
cx: &mut crate::EventContext,
_: &mut (),
_: &mut (),
cx: &mut EventContext,
) -> bool {
todo!()
let mut handled = false;
let mut state = self.state.0.lock();
let visible_range = state.visible_range(bounds.height());
for item in &mut state.elements[visible_range] {
handled = item.dispatch_event(event, cx) || handled;
}
match event {
Event::ScrollWheel {
position,
delta,
precise,
} => {
if bounds.contains_point(*position) {
let scroll_max = state.scroll_max(bounds.height());
if self.scroll(*position, *delta, *precise, scroll_max, cx) {
handled = true;
}
}
}
_ => {}
}
handled
}
fn debug(
&self,
bounds: RectF,
layout: &Self::LayoutState,
paint: &Self::PaintState,
cx: &crate::DebugContext,
) -> serde_json::Value {
todo!()
fn debug(&self, bounds: RectF, _: &(), _: &(), cx: &DebugContext) -> serde_json::Value {
let state = self.state.0.lock();
let visible_range = state.visible_range(bounds.height());
let visible_elements = state.elements[visible_range.clone()]
.iter()
.map(|e| e.debug(cx))
.collect::<Vec<_>>();
json!({
"visible_range": visible_range,
"visible_elements": visible_elements,
"scroll_top": state.scroll_top,
})
}
}
@ -139,6 +198,7 @@ impl ListState {
last_layout_width: 0.,
elements,
heights,
scroll_top: 0.,
})))
}
@ -170,6 +230,21 @@ impl ListState {
}
}
impl StateInner {
fn visible_range(&self, height: f32) -> Range<usize> {
let mut cursor = self.heights.cursor::<Height, Count>();
cursor.seek(&Height(self.scroll_top), Bias::Right, &());
let start_ix = cursor.sum_start().0;
cursor.seek(&Height(self.scroll_top + height), Bias::Left, &());
let end_ix = cursor.sum_start().0;
start_ix..end_ix + 1
}
fn scroll_max(&self, height: f32) -> f32 {
self.heights.summary().height - height
}
}
impl ElementHeight {
fn is_pending(&self) -> bool {
matches!(self, ElementHeight::Pending)
@ -241,6 +316,12 @@ impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for Height {
}
}
impl<'a> sum_tree::SeekDimension<'a, ElementHeightSummary> for Height {
fn cmp(&self, other: &Self, _: &()) -> std::cmp::Ordering {
self.0.partial_cmp(&other.0).unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;