iced_native/widget/
tree.rs

1//! Store internal widget state in a state tree to ensure continuity.
2use crate::Widget;
3
4use std::any::{self, Any};
5use std::borrow::Borrow;
6use std::fmt;
7
8/// A persistent state widget tree.
9///
10/// A [`Tree`] is normally associated with a specific widget in the widget tree.
11#[derive(Debug)]
12pub struct Tree {
13    /// The tag of the [`Tree`].
14    pub tag: Tag,
15
16    /// The [`State`] of the [`Tree`].
17    pub state: State,
18
19    /// The children of the root widget of the [`Tree`].
20    pub children: Vec<Tree>,
21}
22
23impl Tree {
24    /// Creates an empty, stateless [`Tree`] with no children.
25    pub fn empty() -> Self {
26        Self {
27            tag: Tag::stateless(),
28            state: State::None,
29            children: Vec::new(),
30        }
31    }
32
33    /// Creates a new [`Tree`] for the provided [`Widget`].
34    pub fn new<'a, Message, Renderer>(
35        widget: impl Borrow<dyn Widget<Message, Renderer> + 'a>,
36    ) -> Self
37    where
38        Renderer: crate::Renderer,
39    {
40        let widget = widget.borrow();
41
42        Self {
43            tag: widget.tag(),
44            state: widget.state(),
45            children: widget.children(),
46        }
47    }
48
49    /// Reconciliates the current tree with the provided [`Widget`].
50    ///
51    /// If the tag of the [`Widget`] matches the tag of the [`Tree`], then the
52    /// [`Widget`] proceeds with the reconciliation (i.e. [`Widget::diff`] is called).
53    ///
54    /// Otherwise, the whole [`Tree`] is recreated.
55    ///
56    /// [`Widget::diff`]: crate::Widget::diff
57    pub fn diff<'a, Message, Renderer>(
58        &mut self,
59        new: impl Borrow<dyn Widget<Message, Renderer> + 'a>,
60    ) where
61        Renderer: crate::Renderer,
62    {
63        if self.tag == new.borrow().tag() {
64            new.borrow().diff(self)
65        } else {
66            *self = Self::new(new);
67        }
68    }
69
70    /// Reconciliates the children of the tree with the provided list of widgets.
71    pub fn diff_children<'a, Message, Renderer>(
72        &mut self,
73        new_children: &[impl Borrow<dyn Widget<Message, Renderer> + 'a>],
74    ) where
75        Renderer: crate::Renderer,
76    {
77        self.diff_children_custom(
78            new_children,
79            |tree, widget| tree.diff(widget.borrow()),
80            |widget| Self::new(widget.borrow()),
81        )
82    }
83
84    /// Reconciliates the children of the tree with the provided list of widgets using custom
85    /// logic both for diffing and creating new widget state.
86    pub fn diff_children_custom<T>(
87        &mut self,
88        new_children: &[T],
89        diff: impl Fn(&mut Tree, &T),
90        new_state: impl Fn(&T) -> Self,
91    ) {
92        if self.children.len() > new_children.len() {
93            self.children.truncate(new_children.len());
94        }
95
96        for (child_state, new) in
97            self.children.iter_mut().zip(new_children.iter())
98        {
99            diff(child_state, new);
100        }
101
102        if self.children.len() < new_children.len() {
103            self.children.extend(
104                new_children[self.children.len()..].iter().map(new_state),
105            );
106        }
107    }
108}
109
110/// The identifier of some widget state.
111#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
112pub struct Tag(any::TypeId);
113
114impl Tag {
115    /// Creates a [`Tag`] for a state of type `T`.
116    pub fn of<T>() -> Self
117    where
118        T: 'static,
119    {
120        Self(any::TypeId::of::<T>())
121    }
122
123    /// Creates a [`Tag`] for a stateless widget.
124    pub fn stateless() -> Self {
125        Self::of::<()>()
126    }
127}
128
129/// The internal [`State`] of a widget.
130pub enum State {
131    /// No meaningful internal state.
132    None,
133
134    /// Some meaningful internal state.
135    Some(Box<dyn Any>),
136}
137
138impl State {
139    /// Creates a new [`State`].
140    pub fn new<T>(state: T) -> Self
141    where
142        T: 'static,
143    {
144        State::Some(Box::new(state))
145    }
146
147    /// Downcasts the [`State`] to `T` and returns a reference to it.
148    ///
149    /// # Panics
150    /// This method will panic if the downcast fails or the [`State`] is [`State::None`].
151    pub fn downcast_ref<T>(&self) -> &T
152    where
153        T: 'static,
154    {
155        match self {
156            State::None => panic!("Downcast on stateless state"),
157            State::Some(state) => {
158                state.downcast_ref().expect("Downcast widget state")
159            }
160        }
161    }
162
163    /// Downcasts the [`State`] to `T` and returns a mutable reference to it.
164    ///
165    /// # Panics
166    /// This method will panic if the downcast fails or the [`State`] is [`State::None`].
167    pub fn downcast_mut<T>(&mut self) -> &mut T
168    where
169        T: 'static,
170    {
171        match self {
172            State::None => panic!("Downcast on stateless state"),
173            State::Some(state) => {
174                state.downcast_mut().expect("Downcast widget state")
175            }
176        }
177    }
178}
179
180impl fmt::Debug for State {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        match self {
183            Self::None => write!(f, "State::None"),
184            Self::Some(_) => write!(f, "State::Some"),
185        }
186    }
187}