Skip to main content

ferroui_view/
control_flow.rs

1//! Children that follow state: a keyed list ([`for_each`]), a condition
2//! ([`show`]), anything computed ([`dynamic`]), and the reference to a
3//! control an element made ([`NodeRef`]).
4//!
5//! Each is an element that a parent mounts in place: its children are made
6//! when it is mounted and made again (or kept, by key) when what they read
7//! changes. Each child has an owner of its own, disposed when the child is
8//! taken out, so the signals and effects a child made end with it.
9
10use crate::element::{AnyValueEq, DynamicChildren, Element, IntoElement, Key};
11use crate::reactive::{memo, signal, Signal};
12use ferroui_base::{ObjectType, Ref};
13use std::rc::Rc;
14
15/// A list of children made from items, one per item, kept by key: an item
16/// whose key and value stay keeps its control; an item that is new or whose
17/// value changed gets a new one; the controls of items that left are taken
18/// out. `each` is reactive: the list follows the signals it reads.
19///
20/// ```ignore
21/// for_each(move || todos.get(), |todo| todo.id, move |todo| text_block().text(todo.title))
22/// ```
23pub fn for_each<T, I, K, E>(
24    each: impl Fn() -> I + 'static,
25    key: impl Fn(&T) -> K + 'static,
26    view: impl Fn(T) -> E + 'static,
27) -> Element
28where
29    I: IntoIterator<Item = T>,
30    T: Clone + PartialEq + 'static,
31    K: Into<Key>,
32    E: IntoElement,
33{
34    let view = Rc::new(view);
35    Element::Dynamic(DynamicChildren {
36        render: Rc::new(move || {
37            each()
38                .into_iter()
39                .map(|item| {
40                    let key = key(&item).into();
41                    let value: Rc<dyn AnyValueEq> = Rc::new(item.clone());
42                    let view = view.clone();
43                    let render: Rc<dyn Fn() -> Element> = Rc::new(move || view(item.clone()).into_element());
44                    (Some(key), Some(value), render)
45                })
46                .collect()
47        }),
48    })
49}
50
51/// The children of `then` while `when` is true, and nothing otherwise. The
52/// children are made again only when the condition changes.
53pub fn show<E: IntoElement>(when: impl Fn() -> bool + 'static, then: impl Fn() -> E + 'static) -> Element {
54    show_else(when, then, || Element::Empty)
55}
56
57/// The children of `then` while `when` is true, those of `otherwise`
58/// otherwise.
59pub fn show_else<E: IntoElement, O: IntoElement>(
60    when: impl Fn() -> bool + 'static,
61    then: impl Fn() -> E + 'static,
62    otherwise: impl Fn() -> O + 'static,
63) -> Element {
64    let condition = memo(move |_| when());
65    let then: Rc<dyn Fn() -> Element> = Rc::new(move || then().into_element());
66    let otherwise: Rc<dyn Fn() -> Element> = Rc::new(move || otherwise().into_element());
67    Element::Dynamic(DynamicChildren {
68        render: Rc::new(move || {
69            let render = if condition.get() { then.clone() } else { otherwise.clone() };
70            vec![(None, None, render)]
71        }),
72    })
73}
74
75/// Children computed by a reactive closure: made again whenever what it
76/// read changes. An element with a key keeps its control while an element
77/// with the same key comes back (its values are not compared: give the key
78/// what identifies the values, or use [`for_each`]).
79pub fn dynamic<E: IntoElement>(render: impl Fn() -> E + 'static) -> Element {
80    Element::Dynamic(DynamicChildren {
81        render: Rc::new(move || {
82            let element = render().into_element();
83            let children = match element {
84                Element::Fragment(items) => items,
85                Element::Empty => Vec::new(),
86                element => vec![element],
87            };
88            children
89                .into_iter()
90                .map(|child| {
91                    let key = child.key().cloned();
92                    let render: Rc<dyn Fn() -> Element> = Rc::new(move || child.clone());
93                    (key, None, render)
94                })
95                .collect()
96        }),
97    })
98}
99
100/// The control an element made, once it is mounted (see
101/// [`El::node_ref`](crate::El::node_ref)). `Copy`; reading it is reactive.
102pub struct NodeRef<T: ObjectType> {
103    control: Signal<Option<Ref<T>>>,
104}
105
106impl<T: ObjectType> Clone for NodeRef<T> {
107    fn clone(&self) -> Self {
108        *self
109    }
110}
111
112impl<T: ObjectType> Copy for NodeRef<T> {}
113
114impl<T: ObjectType> NodeRef<T> {
115    /// A reference with no control yet, owned by the current owner.
116    pub fn new() -> Self {
117        Self { control: signal(None) }
118    }
119
120    /// The control, once mounted.
121    pub fn get(&self) -> Option<Ref<T>> {
122        self.control.get()
123    }
124
125    /// The control without recording the read.
126    pub fn get_untracked(&self) -> Option<Ref<T>> {
127        self.control.get_untracked()
128    }
129
130    pub(crate) fn set(&self, control: Ref<T>) {
131        self.control.set(Some(control));
132    }
133}
134
135impl<T: ObjectType> Default for NodeRef<T> {
136    fn default() -> Self {
137        Self::new()
138    }
139}