Implement and test splice for ListState

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2021-08-23 16:00:05 +02:00
parent 2c3ba00d3e
commit 03b7c3c8c6

View file

@ -4,7 +4,7 @@ use crate::{
Element, Element,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use std::sync::Arc; use std::{ops::Range, sync::Arc};
use crate::ElementBox; use crate::ElementBox;
@ -12,6 +12,7 @@ pub struct List {
state: ListState, state: ListState,
} }
#[derive(Clone)]
pub struct ListState(Arc<Mutex<StateInner>>); pub struct ListState(Arc<Mutex<StateInner>>);
struct StateInner { struct StateInner {
@ -26,7 +27,7 @@ enum ElementHeight {
Ready(f32), Ready(f32),
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, PartialEq)]
struct ElementHeightSummary { struct ElementHeightSummary {
count: usize, count: usize,
pending_count: usize, pending_count: usize,
@ -42,6 +43,12 @@ struct PendingCount(usize);
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
struct Height(f32); struct Height(f32);
impl List {
pub fn new(state: ListState) -> Self {
Self { state }
}
}
impl Element for List { impl Element for List {
type LayoutState = (); type LayoutState = ();
@ -82,8 +89,7 @@ impl Element for List {
drop(old_heights); drop(old_heights);
state.heights = new_heights; state.heights = new_heights;
(constraint.max, ())
todo!()
} }
fn paint( fn paint(
@ -127,6 +133,33 @@ impl ListState {
heights, heights,
}))) })))
} }
pub fn splice(
&self,
old_range: Range<usize>,
new_elements: impl IntoIterator<Item = ElementBox>,
) {
let state = &mut *self.0.lock();
let mut old_heights = state.heights.cursor::<Count, ()>();
let mut new_heights = old_heights.slice(&Count(old_range.start), Bias::Right, &());
old_heights.seek_forward(&Count(old_range.end), Bias::Right, &());
let mut len = 0;
let old_elements = state.elements.splice(
old_range,
new_elements.into_iter().map(|e| {
len += 1;
e
}),
);
drop(old_elements);
new_heights.extend((0..len).map(|_| ElementHeight::Pending), &());
new_heights.push_tree(old_heights.suffix(&()), &());
drop(old_heights);
state.heights = new_heights;
}
} }
impl ElementHeight { impl ElementHeight {
@ -158,6 +191,7 @@ impl sum_tree::Summary for ElementHeightSummary {
type Context = (); type Context = ();
fn add_summary(&mut self, summary: &Self, _: &()) { fn add_summary(&mut self, summary: &Self, _: &()) {
self.count += summary.count;
self.pending_count += summary.pending_count; self.pending_count += summary.pending_count;
self.height += summary.height; self.height += summary.height;
} }
@ -175,6 +209,12 @@ impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for Count {
} }
} }
impl<'a> sum_tree::SeekDimension<'a, ElementHeightSummary> for Count {
fn cmp(&self, other: &Self, _: &()) -> std::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for PendingCount { impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for PendingCount {
fn add_summary(&mut self, summary: &'a ElementHeightSummary, _: &()) { fn add_summary(&mut self, summary: &'a ElementHeightSummary, _: &()) {
self.0 += summary.pending_count; self.0 += summary.pending_count;
@ -196,10 +236,58 @@ impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for Height {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{elements::*, geometry::vector::vec2f};
#[crate::test(self)] #[crate::test(self)]
fn test_layout(cx: &mut crate::MutableAppContext) { fn test_layout(cx: &mut crate::MutableAppContext) {
let mut presenter = cx.build_presenter(0, 20.0); let mut presenter = cx.build_presenter(0, 20.0);
let layout_cx = presenter.layout_cx(cx); let mut layout_cx = presenter.layout_cx(cx);
let state = ListState::new(vec![item(20.), item(30.), item(10.)]);
let mut list = List::new(state.clone()).boxed();
let size = list.layout(
SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.)),
&mut layout_cx,
);
assert_eq!(size, vec2f(100., 40.));
assert_eq!(
state.0.lock().heights.summary(),
ElementHeightSummary {
count: 3,
pending_count: 0,
height: 60.
}
);
state.splice(1..2, vec![item(40.), item(50.)]);
state.splice(3..3, vec![item(60.)]);
assert_eq!(
state.0.lock().heights.summary(),
ElementHeightSummary {
count: 5,
pending_count: 3,
height: 30.
}
);
let size = list.layout(
SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.)),
&mut layout_cx,
);
assert_eq!(size, vec2f(100., 40.));
assert_eq!(
state.0.lock().heights.summary(),
ElementHeightSummary {
count: 5,
pending_count: 0,
height: 180.
}
);
}
fn item(height: f32) -> ElementBox {
ConstrainedBox::new(Empty::new().boxed())
.with_height(height)
.with_width(100.)
.boxed()
} }
} }