iced_core/widget/
tree.rs

1//! Store internal widget state in a state tree to ensure continuity.
2use crate::id::{Id, Internal};
3use crate::Widget;
4use std::any::{self, Any};
5use std::borrow::{Borrow, BorrowMut, Cow};
6use std::collections::{HashMap, VecDeque};
7use std::hash::Hash;
8use std::{fmt, mem};
9
10thread_local! {
11    /// A map of named widget states.
12pub static NAMED: std::cell::RefCell<HashMap<Cow<'static, str>, (State, Vec<(usize, Tree)>)>> = std::cell::RefCell::new(HashMap::new());
13}
14
15/// A persistent state widget tree.
16///
17/// A [`Tree`] is normally associated with a specific widget in the widget tree.
18#[derive(Debug)]
19pub struct Tree {
20    /// The tag of the [`Tree`].
21    pub tag: Tag,
22
23    /// the Id of the [`Tree`]
24    pub id: Option<Id>,
25
26    /// The [`State`] of the [`Tree`].
27    pub state: State,
28
29    /// The children of the root widget of the [`Tree`].
30    pub children: Vec<Tree>,
31}
32
33impl Tree {
34    /// Creates an empty, stateless [`Tree`] with no children.
35    pub fn empty() -> Self {
36        Self {
37            id: None,
38            tag: Tag::stateless(),
39            state: State::None,
40            children: Vec::new(),
41        }
42    }
43
44    /// Creates a new [`Tree`] for the provided [`Widget`].
45    pub fn new<'a, Message, Theme, Renderer>(
46        widget: impl Borrow<dyn Widget<Message, Theme, Renderer> + 'a>,
47    ) -> Self
48    where
49        Renderer: crate::Renderer,
50    {
51        let widget = widget.borrow();
52
53        Self {
54            id: widget.id(),
55            tag: widget.tag(),
56            state: widget.state(),
57            children: widget.children(),
58        }
59    }
60
61    /// Takes all named widgets from the tree.
62    pub fn take_all_named(
63        &mut self,
64    ) -> HashMap<Cow<'static, str>, (State, Vec<(usize, Tree)>)> {
65        let mut named = HashMap::new();
66        struct Visit {
67            parent: Cow<'static, str>,
68            index: usize,
69            visited: bool,
70        }
71        // tree traversal to find all named widgets
72        // and keep their state and children
73        let mut stack = vec![(self, None)];
74        while let Some((tree, visit)) = stack.pop() {
75            if let Some(Id(Internal::Custom(_, n))) = tree.id.clone() {
76                let state = mem::replace(&mut tree.state, State::None);
77                let children_count = tree.children.len();
78                let children =
79                    tree.children.iter_mut().enumerate().rev().map(|(i, c)| {
80                        if matches!(c.id, Some(Id(Internal::Custom(_, _)))) {
81                            (c, None)
82                        } else {
83                            (
84                                c,
85                                Some(Visit {
86                                    index: i,
87                                    parent: n.clone(),
88                                    visited: false,
89                                }),
90                            )
91                        }
92                    });
93                _ = named.insert(
94                    n.clone(),
95                    (state, Vec::with_capacity(children_count)),
96                );
97                stack.extend(children);
98            } else if let Some(visit) = visit {
99                if visit.visited {
100                    named.get_mut(&visit.parent).unwrap().1.push((
101                        visit.index,
102                        mem::replace(
103                            tree,
104                            Tree {
105                                id: tree.id.clone(),
106                                tag: tree.tag,
107                                ..Tree::empty()
108                            },
109                        ),
110                    ));
111                } else {
112                    let ptr = tree as *mut Tree;
113
114                    stack.push((
115                        // TODO remove this unsafe block
116                        #[allow(unsafe_code)]
117                        // SAFETY: when the reference is finally accessed, all the children references will have been processed first.
118                        unsafe {
119                            ptr.as_mut().unwrap()
120                        },
121                        Some(Visit {
122                            visited: true,
123                            ..visit
124                        }),
125                    ));
126                    stack.extend(tree.children.iter_mut().map(|c| (c, None)));
127                }
128            } else {
129                stack.extend(tree.children.iter_mut().map(|s| (s, None)));
130            }
131        }
132
133        named
134    }
135
136    /// Finds a widget state in the tree by its id.
137    pub fn find<'a>(&'a self, id: &Id) -> Option<&'a Tree> {
138        if self.id == Some(id.clone()) {
139            return Some(self);
140        }
141
142        for child in self.children.iter() {
143            if let Some(tree) = child.find(id) {
144                return Some(tree);
145            }
146        }
147
148        None
149    }
150
151    /// Reconciliates the current tree with the provided [`Widget`].
152    ///
153    /// If the tag of the [`Widget`] matches the tag of the [`Tree`], then the
154    /// [`Widget`] proceeds with the reconciliation (i.e. [`Widget::diff`] is called).
155    ///
156    /// Otherwise, the whole [`Tree`] is recreated.
157    ///
158    /// [`Widget::diff`]: crate::Widget::diff
159    pub fn diff<'a, Message, Theme, Renderer>(
160        &mut self,
161        mut new: impl BorrowMut<dyn Widget<Message, Theme, Renderer> + 'a>,
162    ) where
163        Renderer: crate::Renderer,
164    {
165        let borrowed: &mut dyn Widget<Message, Theme, Renderer> =
166            new.borrow_mut();
167
168        let mut tag_match = self.tag == borrowed.tag();
169        if tag_match {
170            if let Some(Id(Internal::Custom(_, n))) = borrowed.id() {
171                if let Some((mut state, children)) = NAMED
172                    .with(|named| named.borrow_mut().remove(&n))
173                    .or_else(|| {
174                        //check self.id
175                        if let Some(Id(Internal::Custom(_, ref name))) = self.id
176                        {
177                            if name == &n {
178                                Some((
179                                    mem::replace(&mut self.state, State::None),
180                                    self.children
181                                        .iter_mut()
182                                        .map(|s| {
183                                            // take the data
184                                            mem::replace(
185                                                s,
186                                                Tree {
187                                                    id: s.id.clone(),
188                                                    tag: s.tag,
189                                                    ..Tree::empty()
190                                                },
191                                            )
192                                        })
193                                        .enumerate()
194                                        .collect(),
195                                ))
196                            } else {
197                                None
198                            }
199                        } else {
200                            None
201                        }
202                    })
203                {
204                    std::mem::swap(&mut self.state, &mut state);
205                    let widget_children = borrowed.children();
206                    if !tag_match
207                        || self.children.len() != widget_children.len()
208                    {
209                        self.children = borrowed.children();
210                    } else {
211                        for (old_i, mut old) in children {
212                            let Some(my_state) = self.children.get_mut(old_i)
213                            else {
214                                continue;
215                            };
216                            if my_state.tag != old.tag || {
217                                !match (&old.id, &my_state.id) {
218                                    (
219                                        Some(Id(Internal::Custom(
220                                            _,
221                                            ref old_name,
222                                        ))),
223                                        Some(Id(Internal::Custom(
224                                            _,
225                                            ref my_name,
226                                        ))),
227                                    ) => old_name == my_name,
228                                    (
229                                        Some(Id(Internal::Set(a))),
230                                        Some(Id(Internal::Set(b))),
231                                    ) => a.len() == b.len(),
232                                    (
233                                        Some(Id(Internal::Unique(_))),
234                                        Some(Id(Internal::Unique(_))),
235                                    ) => true,
236                                    (None, None) => true,
237                                    _ => false,
238                                }
239                            } {
240                                continue;
241                            }
242
243                            mem::swap(my_state, &mut old);
244                        }
245                    }
246                } else {
247                    tag_match = false;
248                }
249            } else {
250                if let Some(id) = self.id.clone() {
251                    borrowed.set_id(id);
252                }
253                if self.children.len() != borrowed.children().len() {
254                    self.children = borrowed.children();
255                }
256            }
257        }
258        if tag_match {
259            borrowed.diff(self);
260        } else {
261            *self = Self::new(borrowed);
262            let borrowed = new.borrow_mut();
263            borrowed.diff(self);
264        }
265    }
266
267    /// Reconciles the children of the tree with the provided list of widgets.
268    pub fn diff_children<'a, Message, Theme, Renderer>(
269        &mut self,
270        new_children: &mut [impl BorrowMut<
271            dyn Widget<Message, Theme, Renderer> + 'a,
272        >],
273    ) where
274        Renderer: crate::Renderer,
275    {
276        self.diff_children_custom(
277            new_children,
278            new_children.iter().map(|c| c.borrow().id()).collect(),
279            |tree, widget| {
280                let borrowed: &mut dyn Widget<_, _, _> = widget.borrow_mut();
281
282                tree.diff(borrowed);
283            },
284            |widget| {
285                let borrowed: &dyn Widget<_, _, _> = widget.borrow();
286                Self::new(borrowed)
287            },
288        );
289    }
290
291    /// Reconciles the children of the tree with the provided list of widgets using custom
292    /// logic both for diffing and creating new widget state.
293    pub fn diff_children_custom<T>(
294        &mut self,
295        new_children: &mut [T],
296        new_ids: Vec<Option<Id>>,
297        diff: impl Fn(&mut Tree, &mut T),
298        new_state: impl Fn(&T) -> Self,
299    ) {
300        if self.children.len() > new_children.len() {
301            self.children.truncate(new_children.len());
302        }
303
304        let children_len = self.children.len();
305        let (mut id_map, mut id_list): (
306            HashMap<String, &mut Tree>,
307            VecDeque<(usize, &mut Tree)>,
308        ) = self.children.iter_mut().enumerate().fold(
309            (HashMap::new(), VecDeque::with_capacity(children_len)),
310            |(mut id_map, mut id_list), (i, c)| {
311                if let Some(id) = c.id.as_ref() {
312                    if let Internal::Custom(_, ref name) = id.0 {
313                        let _ = id_map.insert(name.to_string(), c);
314                    } else {
315                        id_list.push_back((i, c));
316                    }
317                } else {
318                    id_list.push_back((i, c));
319                }
320                (id_map, id_list)
321            },
322        );
323
324        let mut new_trees: Vec<(Tree, usize)> =
325            Vec::with_capacity(new_children.len());
326        for (i, (new, new_id)) in
327            new_children.iter_mut().zip(new_ids.iter()).enumerate()
328        {
329            let child_state = if let Some(c) = new_id.as_ref().and_then(|id| {
330                if let Internal::Custom(_, ref name) = id.0 {
331                    id_map.remove(name.as_ref())
332                } else {
333                    None
334                }
335            }) {
336                c
337            } else if let Some(i) = {
338                let mut found = None;
339                for c_i in 0..id_list.len() {
340                    if id_list[c_i].0 == i {
341                        found = Some(c_i);
342                        break;
343                    }
344                    if i < c_i {
345                        break;
346                    }
347                }
348                found
349            } {
350                let c = id_list.remove(i).unwrap().1;
351                c
352            } else {
353                let mut my_new_state = new_state(new);
354                diff(&mut my_new_state, new);
355                new_trees.push((my_new_state, i));
356                continue;
357            };
358
359            diff(child_state, new);
360        }
361
362        for (new_tree, i) in new_trees {
363            if self.children.len() > i {
364                self.children[i] = new_tree;
365            } else {
366                self.children.push(new_tree);
367            }
368        }
369    }
370}
371
372/// Reconciles the `current_children` with the provided list of widgets using
373/// custom logic both for diffing and creating new widget state.
374///
375/// The algorithm will try to minimize the impact of diffing by querying the
376/// `maybe_changed` closure.
377pub fn diff_children_custom_with_search<T>(
378    current_children: &mut Vec<Tree>,
379    new_children: &mut [T],
380    diff: impl Fn(&mut Tree, &mut T),
381    maybe_changed: impl Fn(usize) -> bool,
382    new_state: impl Fn(&T) -> Tree,
383) {
384    if new_children.is_empty() {
385        current_children.clear();
386        return;
387    }
388
389    if current_children.is_empty() {
390        current_children.extend(new_children.iter().map(new_state));
391        return;
392    }
393
394    let first_maybe_changed = maybe_changed(0);
395    let last_maybe_changed = maybe_changed(current_children.len() - 1);
396
397    if current_children.len() > new_children.len() {
398        if !first_maybe_changed && last_maybe_changed {
399            current_children.truncate(new_children.len());
400        } else {
401            let difference_index = if first_maybe_changed {
402                0
403            } else {
404                (1..current_children.len())
405                    .find(|&i| maybe_changed(i))
406                    .unwrap_or(0)
407            };
408
409            let _ = current_children.splice(
410                difference_index
411                    ..difference_index
412                        + (current_children.len() - new_children.len()),
413                std::iter::empty(),
414            );
415        }
416    }
417
418    if current_children.len() < new_children.len() {
419        let first_maybe_changed = maybe_changed(0);
420        let last_maybe_changed = maybe_changed(current_children.len() - 1);
421
422        if !first_maybe_changed && last_maybe_changed {
423            current_children.extend(
424                new_children[current_children.len()..].iter().map(new_state),
425            );
426        } else {
427            let difference_index = if first_maybe_changed {
428                0
429            } else {
430                (1..current_children.len())
431                    .find(|&i| maybe_changed(i))
432                    .unwrap_or(0)
433            };
434
435            let _ = current_children.splice(
436                difference_index..difference_index,
437                new_children[difference_index
438                    ..difference_index
439                        + (new_children.len() - current_children.len())]
440                    .iter()
441                    .map(new_state),
442            );
443        }
444    }
445
446    // TODO: Merge loop with extend logic (?)
447    for (child_state, new) in
448        current_children.iter_mut().zip(new_children.iter_mut())
449    {
450        diff(child_state, new);
451    }
452}
453
454/// The identifier of some widget state.
455#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
456pub struct Tag(any::TypeId);
457
458impl Tag {
459    /// Creates a [`Tag`] for a state of type `T`.
460    pub fn of<T>() -> Self
461    where
462        T: 'static,
463    {
464        Self(any::TypeId::of::<T>())
465    }
466
467    /// Creates a [`Tag`] for a stateless widget.
468    pub fn stateless() -> Self {
469        Self::of::<()>()
470    }
471}
472
473/// The internal [`State`] of a widget.
474pub enum State {
475    /// No meaningful internal state.
476    None,
477
478    /// Some meaningful internal state.
479    Some(Box<dyn Any>),
480}
481
482impl State {
483    /// Creates a new [`State`].
484    pub fn new<T>(state: T) -> Self
485    where
486        T: 'static,
487    {
488        State::Some(Box::new(state))
489    }
490
491    /// Downcasts the [`State`] to `T` and returns a reference to it.
492    ///
493    /// # Panics
494    /// This method will panic if the downcast fails or the [`State`] is [`State::None`].
495    pub fn downcast_ref<T>(&self) -> &T
496    where
497        T: 'static,
498    {
499        match self {
500            State::None => panic!("Downcast on stateless state"),
501            State::Some(state) => {
502                state.downcast_ref().expect("Downcast widget state")
503            }
504        }
505    }
506
507    /// Downcasts the [`State`] to `T` and returns a mutable reference to it.
508    ///
509    /// # Panics
510    /// This method will panic if the downcast fails or the [`State`] is [`State::None`].
511    pub fn downcast_mut<T>(&mut self) -> &mut T
512    where
513        T: 'static,
514    {
515        match self {
516            State::None => panic!("Downcast on stateless state"),
517            State::Some(state) => {
518                state.downcast_mut().expect("Downcast widget state")
519            }
520        }
521    }
522}
523
524impl fmt::Debug for State {
525    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
526        match self {
527            Self::None => write!(f, "State::None"),
528            Self::Some(_) => write!(f, "State::Some"),
529        }
530    }
531}