//! Store internal widget state in a state tree to ensure continuity. use crate::Widget; use std::any::{self, Any}; use std::borrow::Borrow; use std::fmt; /// A persistent state widget tree. /// /// A [`Tree`] is normally associated with a specific widget in the widget tree. #[derive(Debug)] pub struct Tree { /// The tag of the [`Tree`]. pub tag: Tag, /// The [`State`] of the [`Tree`]. pub state: State, /// The children of the root widget of the [`Tree`]. pub children: Vec, } impl Tree { /// Creates an empty, stateless [`Tree`] with no children. pub fn empty() -> Self { Self { tag: Tag::stateless(), state: State::None, children: Vec::new(), } } /// Creates a new [`Tree`] for the provided [`Widget`]. pub fn new<'a, Message, Theme, Renderer>( widget: impl Borrow + 'a>, ) -> Self where Renderer: crate::Renderer, { let widget = widget.borrow(); Self { tag: widget.tag(), state: widget.state(), children: widget.children(), } } /// Reconciles the current tree with the provided [`Widget`]. /// /// If the tag of the [`Widget`] matches the tag of the [`Tree`], then the /// [`Widget`] proceeds with the reconciliation (i.e. [`Widget::diff`] is called). /// /// Otherwise, the whole [`Tree`] is recreated. /// /// [`Widget::diff`]: crate::Widget::diff pub fn diff<'a, Message, Theme, Renderer>( &mut self, new: impl Borrow + 'a>, ) where Renderer: crate::Renderer, { if self.tag == new.borrow().tag() { new.borrow().diff(self); } else { *self = Self::new(new); } } /// Reconciles the children of the tree with the provided list of widgets. pub fn diff_children<'a, Message, Theme, Renderer>( &mut self, new_children: &[impl Borrow + 'a>], ) where Renderer: crate::Renderer, { self.diff_children_custom( new_children, |tree, widget| tree.diff(widget.borrow()), |widget| Self::new(widget.borrow()), ); } /// Reconciles the children of the tree with the provided list of widgets using custom /// logic both for diffing and creating new widget state. pub fn diff_children_custom( &mut self, new_children: &[T], diff: impl Fn(&mut Tree, &T), new_state: impl Fn(&T) -> Self, ) { if self.children.len() > new_children.len() { self.children.truncate(new_children.len()); } for (child_state, new) in self.children.iter_mut().zip(new_children.iter()) { diff(child_state, new); } if self.children.len() < new_children.len() { self.children.extend( new_children[self.children.len()..].iter().map(new_state), ); } } } /// Reconciles the `current_children` with the provided list of widgets using /// custom logic both for diffing and creating new widget state. /// /// The algorithm will try to minimize the impact of diffing by querying the /// `maybe_changed` closure. pub fn diff_children_custom_with_search( current_children: &mut Vec, new_children: &[T], diff: impl Fn(&mut Tree, &T), maybe_changed: impl Fn(usize) -> bool, new_state: impl Fn(&T) -> Tree, ) { if new_children.is_empty() { current_children.clear(); return; } if current_children.is_empty() { current_children.extend(new_children.iter().map(new_state)); return; } let first_maybe_changed = maybe_changed(0); let last_maybe_changed = maybe_changed(current_children.len() - 1); if current_children.len() > new_children.len() { if !first_maybe_changed && last_maybe_changed { current_children.truncate(new_children.len()); } else { let difference_index = if first_maybe_changed { 0 } else { (1..current_children.len()) .find(|&i| maybe_changed(i)) .unwrap_or(0) }; let _ = current_children.splice( difference_index ..difference_index + (current_children.len() - new_children.len()), std::iter::empty(), ); } } if current_children.len() < new_children.len() { let first_maybe_changed = maybe_changed(0); let last_maybe_changed = maybe_changed(current_children.len() - 1); if !first_maybe_changed && last_maybe_changed { current_children.extend( new_children[current_children.len()..].iter().map(new_state), ); } else { let difference_index = if first_maybe_changed { 0 } else { (1..current_children.len()) .find(|&i| maybe_changed(i)) .unwrap_or(0) }; let _ = current_children.splice( difference_index..difference_index, new_children[difference_index ..difference_index + (new_children.len() - current_children.len())] .iter() .map(new_state), ); } } // TODO: Merge loop with extend logic (?) for (child_state, new) in current_children.iter_mut().zip(new_children.iter()) { diff(child_state, new); } } /// The identifier of some widget state. #[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] pub struct Tag(any::TypeId); impl Tag { /// Creates a [`Tag`] for a state of type `T`. pub fn of() -> Self where T: 'static, { Self(any::TypeId::of::()) } /// Creates a [`Tag`] for a stateless widget. pub fn stateless() -> Self { Self::of::<()>() } } /// The internal [`State`] of a widget. pub enum State { /// No meaningful internal state. None, /// Some meaningful internal state. Some(Box), } impl State { /// Creates a new [`State`]. pub fn new(state: T) -> Self where T: 'static, { State::Some(Box::new(state)) } /// Downcasts the [`State`] to `T` and returns a reference to it. /// /// # Panics /// This method will panic if the downcast fails or the [`State`] is [`State::None`]. pub fn downcast_ref(&self) -> &T where T: 'static, { match self { State::None => panic!("Downcast on stateless state"), State::Some(state) => { state.downcast_ref().expect("Downcast widget state") } } } /// Downcasts the [`State`] to `T` and returns a mutable reference to it. /// /// # Panics /// This method will panic if the downcast fails or the [`State`] is [`State::None`]. pub fn downcast_mut(&mut self) -> &mut T where T: 'static, { match self { State::None => panic!("Downcast on stateless state"), State::Some(state) => { state.downcast_mut().expect("Downcast widget state") } } } } impl fmt::Debug for State { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::None => write!(f, "State::None"), Self::Some(_) => write!(f, "State::Some"), } } }