Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fine-grained tree-structure tracking in xilem and effcient tree-updates in xilem_web #160

Merged
merged 9 commits into from
Feb 27, 2024
119 changes: 80 additions & 39 deletions crates/xilem_core/src/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
#[doc(hidden)]
#[macro_export]
macro_rules! impl_view_tuple {
( $viewseq:ident, $pod:ty, $cx:ty, $changeflags:ty, $( $t:ident),* ; $( $i:tt ),* ) => {
( $viewseq:ident, $elements_splice: ident, $pod:ty, $cx:ty, $changeflags:ty, $( $t:ident),* ; $( $i:tt ),* ) => {
impl<T, A, $( $t: $viewseq<T, A> ),* > $viewseq<T, A> for ( $( $t, )* ) {
type State = ( $( $t::State, )*);

fn build(&self, cx: &mut $cx, elements: &mut Vec<$pod>) -> Self::State {
fn build(&self, cx: &mut $cx, elements: &mut dyn $elements_splice) -> Self::State {
let b = ( $( self.$i.build(cx, elements), )* );
let state = ( $( b.$i, )*);
state
Expand All @@ -19,7 +19,7 @@ macro_rules! impl_view_tuple {
cx: &mut $cx,
prev: &Self,
state: &mut Self::State,
els: &mut $crate::VecSplice<$pod>,
els: &mut dyn $elements_splice,
) -> ChangeFlags {
let mut changed = <$changeflags>::default();
$(
Expand Down Expand Up @@ -53,19 +53,67 @@ macro_rules! impl_view_tuple {
}
}
}

#[macro_export]
macro_rules! generate_viewsequence_trait {
($viewseq:ident, $view:ident, $viewmarker: ident, $bound:ident, $cx:ty, $changeflags:ty, $pod:ty; $( $ss:tt )* ) => {
($viewseq:ident, $view:ident, $viewmarker: ident, $elements_splice: ident, $bound:ident, $cx:ty, $changeflags:ty, $pod:ty; $( $ss:tt )* ) => {

/// A temporary "splice" to add, update, delete and monitor elements in a sequence of elements.
/// It is mainly intended for view sequences
///
/// Usually it's backed by a collection (e.g. `Vec`) that holds all the (existing) elements.
/// It sweeps over the element collection and does updates in place.
/// Internally it works by having a pointer/index to the current/old element (0 at the beginning),
/// and the pointer is incremented by basically all methods that mutate that sequence.
pub trait $elements_splice {
/// Insert a new element at the current index in the resulting collection (and increment the index by 1)
fn push(&mut self, element: $pod, cx: &mut $cx);
/// Mutate the next existing element, and add it to the resulting collection (and increment the index by 1)
fn mutate(&mut self, cx: &mut $cx) -> &mut $pod;
// TODO(#160) this could also track view id changes (old_id, new_id)
/// Mark any changes done by `mutate` on the current element (this doesn't change the index)
fn mark(&mut self, changeflags: $changeflags, cx: &mut $cx) -> $changeflags;
/// Delete the next n existing elements (this doesn't change the index)
fn delete(&mut self, n: usize, cx: &mut $cx);
/// Current length of the elements collection
fn len(&self, cx: &mut $cx) -> usize;
// TODO(#160) add a skip method when it is necessary (e.g. relevant for immutable ViewSequences like ropes)
}

impl<'a, 'b> $elements_splice for $crate::VecSplice<'a, 'b, $pod> {
fn push(&mut self, element: $pod, _cx: &mut $cx) {
self.push(element);
}

fn mutate(&mut self, _cx: &mut $cx) -> &mut $pod
{
self.mutate()
}

fn mark(&mut self, changeflags: $changeflags, _cx: &mut $cx) -> $changeflags
{
self.peek_mut().map(|pod| pod.mark(changeflags)).unwrap_or_default()
}

fn delete(&mut self, n: usize, _cx: &mut $cx) {
self.delete(n)
}

fn len(&self, _cx: &mut $cx) -> usize {
self.len()
}
}

/// This trait represents a (possibly empty) sequence of views.
///
/// It is up to the parent view how to lay out and display them.
pub trait $viewseq<T, A = ()> $( $ss )* {
/// Associated states for the views.
type State $( $ss )*;

// To be able to monitor changes (e.g. tree-structure tracking) rather than just adding elements,
// this takes an element splice as well (when it could be just a `Vec` otherwise)
/// Build the associated widgets and initialize all states.
fn build(&self, cx: &mut $cx, elements: &mut Vec<$pod>) -> Self::State;
fn build(&self, cx: &mut $cx, elements: &mut dyn $elements_splice) -> Self::State;

/// Update the associated widget.
///
Expand All @@ -75,7 +123,7 @@ macro_rules! generate_viewsequence_trait {
cx: &mut $cx,
prev: &Self,
state: &mut Self::State,
element: &mut $crate::VecSplice<$pod>,
elements: &mut dyn $elements_splice,
) -> $changeflags;

/// Propagate a message.
Expand All @@ -100,9 +148,9 @@ macro_rules! generate_viewsequence_trait {
{
type State = (<V as $view<T, A>>::State, $crate::Id);

fn build(&self, cx: &mut $cx, elements: &mut Vec<$pod>) -> Self::State {
fn build(&self, cx: &mut $cx, elements: &mut dyn $elements_splice) -> Self::State {
let (id, state, element) = <V as $view<T, A>>::build(self, cx);
elements.push(<$pod>::new(element));
elements.push(<$pod>::new(element), cx);
(state, id)
}

Expand All @@ -111,9 +159,9 @@ macro_rules! generate_viewsequence_trait {
cx: &mut $cx,
prev: &Self,
state: &mut Self::State,
element: &mut $crate::VecSplice<$pod>,
elements: &mut dyn $elements_splice,
) -> $changeflags {
let el = element.mutate();
let el = elements.mutate(cx);
let downcast = el.downcast_mut().unwrap();
let flags = <V as $view<T, A>>::rebuild(
self,
Expand All @@ -123,8 +171,7 @@ macro_rules! generate_viewsequence_trait {
&mut state.0,
downcast,
);

el.mark(flags)
elements.mark(flags, cx)
}

fn message(
Expand Down Expand Up @@ -156,7 +203,7 @@ macro_rules! generate_viewsequence_trait {
impl<T, A, VT: $viewseq<T, A>> $viewseq<T, A> for Option<VT> {
type State = Option<VT::State>;

fn build(&self, cx: &mut $cx, elements: &mut Vec<$pod>) -> Self::State {
fn build(&self, cx: &mut $cx, elements: &mut dyn $elements_splice) -> Self::State {
match self {
None => None,
Some(vt) => {
Expand All @@ -171,20 +218,19 @@ macro_rules! generate_viewsequence_trait {
cx: &mut $cx,
prev: &Self,
state: &mut Self::State,
element: &mut $crate::VecSplice<$pod>,
elements: &mut dyn $elements_splice,
) -> $changeflags {
match (self, &mut *state, prev) {
(Some(this), Some(state), Some(prev)) => this.rebuild(cx, prev, state, element),
(Some(this), Some(state), Some(prev)) => this.rebuild(cx, prev, state, elements),
(None, Some(seq_state), Some(prev)) => {
let count = prev.count(&seq_state);
element.delete(count);
elements.delete(count, cx);
*state = None;

<$changeflags>::tree_structure()
}
(Some(this), None, None) => {
let seq_state = element.as_vec(|vec| this.build(cx, vec));
*state = Some(seq_state);
*state = Some(this.build(cx, elements));

<$changeflags>::tree_structure()
}
Expand Down Expand Up @@ -219,7 +265,7 @@ macro_rules! generate_viewsequence_trait {
impl<T, A, VT: $viewseq<T, A>> $viewseq<T, A> for Vec<VT> {
type State = Vec<VT::State>;

fn build(&self, cx: &mut $cx, elements: &mut Vec<$pod>) -> Self::State {
fn build(&self, cx: &mut $cx, elements: &mut dyn $elements_splice) -> Self::State {
self.iter().map(|child| child.build(cx, elements)).collect()
}

Expand All @@ -228,7 +274,7 @@ macro_rules! generate_viewsequence_trait {
cx: &mut $cx,
prev: &Self,
state: &mut Self::State,
elements: &mut $crate::VecSplice<$pod>,
elements: &mut dyn $elements_splice,
) -> $changeflags {
let mut changed = <$changeflags>::default();
for ((child, child_prev), child_state) in self.iter().zip(prev).zip(state.iter_mut()) {
Expand All @@ -242,16 +288,11 @@ macro_rules! generate_viewsequence_trait {
.enumerate()
.map(|(i, state)| prev[n + i].count(&state))
.sum();
elements.delete(n_delete);
elements.delete(n_delete, cx);
changed |= <$changeflags>::tree_structure();
} else if n > prev.len() {
let mut child_elements = vec![];
for i in prev.len()..n {
state.push(self[i].build(cx, &mut child_elements));
}
// Discussion question: should VecSplice get an extend method?
for element in child_elements {
elements.push(element);
state.push(self[i].build(cx, elements));
}
changed |= <$changeflags>::tree_structure();
}
Expand Down Expand Up @@ -295,26 +336,26 @@ macro_rules! generate_viewsequence_trait {
#[doc = concat!("`", stringify!($viewmarker), "`.")]
pub trait $viewmarker {}

$crate::impl_view_tuple!($viewseq, $pod, $cx, $changeflags, ;);
$crate::impl_view_tuple!($viewseq, $pod, $cx, $changeflags,
$crate::impl_view_tuple!($viewseq, $elements_splice, $pod, $cx, $changeflags, ;);
$crate::impl_view_tuple!($viewseq, $elements_splice, $pod, $cx, $changeflags,
V0; 0);
$crate::impl_view_tuple!($viewseq, $pod, $cx, $changeflags,
$crate::impl_view_tuple!($viewseq, $elements_splice, $pod, $cx, $changeflags,
V0, V1; 0, 1);
$crate::impl_view_tuple!($viewseq, $pod, $cx, $changeflags,
$crate::impl_view_tuple!($viewseq, $elements_splice, $pod, $cx, $changeflags,
V0, V1, V2; 0, 1, 2);
$crate::impl_view_tuple!($viewseq, $pod, $cx, $changeflags,
$crate::impl_view_tuple!($viewseq, $elements_splice, $pod, $cx, $changeflags,
V0, V1, V2, V3; 0, 1, 2, 3);
$crate::impl_view_tuple!($viewseq, $pod, $cx, $changeflags,
$crate::impl_view_tuple!($viewseq, $elements_splice, $pod, $cx, $changeflags,
V0, V1, V2, V3, V4; 0, 1, 2, 3, 4);
$crate::impl_view_tuple!($viewseq, $pod, $cx, $changeflags,
$crate::impl_view_tuple!($viewseq, $elements_splice, $pod, $cx, $changeflags,
V0, V1, V2, V3, V4, V5; 0, 1, 2, 3, 4, 5);
$crate::impl_view_tuple!($viewseq, $pod, $cx, $changeflags,
$crate::impl_view_tuple!($viewseq, $elements_splice, $pod, $cx, $changeflags,
V0, V1, V2, V3, V4, V5, V6; 0, 1, 2, 3, 4, 5, 6);
$crate::impl_view_tuple!($viewseq, $pod, $cx, $changeflags,
$crate::impl_view_tuple!($viewseq, $elements_splice, $pod, $cx, $changeflags,
V0, V1, V2, V3, V4, V5, V6, V7; 0, 1, 2, 3, 4, 5, 6, 7);
$crate::impl_view_tuple!($viewseq, $pod, $cx, $changeflags,
$crate::impl_view_tuple!($viewseq, $elements_splice, $pod, $cx, $changeflags,
V0, V1, V2, V3, V4, V5, V6, V7, V8; 0, 1, 2, 3, 4, 5, 6, 7, 8);
$crate::impl_view_tuple!($viewseq, $pod, $cx, $changeflags,
$crate::impl_view_tuple!($viewseq, $elements_splice, $pod, $cx, $changeflags,
V0, V1, V2, V3, V4, V5, V6, V7, V8, V9; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
};
}
8 changes: 8 additions & 0 deletions crates/xilem_core/src/vec_splice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ impl<'a, 'b, T> VecSplice<'a, 'b, T> {
&mut self.v[ix]
}

pub fn peek(&self) -> Option<&T> {
self.v.last()
}

pub fn peek_mut(&mut self) -> Option<&mut T> {
self.v.last_mut()
}

pub fn len(&self) -> usize {
self.ix
}
Expand Down
Loading