Skip to main content

frus_widgets/
component.rs

1//! **Components**: [`StatelessWidget`], [`StatefulWidget`] with its [`State`], and the
2//! hooks that let a plain function keep state too.
3//!
4//! An interface is a tree of widgets, and some of them are not drawn: they are *built* —
5//! a component is a widget whose only job is to say which other widgets it stands for.
6//! `build` is called when the tree is rebuilt, and what it returns is put in its place.
7//!
8//! ```
9//! use frus_widgets::{text, BuildContext, StatelessWidget, Widget};
10//!
11//! struct Greeting {
12//!     name: String,
13//! }
14//!
15//! impl StatelessWidget for Greeting {
16//!     fn build(&self, _cx: &BuildContext) -> Box<dyn Widget> {
17//!         Box::new(text(format!("Hello, {}", self.name)))
18//!     }
19//! }
20//!
21//! // used anywhere a widget is:
22//! let greeting = Greeting { name: "world".into() }.into_widget();
23//! # let _ = greeting;
24//! ```
25//!
26//! ## State that outlives a rebuild
27//!
28//! The tree is rebuilt from scratch whenever something changes, so anything a widget must
29//! remember cannot live in the widget. A [`StatefulWidget`] splits in two: the widget is
30//! the *configuration* — cheap, thrown away and made again on every rebuild — and its
31//! [`State`] is what stays. The framework keeps each `State` under the widget's place in
32//! the tree, calls [`State::init_state`] when it is first created, [`State::did_update_widget`]
33//! whenever the configuration above it changes, and [`State::dispose`] when the widget
34//! leaves the tree.
35//!
36//! ```
37//! use frus_widgets::{
38//!     button, column, text, BuildContext, State, StateContext, StatefulWidget, Widget,
39//! };
40//!
41//! struct Counter;
42//!
43//! struct CounterState {
44//!     count: i32,
45//! }
46//!
47//! impl StatefulWidget for Counter {
48//!     type State = CounterState;
49//!     fn create_state(&self) -> CounterState {
50//!         CounterState { count: 0 }
51//!     }
52//! }
53//!
54//! impl State for CounterState {
55//!     type Widget = Counter;
56//!     fn build(&self, cx: &StateContext<Self>) -> Box<dyn Widget> {
57//!         Box::new(column![
58//!             text(format!("{}", self.count)),
59//!             // `callback` is `set_state` wrapped up as a handler
60//!             button("+", cx.callback(|state| state.count += 1)),
61//!         ])
62//!     }
63//! }
64//! ```
65//!
66//! ## State without a struct
67//!
68//! A function of `&BuildContext` is a component too, and inside one, **hooks** —
69//! [`BuildContext::use_state`], [`BuildContext::use_ref`], [`BuildContext::use_memo`],
70//! [`BuildContext::use_effect`] — keep what it needs between rebuilds. They are matched to
71//! their values by the order they are called in, so they are called **unconditionally and in
72//! the same order** every time: never inside an `if`, a loop or a closure.
73//!
74//! ```
75//! use frus_widgets::{button, column, text, BuildContext, StatelessWidget, Widget};
76//!
77//! fn counter(cx: &BuildContext) -> Box<dyn Widget> {
78//!     let count = cx.use_state(|| 0);
79//!     let add = count.clone();
80//!     Box::new(column![
81//!         text(format!("{}", count.get())),
82//!         button("+", move || add.update(|n| *n += 1)),
83//!     ])
84//! }
85//!
86//! let widget = counter.into_widget();
87//! # let _ = widget;
88//! ```
89//!
90//! ## Where a state lives
91//!
92//! A state is found again by **where its widget sits**: the same place in the tree, the
93//! same kind of widget. Give a widget a key ([`Component::with_key`], or `keyed(..)`
94//! around it) and it is found by the key instead, wherever its siblings move it. A widget
95//! that is no longer built in a frame that rebuilt the tree is disposed at the end of that
96//! frame.
97
98use std::any::{Any, TypeId};
99use std::cell::{Cell, OnceCell, RefCell};
100use std::collections::HashMap;
101use std::hash::{Hash, Hasher};
102use std::ops::Deref;
103use std::rc::Rc;
104
105use frus_core::{Rect, Scene};
106use frus_layout::Style;
107
108use crate::callback::Callback;
109use crate::interaction::{Status, WidgetId};
110use crate::runtime::Runtime;
111use crate::theme::Theme;
112use crate::widget::Widget;
113
114// ---------------------------------------------------------------------------------------
115// Asking for a rebuild
116// ---------------------------------------------------------------------------------------
117
118thread_local! {
119    static REBUILD: Cell<bool> = const { Cell::new(false) };
120    static DEPTH: Cell<u32> = const { Cell::new(0) };
121    static INTERVALS: RefCell<Vec<Interval>> = const { RefCell::new(Vec::new()) };
122    static BACK_BLOCKS: Cell<u32> = const { Cell::new(0) };
123}
124
125/// Whether some component of the latest build has said that the back gesture and the back
126/// button are not for the router to answer — an open menu or dialog is what they close first.
127/// See [`BuildContext::block_back`].
128pub fn back_blocked() -> bool {
129    BACK_BLOCKS.with(|blocks| blocks.get() > 0)
130}
131
132/// A timer a component asked for with [`BuildContext::use_interval`].
133#[derive(Clone)]
134pub struct Interval {
135    /// What tells this timer from the others, and the same one from one build to the next: it
136    /// is what the shell diffs to start a timer, keep it running, and stop it.
137    pub id: u64,
138    /// How often it fires.
139    pub period: std::time::Duration,
140    /// What it does when it does.
141    pub callback: Callback,
142}
143
144/// The timers the components of the latest build asked for. The shell starts the ones it is not
145/// running and stops the ones no longer here.
146pub fn intervals() -> Vec<Interval> {
147    INTERVALS.with(|intervals| intervals.borrow().clone())
148}
149
150/// Asks the shell to rebuild the tree on its next frame. Anything that changed what a
151/// component would build — [`StateHandle::set_state`], a hook's `set`, a controller —
152/// calls this; an application rarely does.
153pub fn request_rebuild() {
154    REBUILD.with(|flag| flag.set(true));
155}
156
157/// Whether a rebuild has been asked for and not yet taken. Asking does not consume it.
158pub fn rebuild_requested() -> bool {
159    REBUILD.with(|flag| flag.get())
160}
161
162/// Whether a rebuild was asked for since the last time this was called, and forgets it.
163/// The shell reads this at the top of every frame.
164pub fn take_rebuild_request() -> bool {
165    REBUILD.with(|flag| flag.replace(false))
166}
167
168/// How many components deep the transparent chain being expanded is. A component that
169/// builds another component *directly* is the same place in the tree as it, so they are
170/// told apart by this.
171struct Deeper(u32);
172
173impl Deeper {
174    fn enter() -> Self {
175        let now = DEPTH.with(|d| d.get());
176        DEPTH.with(|d| d.set(now + 1));
177        Deeper(now)
178    }
179}
180
181impl Drop for Deeper {
182    fn drop(&mut self) {
183        DEPTH.with(|d| d.set(self.0));
184    }
185}
186
187// ---------------------------------------------------------------------------------------
188// The store
189// ---------------------------------------------------------------------------------------
190
191/// Where one component's state is found: its place in the tree, and how far down a chain of
192/// components that place is.
193type StateKey = (WidgetId, u32);
194
195/// What every component's state, and every hook's, is kept in between rebuilds. It lives in
196/// the [`Runtime`], under the place of the component that owns it.
197#[derive(Default)]
198pub struct StateStore {
199    entries: RefCell<HashMap<StateKey, Entry>>,
200    /// Counts the rebuilds. What a rebuild does not touch, it has left behind.
201    epoch: Cell<u64>,
202    /// Whether a rebuild has run since the last sweep.
203    building: Cell<bool>,
204    effects: RefCell<Vec<PendingEffect>>,
205    services: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
206}
207
208struct Entry {
209    kind: TypeId,
210    epoch: u64,
211    state: Option<Rc<dyn Any>>,
212    hooks: Rc<Hooks>,
213    dispose: Option<Box<dyn FnOnce()>>,
214}
215
216impl Entry {
217    fn dispose_all(self) {
218        if let Some(dispose) = self.dispose {
219            dispose();
220        }
221        self.hooks.dispose();
222    }
223}
224
225/// Work that undoes what an effect did.
226type Cleanup = Box<dyn FnOnce()>;
227
228struct PendingEffect {
229    hooks: Rc<Hooks>,
230    index: usize,
231    run: Box<dyn FnOnce() -> Option<Cleanup>>,
232}
233
234impl StateStore {
235    /// A rebuild of the tree is about to run. Every state it reaches is marked as reached.
236    pub fn begin_build(&self) {
237        self.epoch.set(self.epoch.get() + 1);
238        self.building.set(true);
239        // Each build says which timers it wants, and what stands in the way of going back,
240        // from scratch.
241        INTERVALS.with(|intervals| intervals.borrow_mut().clear());
242        BACK_BLOCKS.with(|blocks| blocks.set(0));
243    }
244
245    /// The frame is done laying out: the states a rebuild did not reach are disposed, and
246    /// the effects it asked for run.
247    ///
248    /// Effects run *after* the tree is built and laid out, so that what they do — start a
249    /// timer, read a controller, ask for another rebuild — never happens in the middle of
250    /// building.
251    pub fn end_frame(&self) {
252        if self.building.replace(false) {
253            self.sweep();
254        }
255        self.flush_effects();
256    }
257
258    /// Makes `value` available to every component below as a service: the way an
259    /// application-wide object — a router, a repository — is reached from a `build`.
260    pub fn provide<T: 'static>(&self, value: Rc<T>) {
261        self.services.borrow_mut().insert(TypeId::of::<T>(), value);
262    }
263
264    /// The service of type `T`, if one was provided.
265    pub fn service<T: 'static>(&self) -> Option<Rc<T>> {
266        self.services
267            .borrow()
268            .get(&TypeId::of::<T>())
269            .and_then(|s| s.clone().downcast::<T>().ok())
270    }
271
272    /// How many components currently hold state. For tests and diagnostics.
273    pub fn len(&self) -> usize {
274        self.entries.borrow().len()
275    }
276
277    /// Whether no component holds any state.
278    pub fn is_empty(&self) -> bool {
279        self.entries.borrow().is_empty()
280    }
281
282    fn sweep(&self) {
283        let epoch = self.epoch.get();
284        let stale: Vec<Entry> = {
285            let mut entries = self.entries.borrow_mut();
286            let keys: Vec<StateKey> = entries
287                .iter()
288                .filter(|(_, entry)| entry.epoch < epoch)
289                .map(|(key, _)| *key)
290                .collect();
291            keys.into_iter()
292                .filter_map(|key| entries.remove(&key))
293                .collect()
294        };
295        for entry in stale {
296            entry.dispose_all();
297        }
298    }
299
300    fn flush_effects(&self) {
301        let pending: Vec<PendingEffect> = std::mem::take(&mut *self.effects.borrow_mut());
302        for effect in pending {
303            if effect.hooks.disposed.get() {
304                continue;
305            }
306            let previous = effect.hooks.take_cleanup(effect.index);
307            if let Some(cleanup) = previous {
308                cleanup();
309            }
310            let cleanup = (effect.run)();
311            effect.hooks.set_cleanup(effect.index, cleanup);
312        }
313    }
314
315    /// The hooks of the component at `key`, made if it has none yet. A component of a
316    /// different kind that was there is disposed: the place is the same, the widget is not.
317    fn hooks_for(&self, key: StateKey, kind: TypeId) -> Rc<Hooks> {
318        let stale = {
319            let mut entries = self.entries.borrow_mut();
320            match entries.get_mut(&key) {
321                Some(entry) if entry.kind == kind => {
322                    entry.epoch = self.epoch.get();
323                    return entry.hooks.clone();
324                }
325                Some(_) => entries.remove(&key),
326                None => None,
327            }
328        };
329        if let Some(stale) = stale {
330            stale.dispose_all();
331        }
332        let hooks = Rc::new(Hooks::default());
333        self.entries.borrow_mut().insert(
334            key,
335            Entry {
336                kind,
337                epoch: self.epoch.get(),
338                state: None,
339                hooks: hooks.clone(),
340                dispose: None,
341            },
342        );
343        hooks
344    }
345
346    /// The state of the stateful widget at `key`: found if there is one, created if not.
347    /// The widget that was there before comes back with a state that already existed.
348    fn mount<W: StatefulWidget>(&self, key: StateKey, widget: Rc<W>) -> Mounted<W> {
349        let kind = TypeId::of::<W>();
350        let found = {
351            let mut entries = self.entries.borrow_mut();
352            entries
353                .get_mut(&key)
354                .filter(|entry| entry.kind == kind)
355                .map(|entry| {
356                    entry.epoch = self.epoch.get();
357                    (entry.state.clone(), entry.hooks.clone())
358                })
359        };
360        if let Some((Some(state), hooks)) = found {
361            let cell = state
362                .downcast::<StateCell<W::State>>()
363                .unwrap_or_else(|_| unreachable!("an entry's kind says what its state is"));
364            let previous = std::mem::replace(&mut *cell.widget.borrow_mut(), widget);
365            return Mounted {
366                cell,
367                hooks,
368                previous: Some(previous),
369            };
370        }
371        let stale = self.entries.borrow_mut().remove(&key);
372        if let Some(stale) = stale {
373            stale.dispose_all();
374        }
375        let cell = Rc::new(StateCell {
376            state: RefCell::new(widget.create_state()),
377            widget: RefCell::new(widget),
378            mounted: Cell::new(true),
379        });
380        let hooks = Rc::new(Hooks::default());
381        let leaving = cell.clone();
382        self.entries.borrow_mut().insert(
383            key,
384            Entry {
385                kind,
386                epoch: self.epoch.get(),
387                state: Some(cell.clone() as Rc<dyn Any>),
388                hooks: hooks.clone(),
389                dispose: Some(Box::new(move || {
390                    leaving.mounted.set(false);
391                    if let Ok(mut state) = leaving.state.try_borrow_mut() {
392                        state.dispose();
393                    }
394                })),
395            },
396        );
397        Mounted {
398            cell,
399            hooks,
400            previous: None,
401        }
402    }
403}
404
405struct Mounted<W: StatefulWidget> {
406    cell: Rc<StateCell<W::State>>,
407    hooks: Rc<Hooks>,
408    previous: Option<Rc<W>>,
409}
410
411// ---------------------------------------------------------------------------------------
412// Hooks
413// ---------------------------------------------------------------------------------------
414
415const ORDER: &str = "hooks were called in a different order than on the last build: call every \
416                     hook unconditionally, in the same order, never inside an `if`, a loop or a \
417                     closure";
418
419#[derive(Default)]
420struct Hooks {
421    slots: RefCell<Vec<Slot>>,
422    disposed: Cell<bool>,
423}
424
425enum Slot {
426    Value(Rc<dyn Any>),
427    Effect {
428        deps: Box<dyn Any>,
429        cleanup: Option<Cleanup>,
430    },
431}
432
433impl Hooks {
434    /// The value in slot `index`, made by `init` the first time.
435    fn value<T: 'static>(&self, index: usize, init: impl FnOnce() -> T) -> Rc<T> {
436        let existing = self.slots.borrow().get(index).map(|slot| match slot {
437            Slot::Value(value) => Some(value.clone()),
438            Slot::Effect { .. } => None,
439        });
440        match existing {
441            Some(Some(value)) => value.downcast::<T>().unwrap_or_else(|_| panic!("{ORDER}")),
442            Some(None) => panic!("{ORDER}"),
443            None => {
444                let value = Rc::new(init());
445                let mut slots = self.slots.borrow_mut();
446                assert_eq!(slots.len(), index, "{ORDER}");
447                slots.push(Slot::Value(value.clone() as Rc<dyn Any>));
448                value
449            }
450        }
451    }
452
453    fn take_cleanup(&self, index: usize) -> Option<Cleanup> {
454        match self.slots.borrow_mut().get_mut(index) {
455            Some(Slot::Effect { cleanup, .. }) => cleanup.take(),
456            _ => None,
457        }
458    }
459
460    fn set_cleanup(&self, index: usize, cleanup: Option<Cleanup>) {
461        if let Some(Slot::Effect { cleanup: slot, .. }) = self.slots.borrow_mut().get_mut(index) {
462            *slot = cleanup;
463        }
464    }
465
466    fn dispose(&self) {
467        self.disposed.set(true);
468        let cleanups: Vec<Cleanup> = self
469            .slots
470            .borrow_mut()
471            .iter_mut()
472            .filter_map(|slot| match slot {
473                Slot::Effect { cleanup, .. } => cleanup.take(),
474                Slot::Value(_) => None,
475            })
476            .collect();
477        for cleanup in cleanups {
478            cleanup();
479        }
480    }
481}
482
483/// What an effect may hand back: nothing, or the work that undoes it.
484pub trait IntoCleanup {
485    /// The cleanup, if there is one.
486    fn into_cleanup(self) -> Option<Box<dyn FnOnce()>>;
487}
488
489impl IntoCleanup for () {
490    fn into_cleanup(self) -> Option<Cleanup> {
491        None
492    }
493}
494
495impl<F: FnOnce() + 'static> IntoCleanup for F {
496    fn into_cleanup(self) -> Option<Cleanup> {
497        Some(Box::new(self))
498    }
499}
500
501/// A value kept between rebuilds by [`BuildContext::use_state`]. Changing it asks for a
502/// rebuild; reading it does not.
503///
504/// Cloning a `UseState` clones the *handle*: every clone reads and writes the same value,
505/// which is what lets a handler own one.
506pub struct UseState<T> {
507    cell: Rc<RefCell<T>>,
508}
509
510impl<T> Clone for UseState<T> {
511    fn clone(&self) -> Self {
512        Self {
513            cell: self.cell.clone(),
514        }
515    }
516}
517
518impl<T: 'static> UseState<T> {
519    /// A copy of the current value.
520    pub fn get(&self) -> T
521    where
522        T: Clone,
523    {
524        self.cell.borrow().clone()
525    }
526
527    /// Reads the current value without copying it.
528    pub fn with<R>(&self, read: impl FnOnce(&T) -> R) -> R {
529        read(&self.cell.borrow())
530    }
531
532    /// Replaces the value, and asks for a rebuild.
533    pub fn set(&self, value: T) {
534        *self.cell.borrow_mut() = value;
535        request_rebuild();
536    }
537
538    /// Changes the value in place, and asks for a rebuild.
539    pub fn update(&self, change: impl FnOnce(&mut T)) {
540        change(&mut self.cell.borrow_mut());
541        request_rebuild();
542    }
543}
544
545impl<T: std::fmt::Debug> std::fmt::Debug for UseState<T> {
546    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
547        f.debug_tuple("UseState")
548            .field(&*self.cell.borrow())
549            .finish()
550    }
551}
552
553/// A value kept between rebuilds by [`BuildContext::use_ref`]. Unlike [`UseState`],
554/// changing it does *not* ask for a rebuild: it is for what the interface does not show.
555pub struct UseRef<T> {
556    cell: Rc<RefCell<T>>,
557}
558
559impl<T> Clone for UseRef<T> {
560    fn clone(&self) -> Self {
561        Self {
562            cell: self.cell.clone(),
563        }
564    }
565}
566
567impl<T: 'static> UseRef<T> {
568    /// A copy of the current value.
569    pub fn get(&self) -> T
570    where
571        T: Clone,
572    {
573        self.cell.borrow().clone()
574    }
575
576    /// Reads the current value without copying it.
577    pub fn with<R>(&self, read: impl FnOnce(&T) -> R) -> R {
578        read(&self.cell.borrow())
579    }
580
581    /// Replaces the value.
582    pub fn set(&self, value: T) {
583        *self.cell.borrow_mut() = value;
584    }
585
586    /// Changes the value in place.
587    pub fn update(&self, change: impl FnOnce(&mut T)) {
588        change(&mut self.cell.borrow_mut());
589    }
590}
591
592// ---------------------------------------------------------------------------------------
593// BuildContext
594// ---------------------------------------------------------------------------------------
595
596/// What a component is told when it is built: where it is, under which theme, and where
597/// its hooks keep their values.
598pub struct BuildContext<'a> {
599    key: StateKey,
600    kind: TypeId,
601    runtime: &'a Runtime,
602    theme: &'a Theme,
603    hooks: OnceCell<Rc<Hooks>>,
604    cursor: Cell<usize>,
605    intervals: Cell<usize>,
606}
607
608impl<'a> BuildContext<'a> {
609    fn new(key: StateKey, kind: TypeId, runtime: &'a Runtime, theme: &'a Theme) -> Self {
610        Self {
611            key,
612            kind,
613            runtime,
614            theme,
615            hooks: OnceCell::new(),
616            cursor: Cell::new(0),
617            intervals: Cell::new(0),
618        }
619    }
620
621    /// The theme this component is built under.
622    pub fn theme(&self) -> &Theme {
623        self.theme
624    }
625
626    /// Where in the tree this component sits.
627    pub fn id(&self) -> WidgetId {
628        self.key.0
629    }
630
631    /// The runtime the tree is being built for.
632    pub fn runtime(&self) -> &Runtime {
633        self.runtime
634    }
635
636    /// An application-wide object provided with [`StateStore::provide`] — a router, say.
637    pub fn service<T: 'static>(&self) -> Option<Rc<T>> {
638        self.runtime.states.service::<T>()
639    }
640
641    fn hooks(&self) -> Rc<Hooks> {
642        self.hooks
643            .get_or_init(|| self.runtime.states.hooks_for(self.key, self.kind))
644            .clone()
645    }
646
647    fn next_slot(&self) -> usize {
648        let slot = self.cursor.get();
649        self.cursor.set(slot + 1);
650        slot
651    }
652
653    /// A value that lives as long as this component does, and that asks for a rebuild when
654    /// it changes. `init` makes it the first time and is not called again.
655    ///
656    /// ```
657    /// # use frus_widgets::{text, BuildContext, Widget};
658    /// fn label(cx: &BuildContext) -> Box<dyn Widget> {
659    ///     let taps = cx.use_state(|| 0);
660    ///     taps.set(taps.get() + 1);
661    ///     Box::new(text(format!("{}", taps.get())))
662    /// }
663    /// ```
664    pub fn use_state<T: 'static>(&self, init: impl FnOnce() -> T) -> UseState<T> {
665        let cell = self
666            .hooks()
667            .value(self.next_slot(), || RefCell::new(init()));
668        UseState { cell }
669    }
670
671    /// A value that lives as long as this component does and does *not* ask for a
672    /// rebuild when it changes.
673    pub fn use_ref<T: 'static>(&self, init: impl FnOnce() -> T) -> UseRef<T> {
674        let cell = self
675            .hooks()
676            .value(self.next_slot(), || RefCell::new(init()));
677        UseRef { cell }
678    }
679
680    /// `compute()`, kept until `deps` change. The work is not repeated on a rebuild that
681    /// changes nothing it depends on.
682    pub fn use_memo<D: PartialEq + 'static, T: 'static>(
683        &self,
684        deps: D,
685        compute: impl FnOnce() -> T,
686    ) -> Rc<T> {
687        let memo = self
688            .hooks()
689            .value(self.next_slot(), || RefCell::new(None::<(D, Rc<T>)>));
690        let mut memo = memo.borrow_mut();
691        match memo.as_ref() {
692            Some((previous, value)) if *previous == deps => value.clone(),
693            _ => {
694                let value = Rc::new(compute());
695                *memo = Some((deps, value.clone()));
696                value
697            }
698        }
699    }
700
701    /// Calls `on_tick` every `period`, for as long as this component asks for it.
702    ///
703    /// It asks again on every build: a build that does not call this any more stops the timer,
704    /// and a build that calls it with another `period` starts a new one. So, unlike the other
705    /// hooks, it may be called conditionally — a timer that is only wanted while something is on
706    /// is `if on { cx.use_interval(..) }`.
707    ///
708    /// `on_tick` runs on the interface's thread, like any other handler, so it may change
709    /// state: `cx.use_interval(period, cx.callback(|state| state.seconds += 1))`.
710    ///
711    /// ```
712    /// # use std::time::Duration;
713    /// # use frus_widgets::{text, BuildContext, Widget};
714    /// fn clock(cx: &BuildContext) -> Box<dyn Widget> {
715    ///     let seconds = cx.use_state(|| 0);
716    ///     let tick = seconds.clone();
717    ///     cx.use_interval(Duration::from_secs(1), move || tick.update(|s| *s += 1));
718    ///     Box::new(text(format!("{}s", seconds.get())))
719    /// }
720    /// ```
721    pub fn use_interval(&self, period: std::time::Duration, on_tick: impl Into<Callback>) {
722        let index = self.intervals.get();
723        self.intervals.set(index + 1);
724        let id = {
725            let mut hasher = std::collections::hash_map::DefaultHasher::new();
726            (self.key.0.as_u64(), self.key.1, index, period.as_millis()).hash(&mut hasher);
727            hasher.finish()
728        };
729        INTERVALS.with(|intervals| {
730            intervals.borrow_mut().push(Interval {
731                id,
732                period,
733                callback: on_tick.into(),
734            })
735        });
736    }
737
738    /// Says, for this build, whether something this component shows — a menu, a dialog, a
739    /// drawer — should take the back gesture and the back button before the router does.
740    /// While any component says `true`, the router is not asked to go back.
741    ///
742    /// Like [`use_interval`](Self::use_interval) it is asked again on every build and may be
743    /// called conditionally: `cx.block_back(self.menu_open)`.
744    pub fn block_back(&self, blocked: bool) {
745        if blocked {
746            BACK_BLOCKS.with(|blocks| blocks.set(blocks.get() + 1));
747        }
748    }
749
750    /// Work to do *after* the tree is built — start a timer, subscribe, read a controller —
751    /// run again whenever `deps` change, not otherwise. Pass `()` to run it once.
752    ///
753    /// The work may return a closure, which undoes it: it runs before the next run and when
754    /// the component leaves the tree.
755    ///
756    /// ```
757    /// # use frus_widgets::{text, BuildContext, Widget};
758    /// fn title(cx: &BuildContext) -> Box<dyn Widget> {
759    ///     cx.use_effect((), || {
760    ///         println!("mounted");
761    ///         || println!("unmounted")
762    ///     });
763    ///     Box::new(text("hello"))
764    /// }
765    /// ```
766    pub fn use_effect<D, C>(&self, deps: D, effect: impl FnOnce() -> C + 'static)
767    where
768        D: PartialEq + 'static,
769        C: IntoCleanup,
770    {
771        let hooks = self.hooks();
772        let index = self.next_slot();
773        let changed = {
774            let mut slots = hooks.slots.borrow_mut();
775            match slots.get_mut(index) {
776                None => {
777                    assert_eq!(slots.len(), index, "{ORDER}");
778                    slots.push(Slot::Effect {
779                        deps: Box::new(deps),
780                        cleanup: None,
781                    });
782                    true
783                }
784                Some(Slot::Effect { deps: old, .. }) => {
785                    if old.downcast_ref::<D>().is_some_and(|old| *old == deps) {
786                        false
787                    } else {
788                        *old = Box::new(deps);
789                        true
790                    }
791                }
792                Some(Slot::Value(_)) => panic!("{ORDER}"),
793            }
794        };
795        if changed {
796            self.runtime
797                .states
798                .effects
799                .borrow_mut()
800                .push(PendingEffect {
801                    hooks,
802                    index,
803                    run: Box::new(move || effect().into_cleanup()),
804                });
805        }
806    }
807}
808
809// ---------------------------------------------------------------------------------------
810// StatelessWidget
811// ---------------------------------------------------------------------------------------
812
813/// A widget with nothing to remember: it says what it looks like from what it is
814/// configured with, and from nothing else.
815///
816/// Any `Fn(&BuildContext) -> Box<dyn Widget>` is one, which is how a function becomes a
817/// component — and, with hooks, one that remembers.
818pub trait StatelessWidget: 'static {
819    /// The widget this one stands for. Called whenever the tree is rebuilt.
820    fn build(&self, cx: &BuildContext) -> Box<dyn Widget>;
821
822    /// This component as a widget: what goes in a `column![..]`, a `Scaffold`'s body, a
823    /// route's page.
824    fn into_widget(self) -> Component
825    where
826        Self: Sized,
827    {
828        Component::stateless(self)
829    }
830}
831
832impl<F> StatelessWidget for F
833where
834    F: Fn(&BuildContext) -> Box<dyn Widget> + 'static,
835{
836    fn build(&self, cx: &BuildContext) -> Box<dyn Widget> {
837        self(cx)
838    }
839}
840
841// ---------------------------------------------------------------------------------------
842// StatefulWidget
843// ---------------------------------------------------------------------------------------
844
845/// A widget whose [`State`] outlives the rebuilds. The widget is only the configuration:
846/// it is made again on every rebuild, and the state is what is kept.
847pub trait StatefulWidget: 'static {
848    /// What is kept.
849    type State: State<Widget = Self>;
850
851    /// Makes the state, once, when the widget first enters the tree.
852    fn create_state(&self) -> Self::State;
853
854    /// This component as a widget.
855    fn into_widget(self) -> Component
856    where
857        Self: Sized,
858    {
859        Component::stateful(self)
860    }
861}
862
863/// What a [`StatefulWidget`] keeps, and how it draws itself from it.
864pub trait State: Sized + 'static {
865    /// The widget this is the state of.
866    type Widget: StatefulWidget<State = Self>;
867
868    /// Called once, right after the state is made. Start what has to run for as long as
869    /// the widget is there — a listener, a timer — here, and stop it in
870    /// [`State::dispose`].
871    fn init_state(&mut self, _cx: &StateContext<Self>) {}
872
873    /// Called when the tree is rebuilt and the widget above changed, or was made again.
874    /// `old` is the configuration it replaces; the new one is [`StateContext::widget`].
875    fn did_update_widget(&mut self, _old: &Self::Widget, _cx: &StateContext<Self>) {}
876
877    /// Called once, when the widget leaves the tree. The state is never used again.
878    fn dispose(&mut self) {}
879
880    /// The widget this state stands for. Called whenever the tree is rebuilt.
881    ///
882    /// It only *reads* the state. What a handler changes goes through
883    /// [`StateContext::callback`] or [`StateContext::handle`]; changing the state from
884    /// inside `build` itself panics, because a build that changes what it builds from
885    /// never settles.
886    fn build(&self, cx: &StateContext<Self>) -> Box<dyn Widget>;
887}
888
889pub(crate) struct StateCell<S: State> {
890    state: RefCell<S>,
891    widget: RefCell<Rc<S::Widget>>,
892    mounted: Cell<bool>,
893}
894
895/// A way to reach a widget's state from outside its `build`: from a handler, a listener, a
896/// timer. Cheap to clone.
897pub struct StateHandle<S: State> {
898    cell: Rc<StateCell<S>>,
899}
900
901impl<S: State> Clone for StateHandle<S> {
902    fn clone(&self) -> Self {
903        Self {
904            cell: self.cell.clone(),
905        }
906    }
907}
908
909impl<S: State> StateHandle<S> {
910    /// Changes the state, and asks for a rebuild.
911    ///
912    /// Does nothing once the widget has left the tree: a timer that outlives its widget
913    /// finds nothing to change.
914    pub fn set_state(&self, change: impl FnOnce(&mut S)) {
915        if !self.cell.mounted.get() {
916            return;
917        }
918        match self.cell.state.try_borrow_mut() {
919            Ok(mut state) => change(&mut state),
920            Err(_) => panic!(
921                "set_state was called while the state was being read — from inside `build`, or \
922                 from inside another `set_state`. Change state from a handler, not while building"
923            ),
924        }
925        request_rebuild();
926    }
927
928    /// Reads the state.
929    pub fn read<R>(&self, read: impl FnOnce(&S) -> R) -> R {
930        read(&self.cell.state.borrow())
931    }
932
933    /// The widget's current configuration.
934    pub fn widget(&self) -> Rc<S::Widget> {
935        self.cell.widget.borrow().clone()
936    }
937
938    /// Whether the widget is still in the tree.
939    pub fn is_mounted(&self) -> bool {
940        self.cell.mounted.get()
941    }
942
943    /// A handler that changes the state, for anything that takes a [`Callback`].
944    pub fn callback(&self, change: impl Fn(&mut S) + 'static) -> Callback {
945        let handle = self.clone();
946        Callback::new(move || handle.set_state(|state| change(state)))
947    }
948
949    /// A handler for a widget that reports a **value** — a field's text, a slider's position, a
950    /// switch's new setting: `TextField::new(..).on_input(cx.handler(|s, text: String| s.name = text))`.
951    ///
952    /// It answers the value with a [`Callback`] that makes the change when it is delivered, which
953    /// is what every widget's setter takes and what leaves no doubt about the message type — a
954    /// closure that returns nothing could be a handler for any.
955    pub fn handler<T: Clone + 'static>(
956        &self,
957        change: impl Fn(&mut S, T) + 'static,
958    ) -> impl Fn(T) -> Callback + 'static {
959        let handle = self.clone();
960        let change = Rc::new(change);
961        move |value| {
962            let (handle, change) = (handle.clone(), change.clone());
963            Callback::new(move || handle.set_state(|state| change(state, value.clone())))
964        }
965    }
966}
967
968/// What a [`State`] is told when it builds and when its lifecycle runs: everything a
969/// [`BuildContext`] offers (this dereferences to one), and the state's own handle.
970pub struct StateContext<'c, 'a, S: State> {
971    cx: &'c BuildContext<'a>,
972    cell: &'c Rc<StateCell<S>>,
973}
974
975impl<'a, S: State> Deref for StateContext<'_, 'a, S> {
976    type Target = BuildContext<'a>;
977
978    fn deref(&self) -> &BuildContext<'a> {
979        self.cx
980    }
981}
982
983impl<S: State> StateContext<'_, '_, S> {
984    /// The widget's current configuration.
985    pub fn widget(&self) -> Rc<S::Widget> {
986        self.cell.widget.borrow().clone()
987    }
988
989    /// A handle to this state that a handler, a listener or a timer can own.
990    pub fn handle(&self) -> StateHandle<S> {
991        StateHandle {
992            cell: self.cell.clone(),
993        }
994    }
995
996    /// A handler that changes this state — [`StateHandle::callback`], from here.
997    pub fn callback(&self, change: impl Fn(&mut S) + 'static) -> Callback {
998        self.handle().callback(change)
999    }
1000
1001    /// A handler that changes this state from a value the widget reports —
1002    /// [`StateHandle::handler`], from here.
1003    pub fn handler<T: Clone + 'static>(
1004        &self,
1005        change: impl Fn(&mut S, T) + 'static,
1006    ) -> impl Fn(T) -> Callback + 'static {
1007        self.handle().handler(change)
1008    }
1009}
1010
1011// ---------------------------------------------------------------------------------------
1012// The node
1013// ---------------------------------------------------------------------------------------
1014
1015type Builder<Msg> = Box<dyn FnOnce(&BuildContext) -> Box<dyn Widget<Msg>>>;
1016
1017/// A component in the tree: a widget that is built into another when the tree is.
1018///
1019/// It is transparent: once built it *is* the widget it built, the same box, the same
1020/// children, the same behaviour, so wrapping something in a component changes nothing about
1021/// where it lands. Made by [`StatelessWidget::into_widget`] and
1022/// [`StatefulWidget::into_widget`].
1023pub struct Component<Msg = Callback> {
1024    inner: LazyChild<Msg>,
1025    key: Option<u64>,
1026}
1027
1028impl Component<Callback> {
1029    /// A stateless component.
1030    pub fn stateless<W: StatelessWidget>(widget: W) -> Self {
1031        Self::from_builder(
1032            TypeId::of::<W>(),
1033            Box::new(move |cx: &BuildContext| widget.build(cx)),
1034        )
1035    }
1036
1037    /// A stateful component.
1038    pub fn stateful<W: StatefulWidget>(widget: W) -> Self {
1039        Self::from_builder(
1040            TypeId::of::<W>(),
1041            Box::new(move |cx: &BuildContext| {
1042                let Mounted {
1043                    cell,
1044                    hooks,
1045                    previous,
1046                } = cx.runtime.states.mount(cx.key, Rc::new(widget));
1047                let _ = cx.hooks.set(hooks);
1048                let state_cx = StateContext { cx, cell: &cell };
1049                match previous {
1050                    None => cell.state.borrow_mut().init_state(&state_cx),
1051                    Some(old) => cell.state.borrow_mut().did_update_widget(&old, &state_cx),
1052                }
1053                let built = cell.state.borrow().build(&state_cx);
1054                built
1055            }),
1056        )
1057    }
1058}
1059
1060impl<Msg> Component<Msg> {
1061    fn from_builder(kind: TypeId, build: Builder<Msg>) -> Self {
1062        Self {
1063            inner: LazyChild {
1064                kind,
1065                build: RefCell::new(Some(build)),
1066                built: OnceCell::new(),
1067                unbuilt: Unbuilt,
1068            },
1069            key: None,
1070        }
1071    }
1072
1073    /// Gives this component an identity that does not depend on where it sits: its state
1074    /// follows the key when its siblings move.
1075    pub fn with_key(mut self, key: impl Hash) -> Self {
1076        let mut hasher = std::collections::hash_map::DefaultHasher::new();
1077        key.hash(&mut hasher);
1078        self.key = Some(hasher.finish());
1079        self
1080    }
1081
1082    /// A component is its child's box: nothing to add.
1083    fn restyle(&self, base: Style) -> Style {
1084        base
1085    }
1086}
1087
1088/// The child a component builds, made the first time the tree reaches it.
1089struct LazyChild<Msg> {
1090    kind: TypeId,
1091    build: RefCell<Option<Builder<Msg>>>,
1092    built: OnceCell<Box<dyn Widget<Msg>>>,
1093    unbuilt: Unbuilt,
1094}
1095
1096impl<Msg> LazyChild<Msg> {
1097    /// Builds the child if it has not been, then lets *it* expand: what it built may be a
1098    /// component too.
1099    fn expand(&self, id: WidgetId, runtime: &Runtime, theme: &Theme) {
1100        if self.built.get().is_none() {
1101            let build = self.build.borrow_mut().take();
1102            if let Some(build) = build {
1103                let depth = DEPTH.with(|d| d.get());
1104                let cx = BuildContext::new((id, depth), self.kind, runtime, theme);
1105                let child = build(&cx);
1106                let _ = self.built.set(child);
1107            }
1108        }
1109        if let Some(child) = self.built.get() {
1110            let _deeper = Deeper::enter();
1111            child.expand(id, runtime, theme);
1112        }
1113    }
1114
1115    fn build_in(&self, id: WidgetId, runtime: &Runtime, theme: &Theme) {
1116        if let Some(child) = self.built.get() {
1117            child.build_in(id, runtime, theme);
1118        }
1119    }
1120
1121    fn build_themed(&self, theme: &Theme) {
1122        if let Some(child) = self.built.get() {
1123            child.build_themed(theme);
1124        }
1125    }
1126
1127    fn children(&self) -> &[Box<dyn Widget<Msg>>] {
1128        match self.built.get() {
1129            Some(child) => child.children(),
1130            None => {
1131                #[cfg(debug_assertions)]
1132                if self.build.borrow().is_some() {
1133                    panic!(
1134                        "a component's children were read before it was built: a traversal \
1135                         reached this tree before the walk that builds components did. Call \
1136                         `build_deferred(tree, &theme, &runtime)` on it first"
1137                    );
1138                }
1139                &[]
1140            }
1141        }
1142    }
1143}
1144
1145impl<Msg> Deref for LazyChild<Msg> {
1146    type Target = dyn Widget<Msg>;
1147
1148    fn deref(&self) -> &(dyn Widget<Msg> + 'static) {
1149        match self.built.get() {
1150            Some(child) => child.as_ref(),
1151            None => &self.unbuilt,
1152        }
1153    }
1154}
1155
1156/// What a component answers as before it is built: nothing.
1157struct Unbuilt;
1158
1159impl<Msg> Widget<Msg> for Unbuilt {
1160    fn style(&self) -> Style {
1161        Style::default()
1162    }
1163
1164    fn children(&self) -> &[Box<dyn Widget<Msg>>] {
1165        &[]
1166    }
1167
1168    fn paint(&self, _bounds: Rect, _status: Status, _theme: &Theme, _scene: &mut Scene) {}
1169
1170    fn on_click(&self) -> Option<Msg> {
1171        None
1172    }
1173
1174    fn debug_name(&self) -> &'static str {
1175        "Component"
1176    }
1177}
1178
1179crate::transparent::forward_transparent!(Component {
1180    /// A component's key is its own and never its child's: the parent reads it to place
1181    /// the component *before* the component has been built, and an answer that changed
1182    /// when the child arrived would move the place under the state that lives there.
1183    fn key(&self) -> Option<u64> {
1184        self.key
1185    }
1186
1187    fn positioned(&self) -> Option<crate::positioned::Positioning> {
1188        self.inner.positioned()
1189    }
1190
1191    fn theme_override(
1192        &self,
1193        inherited: &crate::theme::Theme,
1194    ) -> Option<Box<crate::theme::Theme>> {
1195        self.inner.theme_override(inherited)
1196    }
1197
1198    fn media_override(&self, inherited: crate::MediaQuery) -> Option<crate::MediaQuery> {
1199        self.inner.media_override(inherited)
1200    }
1201
1202    fn scaffold_override(&self) -> Option<crate::ScaffoldInfo> {
1203        self.inner.scaffold_override()
1204    }
1205
1206    fn autofill_group(&self) -> bool {
1207        self.inner.autofill_group()
1208    }
1209});
1210
1211/// A component made of a function: `component(|cx| …)`. The closure is called whenever the
1212/// tree is rebuilt, and may call hooks.
1213///
1214/// ```
1215/// use frus_widgets::{component, text};
1216///
1217/// let clock = component(|cx| {
1218///     let ticks = cx.use_state(|| 0);
1219///     Box::new(text(format!("{} ticks", ticks.get())))
1220/// });
1221/// # let _ = clock;
1222/// ```
1223pub fn component(build: impl Fn(&BuildContext) -> Box<dyn Widget> + 'static) -> Component {
1224    Component::stateless(build)
1225}
1226
1227#[cfg(test)]
1228mod tests {
1229    use super::*;
1230    use crate::{build_deferred, keyed, Container, Flex};
1231    use frus_layout::Dimension;
1232
1233    type Builder = Rc<dyn Fn(&BuildContext) -> Box<dyn Widget>>;
1234    type Log = Rc<RefCell<Vec<String>>>;
1235    type Handle = Rc<RefCell<Option<StateHandle<LifeState>>>>;
1236
1237    fn log() -> Log {
1238        Rc::new(RefCell::new(Vec::new()))
1239    }
1240
1241    fn taken(log: &Log) -> Vec<String> {
1242        std::mem::take(&mut *log.borrow_mut())
1243    }
1244
1245    /// One frame of the shell's, less the drawing: the tree is built, and what it did not
1246    /// reach is let go.
1247    fn frame(runtime: &Runtime, root: &dyn Widget) {
1248        runtime.states.begin_build();
1249        build_deferred(root, &Theme::default(), runtime);
1250        runtime.states.end_frame();
1251    }
1252
1253    /// A stateful widget that says what happens to it.
1254    struct Life {
1255        tag: u32,
1256        log: Log,
1257        out: Handle,
1258    }
1259
1260    struct LifeState {
1261        log: Log,
1262        out: Handle,
1263        count: i32,
1264    }
1265
1266    impl StatefulWidget for Life {
1267        type State = LifeState;
1268        fn create_state(&self) -> LifeState {
1269            LifeState {
1270                log: self.log.clone(),
1271                out: self.out.clone(),
1272                count: 0,
1273            }
1274        }
1275    }
1276
1277    impl State for LifeState {
1278        type Widget = Life;
1279        fn init_state(&mut self, cx: &StateContext<Self>) {
1280            self.log
1281                .borrow_mut()
1282                .push(format!("init {}", cx.widget().tag));
1283            *self.out.borrow_mut() = Some(cx.handle());
1284        }
1285        fn did_update_widget(&mut self, old: &Life, cx: &StateContext<Self>) {
1286            self.log
1287                .borrow_mut()
1288                .push(format!("update {}->{}", old.tag, cx.widget().tag));
1289        }
1290        fn dispose(&mut self) {
1291            self.log.borrow_mut().push("dispose".to_string());
1292        }
1293        fn build(&self, _cx: &StateContext<Self>) -> Box<dyn Widget> {
1294            Box::new(Container::new().width(10.0 + self.count as f32).height(4.0))
1295        }
1296    }
1297
1298    fn life(tag: u32, log: &Log, out: &Handle) -> Component {
1299        Life {
1300            tag,
1301            log: log.clone(),
1302            out: out.clone(),
1303        }
1304        .into_widget()
1305    }
1306
1307    fn handle() -> Handle {
1308        Rc::new(RefCell::new(None))
1309    }
1310
1311    fn width(widget: &dyn Widget) -> Dimension {
1312        widget.style().width
1313    }
1314
1315    #[test]
1316    fn a_stateless_component_is_the_widget_it_builds() {
1317        let runtime = Runtime::default();
1318        let root = component(|_| Box::new(Container::new().width(120.0).height(30.0)));
1319        frame(&runtime, &root);
1320        assert_eq!(width(&root), Dimension::Length(120.0));
1321        assert_eq!(root.style().height, Dimension::Length(30.0));
1322    }
1323
1324    #[test]
1325    fn a_state_is_made_once_and_survives_rebuilds() {
1326        let (runtime, log, out) = (Runtime::default(), log(), handle());
1327        let first = life(1, &log, &out);
1328        frame(&runtime, &first);
1329        assert_eq!(taken(&log), ["init 1"]);
1330        assert_eq!(width(&first), Dimension::Length(10.0));
1331
1332        // the state is changed from outside, as a handler does
1333        out.borrow().as_ref().unwrap().set_state(|s| s.count = 5);
1334        assert!(take_rebuild_request(), "set_state asks for a rebuild");
1335
1336        let second = life(2, &log, &out);
1337        frame(&runtime, &second);
1338        assert_eq!(
1339            taken(&log),
1340            ["update 1->2"],
1341            "the same place, the same kind: the state is kept and told what changed"
1342        );
1343        assert_eq!(
1344            width(&second),
1345            Dimension::Length(15.0),
1346            "and it is the kept one"
1347        );
1348    }
1349
1350    #[test]
1351    fn a_handler_carries_the_value_a_widget_reports_into_the_state() {
1352        let (runtime, log, out) = (Runtime::default(), log(), handle());
1353        frame(&runtime, &life(1, &log, &out));
1354        let handle = out.borrow().clone().unwrap();
1355        let on_value = handle.handler(|s, by: i32| s.count += by);
1356        let (three, four) = (on_value(3), on_value(4));
1357        assert_eq!(
1358            handle.read(|s| s.count),
1359            0,
1360            "nothing changes until it is delivered"
1361        );
1362        three.call();
1363        four.call();
1364        four.call();
1365        assert_eq!(
1366            handle.read(|s| s.count),
1367            11,
1368            "each delivery makes the change"
1369        );
1370        assert!(take_rebuild_request(), "and it asks for the rebuild");
1371    }
1372
1373    #[test]
1374    fn a_component_that_blocks_back_does_so_for_one_build_only() {
1375        let runtime = Runtime::default();
1376        let open = Rc::new(Cell::new(true));
1377        let asked = open.clone();
1378        let root = component(move |cx| {
1379            cx.block_back(asked.get());
1380            Box::new(Container::new().width(4.0).height(4.0))
1381        });
1382        frame(&runtime, &root);
1383        assert!(back_blocked(), "an open menu is in the way");
1384        open.set(false);
1385        frame(&runtime, &root);
1386        assert!(!back_blocked(), "each build says it afresh");
1387    }
1388
1389    #[test]
1390    fn a_state_is_disposed_when_a_rebuild_no_longer_reaches_it() {
1391        let (runtime, log, out) = (Runtime::default(), log(), handle());
1392        frame(&runtime, &Flex::column().child(life(1, &log, &out)));
1393        taken(&log);
1394        assert_eq!(runtime.states.len(), 1);
1395
1396        frame(&runtime, &Flex::column().child(Container::new()));
1397        assert_eq!(taken(&log), ["dispose"]);
1398        assert!(runtime.states.is_empty());
1399        let handle = out.borrow().clone().unwrap();
1400        assert!(!handle.is_mounted());
1401        take_rebuild_request();
1402        handle.set_state(|s| s.count = 9);
1403        assert!(
1404            !take_rebuild_request(),
1405            "a state that has left cannot ask for a rebuild"
1406        );
1407    }
1408
1409    #[test]
1410    fn a_different_kind_of_widget_in_the_same_place_starts_over() {
1411        let (runtime, log, out) = (Runtime::default(), log(), handle());
1412        frame(&runtime, &Flex::column().child(life(1, &log, &out)));
1413        taken(&log);
1414        // a stateless component that keeps hooks takes the place the state had
1415        let other = component(|cx| {
1416            cx.use_state(|| 0u8);
1417            Box::new(Container::new())
1418        });
1419        frame(&runtime, &Flex::column().child(other));
1420        assert_eq!(
1421            taken(&log),
1422            ["dispose"],
1423            "the old kind is disposed, not reused"
1424        );
1425    }
1426
1427    #[test]
1428    fn a_key_carries_the_state_wherever_the_siblings_go() {
1429        let (runtime, log) = (Runtime::default(), log());
1430        let (a, b) = (handle(), handle());
1431        let column = |order: [u32; 2]| {
1432            let mut column = Flex::column();
1433            for tag in order {
1434                let out = if tag == 1 { &a } else { &b };
1435                column = column.child(keyed(tag, life(tag, &log, out)));
1436            }
1437            column
1438        };
1439        frame(&runtime, &column([1, 2]));
1440        a.borrow().as_ref().unwrap().set_state(|s| s.count = 1);
1441        b.borrow().as_ref().unwrap().set_state(|s| s.count = 2);
1442        taken(&log);
1443
1444        frame(&runtime, &column([2, 1]));
1445        let counts = |h: &Handle| h.borrow().as_ref().unwrap().read(|s| s.count);
1446        assert_eq!(
1447            (counts(&a), counts(&b)),
1448            (1, 2),
1449            "each state stayed with its key"
1450        );
1451        assert_eq!(runtime.states.len(), 2);
1452        assert!(
1453            !taken(&log)
1454                .iter()
1455                .any(|line| line == "dispose" || line.starts_with("init")),
1456            "and neither was made again or let go"
1457        );
1458    }
1459
1460    #[test]
1461    fn a_component_that_builds_a_component_keeps_both_states() {
1462        let (runtime, log, out) = (Runtime::default(), log(), handle());
1463        let (inner_log, inner_out) = (log.clone(), out.clone());
1464        let outer = component(move |cx| {
1465            cx.use_state(|| 0);
1466            // built *directly*: the same place in the tree as this component
1467            Box::new(life(7, &inner_log, &inner_out))
1468        });
1469        frame(&runtime, &outer);
1470        assert_eq!(
1471            runtime.states.len(),
1472            2,
1473            "one for the hooks, one for the state"
1474        );
1475        assert_eq!(taken(&log), ["init 7"]);
1476    }
1477
1478    #[test]
1479    fn hooks_keep_their_values_between_builds() {
1480        let runtime = Runtime::default();
1481        let seen = Rc::new(RefCell::new(Vec::new()));
1482        // One closure, built again by every frame: the same kind of component each time.
1483        let build = {
1484            let seen = seen.clone();
1485            Rc::new(move |cx: &BuildContext| -> Box<dyn Widget> {
1486                let taps = cx.use_state(|| 10);
1487                let scratch = cx.use_ref(|| 0);
1488                scratch.update(|n| *n += 1);
1489                taps.update(|n| *n += 1);
1490                seen.borrow_mut().push((taps.get(), scratch.get()));
1491                Box::new(Container::new())
1492            })
1493        };
1494        let make = |build: &Builder| {
1495            let build = build.clone();
1496            Component::stateless(move |cx: &BuildContext| build(cx))
1497        };
1498        let build: Builder = build;
1499        frame(&runtime, &make(&build));
1500        assert!(
1501            take_rebuild_request(),
1502            "changing a state hook asks for a rebuild"
1503        );
1504        frame(&runtime, &make(&build));
1505        frame(&runtime, &make(&build));
1506        assert_eq!(*seen.borrow(), [(11, 1), (12, 2), (13, 3)]);
1507    }
1508
1509    #[test]
1510    fn use_memo_recomputes_only_when_its_dependencies_change() {
1511        let runtime = Runtime::default();
1512        let computed = Rc::new(Cell::new(0));
1513        let dep = Rc::new(Cell::new(1));
1514        let build: Builder = {
1515            let (computed, dep) = (computed.clone(), dep.clone());
1516            Rc::new(move |cx| {
1517                let d = dep.get();
1518                let value = cx.use_memo(d, || {
1519                    computed.set(computed.get() + 1);
1520                    d * 100
1521                });
1522                Box::new(Container::new().width(*value as f32))
1523            })
1524        };
1525        let make = || {
1526            let build = build.clone();
1527            Component::stateless(move |cx: &BuildContext| build(cx))
1528        };
1529        let root = make();
1530        frame(&runtime, &root);
1531        assert_eq!(
1532            (computed.get(), width(&root)),
1533            (1, Dimension::Length(100.0))
1534        );
1535        let root = make();
1536        frame(&runtime, &root);
1537        assert_eq!(computed.get(), 1, "the same dependency: not computed again");
1538        dep.set(2);
1539        let root = make();
1540        frame(&runtime, &root);
1541        assert_eq!(
1542            (computed.get(), width(&root)),
1543            (2, Dimension::Length(200.0))
1544        );
1545    }
1546
1547    #[test]
1548    fn effects_run_after_the_build_and_clean_up_before_the_next_and_at_the_end() {
1549        let (runtime, log) = (Runtime::default(), log());
1550        let dep = Rc::new(Cell::new(1));
1551        let build: Builder = {
1552            let (log, dep) = (log.clone(), dep.clone());
1553            Rc::new(move |cx| {
1554                let d = dep.get();
1555                let log = log.clone();
1556                log.borrow_mut().push(format!("build {d}"));
1557                cx.use_effect(d, move || {
1558                    log.borrow_mut().push(format!("effect {d}"));
1559                    move || log.borrow_mut().push(format!("cleanup {d}"))
1560                });
1561                Box::new(Container::new())
1562            })
1563        };
1564        let make = || {
1565            let build = build.clone();
1566            Component::stateless(move |cx: &BuildContext| build(cx))
1567        };
1568        frame(&runtime, &make());
1569        assert_eq!(
1570            taken(&log),
1571            ["build 1", "effect 1"],
1572            "after the build, not during it"
1573        );
1574
1575        frame(&runtime, &make());
1576        assert_eq!(
1577            taken(&log),
1578            ["build 1"],
1579            "the same dependency: nothing runs"
1580        );
1581
1582        dep.set(2);
1583        frame(&runtime, &make());
1584        assert_eq!(
1585            taken(&log),
1586            ["build 2", "cleanup 1", "effect 2"],
1587            "the old one is undone before the new one starts"
1588        );
1589
1590        // leaving the tree runs the last cleanup
1591        frame(&runtime, &Flex::column().child(Container::new()));
1592        assert_eq!(taken(&log), ["cleanup 2"]);
1593    }
1594
1595    #[test]
1596    #[should_panic(expected = "different order")]
1597    fn hooks_called_in_a_different_order_are_caught() {
1598        let runtime = Runtime::default();
1599        let first = Rc::new(Cell::new(true));
1600        let build: Builder = {
1601            let first = first.clone();
1602            Rc::new(move |cx| {
1603                if first.get() {
1604                    cx.use_state(|| 1i32);
1605                }
1606                cx.use_ref(String::new);
1607                Box::new(Container::new())
1608            })
1609        };
1610        let make = || {
1611            let build = build.clone();
1612            Component::stateless(move |cx: &BuildContext| build(cx))
1613        };
1614        frame(&runtime, &make());
1615        first.set(false);
1616        frame(&runtime, &make());
1617    }
1618
1619    #[test]
1620    #[should_panic(expected = "set_state was called while")]
1621    fn changing_state_from_inside_build_is_refused() {
1622        struct Greedy;
1623        struct GreedyState;
1624        impl StatefulWidget for Greedy {
1625            type State = GreedyState;
1626            fn create_state(&self) -> GreedyState {
1627                GreedyState
1628            }
1629        }
1630        impl State for GreedyState {
1631            type Widget = Greedy;
1632            fn build(&self, cx: &StateContext<Self>) -> Box<dyn Widget> {
1633                cx.handle().set_state(|_| {});
1634                Box::new(Container::new())
1635            }
1636        }
1637        frame(&Runtime::default(), &Greedy.into_widget());
1638    }
1639
1640    #[test]
1641    fn an_interval_is_asked_for_by_each_build_that_wants_it() {
1642        let runtime = Runtime::default();
1643        let on = Rc::new(Cell::new(true));
1644        let build: Builder = {
1645            let on = on.clone();
1646            Rc::new(move |cx| {
1647                if on.get() {
1648                    cx.use_interval(std::time::Duration::from_secs(1), || {});
1649                    cx.use_interval(std::time::Duration::from_secs(5), || {});
1650                }
1651                Box::new(Container::new())
1652            })
1653        };
1654        let make = || {
1655            let build = build.clone();
1656            Component::stateless(move |cx: &BuildContext| build(cx))
1657        };
1658        frame(&runtime, &make());
1659        let first = intervals();
1660        assert_eq!(first.len(), 2);
1661        assert_ne!(first[0].id, first[1].id, "two timers are told apart");
1662
1663        frame(&runtime, &make());
1664        let second = intervals();
1665        assert_eq!(
1666            first.iter().map(|i| i.id).collect::<Vec<_>>(),
1667            second.iter().map(|i| i.id).collect::<Vec<_>>(),
1668            "and each is the same timer from one build to the next"
1669        );
1670
1671        on.set(false);
1672        frame(&runtime, &make());
1673        assert!(
1674            intervals().is_empty(),
1675            "a build that stops asking stops the timer"
1676        );
1677    }
1678
1679    #[test]
1680    fn a_service_provided_to_the_runtime_is_reachable_from_a_build() {
1681        let runtime = Runtime::default();
1682        runtime.states.provide(Rc::new(41u32));
1683        let root = component(|cx| {
1684            let n = cx.service::<u32>().expect("provided");
1685            Box::new(Container::new().width(*n as f32 + 1.0))
1686        });
1687        frame(&runtime, &root);
1688        assert_eq!(width(&root), Dimension::Length(42.0));
1689        assert!(runtime.states.service::<String>().is_none());
1690    }
1691}