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    /// A context for a test that builds something by hand, under `theme`.
622    #[cfg(test)]
623    pub(crate) fn for_test(runtime: &'a Runtime, theme: &'a Theme) -> Self {
624        Self::new((WidgetId::ROOT, 0), TypeId::of::<()>(), runtime, theme)
625    }
626
627    /// The theme this component is built under.
628    pub fn theme(&self) -> &Theme {
629        self.theme
630    }
631
632    /// Where in the tree this component sits.
633    pub fn id(&self) -> WidgetId {
634        self.key.0
635    }
636
637    /// The runtime the tree is being built for.
638    pub fn runtime(&self) -> &Runtime {
639        self.runtime
640    }
641
642    /// An application-wide object provided with [`StateStore::provide`] — a router, say.
643    pub fn service<T: 'static>(&self) -> Option<Rc<T>> {
644        self.runtime.states.service::<T>()
645    }
646
647    fn hooks(&self) -> Rc<Hooks> {
648        self.hooks
649            .get_or_init(|| self.runtime.states.hooks_for(self.key, self.kind))
650            .clone()
651    }
652
653    fn next_slot(&self) -> usize {
654        let slot = self.cursor.get();
655        self.cursor.set(slot + 1);
656        slot
657    }
658
659    /// A value that lives as long as this component does, and that asks for a rebuild when
660    /// it changes. `init` makes it the first time and is not called again.
661    ///
662    /// ```
663    /// # use frus_widgets::{text, BuildContext, Widget};
664    /// fn label(cx: &BuildContext) -> Box<dyn Widget> {
665    ///     let taps = cx.use_state(|| 0);
666    ///     taps.set(taps.get() + 1);
667    ///     Box::new(text(format!("{}", taps.get())))
668    /// }
669    /// ```
670    pub fn use_state<T: 'static>(&self, init: impl FnOnce() -> T) -> UseState<T> {
671        let cell = self
672            .hooks()
673            .value(self.next_slot(), || RefCell::new(init()));
674        UseState { cell }
675    }
676
677    /// A value that lives as long as this component does and does *not* ask for a
678    /// rebuild when it changes.
679    pub fn use_ref<T: 'static>(&self, init: impl FnOnce() -> T) -> UseRef<T> {
680        let cell = self
681            .hooks()
682            .value(self.next_slot(), || RefCell::new(init()));
683        UseRef { cell }
684    }
685
686    /// `compute()`, kept until `deps` change. The work is not repeated on a rebuild that
687    /// changes nothing it depends on.
688    pub fn use_memo<D: PartialEq + 'static, T: 'static>(
689        &self,
690        deps: D,
691        compute: impl FnOnce() -> T,
692    ) -> Rc<T> {
693        let memo = self
694            .hooks()
695            .value(self.next_slot(), || RefCell::new(None::<(D, Rc<T>)>));
696        let mut memo = memo.borrow_mut();
697        match memo.as_ref() {
698            Some((previous, value)) if *previous == deps => value.clone(),
699            _ => {
700                let value = Rc::new(compute());
701                *memo = Some((deps, value.clone()));
702                value
703            }
704        }
705    }
706
707    /// Calls `on_tick` every `period`, for as long as this component asks for it.
708    ///
709    /// It asks again on every build: a build that does not call this any more stops the timer,
710    /// and a build that calls it with another `period` starts a new one. So, unlike the other
711    /// hooks, it may be called conditionally — a timer that is only wanted while something is on
712    /// is `if on { cx.use_interval(..) }`.
713    ///
714    /// `on_tick` runs on the interface's thread, like any other handler, so it may change
715    /// state: `cx.use_interval(period, cx.callback(|state| state.seconds += 1))`.
716    ///
717    /// ```
718    /// # use std::time::Duration;
719    /// # use frus_widgets::{text, BuildContext, Widget};
720    /// fn clock(cx: &BuildContext) -> Box<dyn Widget> {
721    ///     let seconds = cx.use_state(|| 0);
722    ///     let tick = seconds.clone();
723    ///     cx.use_interval(Duration::from_secs(1), move || tick.update(|s| *s += 1));
724    ///     Box::new(text(format!("{}s", seconds.get())))
725    /// }
726    /// ```
727    pub fn use_interval(&self, period: std::time::Duration, on_tick: impl Into<Callback>) {
728        let index = self.intervals.get();
729        self.intervals.set(index + 1);
730        let id = {
731            let mut hasher = std::collections::hash_map::DefaultHasher::new();
732            (self.key.0.as_u64(), self.key.1, index, period.as_millis()).hash(&mut hasher);
733            hasher.finish()
734        };
735        INTERVALS.with(|intervals| {
736            intervals.borrow_mut().push(Interval {
737                id,
738                period,
739                callback: on_tick.into(),
740            })
741        });
742    }
743
744    /// Says, for this build, whether something this component shows — a menu, a dialog, a
745    /// drawer — should take the back gesture and the back button before the router does.
746    /// While any component says `true`, the router is not asked to go back.
747    ///
748    /// Like [`use_interval`](Self::use_interval) it is asked again on every build and may be
749    /// called conditionally: `cx.block_back(self.menu_open)`.
750    pub fn block_back(&self, blocked: bool) {
751        if blocked {
752            BACK_BLOCKS.with(|blocks| blocks.set(blocks.get() + 1));
753        }
754    }
755
756    /// Work to do *after* the tree is built — start a timer, subscribe, read a controller —
757    /// run again whenever `deps` change, not otherwise. Pass `()` to run it once.
758    ///
759    /// The work may return a closure, which undoes it: it runs before the next run and when
760    /// the component leaves the tree.
761    ///
762    /// ```
763    /// # use frus_widgets::{text, BuildContext, Widget};
764    /// fn title(cx: &BuildContext) -> Box<dyn Widget> {
765    ///     cx.use_effect((), || {
766    ///         println!("mounted");
767    ///         || println!("unmounted")
768    ///     });
769    ///     Box::new(text("hello"))
770    /// }
771    /// ```
772    pub fn use_effect<D, C>(&self, deps: D, effect: impl FnOnce() -> C + 'static)
773    where
774        D: PartialEq + 'static,
775        C: IntoCleanup,
776    {
777        let hooks = self.hooks();
778        let index = self.next_slot();
779        let changed = {
780            let mut slots = hooks.slots.borrow_mut();
781            match slots.get_mut(index) {
782                None => {
783                    assert_eq!(slots.len(), index, "{ORDER}");
784                    slots.push(Slot::Effect {
785                        deps: Box::new(deps),
786                        cleanup: None,
787                    });
788                    true
789                }
790                Some(Slot::Effect { deps: old, .. }) => {
791                    if old.downcast_ref::<D>().is_some_and(|old| *old == deps) {
792                        false
793                    } else {
794                        *old = Box::new(deps);
795                        true
796                    }
797                }
798                Some(Slot::Value(_)) => panic!("{ORDER}"),
799            }
800        };
801        if changed {
802            self.runtime
803                .states
804                .effects
805                .borrow_mut()
806                .push(PendingEffect {
807                    hooks,
808                    index,
809                    run: Box::new(move || effect().into_cleanup()),
810                });
811        }
812    }
813}
814
815// ---------------------------------------------------------------------------------------
816// StatelessWidget
817// ---------------------------------------------------------------------------------------
818
819/// A widget with nothing to remember: it says what it looks like from what it is
820/// configured with, and from nothing else.
821///
822/// Any `Fn(&BuildContext) -> Box<dyn Widget>` is one, which is how a function becomes a
823/// component — and, with hooks, one that remembers.
824pub trait StatelessWidget: 'static {
825    /// The widget this one stands for. Called whenever the tree is rebuilt.
826    fn build(&self, cx: &BuildContext) -> Box<dyn Widget>;
827
828    /// This component as a widget: what goes in a `column![..]`, a `Scaffold`'s body, a
829    /// route's page.
830    fn into_widget(self) -> Component
831    where
832        Self: Sized,
833    {
834        Component::stateless(self)
835    }
836}
837
838impl<F> StatelessWidget for F
839where
840    F: Fn(&BuildContext) -> Box<dyn Widget> + 'static,
841{
842    fn build(&self, cx: &BuildContext) -> Box<dyn Widget> {
843        self(cx)
844    }
845}
846
847// ---------------------------------------------------------------------------------------
848// StatefulWidget
849// ---------------------------------------------------------------------------------------
850
851/// A widget whose [`State`] outlives the rebuilds. The widget is only the configuration:
852/// it is made again on every rebuild, and the state is what is kept.
853pub trait StatefulWidget: 'static {
854    /// What is kept.
855    type State: State<Widget = Self>;
856
857    /// Makes the state, once, when the widget first enters the tree.
858    fn create_state(&self) -> Self::State;
859
860    /// This component as a widget.
861    fn into_widget(self) -> Component
862    where
863        Self: Sized,
864    {
865        Component::stateful(self)
866    }
867}
868
869/// What a [`StatefulWidget`] keeps, and how it draws itself from it.
870pub trait State: Sized + 'static {
871    /// The widget this is the state of.
872    type Widget: StatefulWidget<State = Self>;
873
874    /// Called once, right after the state is made. Start what has to run for as long as
875    /// the widget is there — a listener, a timer — here, and stop it in
876    /// [`State::dispose`].
877    fn init_state(&mut self, _cx: &StateContext<Self>) {}
878
879    /// Called when the tree is rebuilt and the widget above changed, or was made again.
880    /// `old` is the configuration it replaces; the new one is [`StateContext::widget`].
881    fn did_update_widget(&mut self, _old: &Self::Widget, _cx: &StateContext<Self>) {}
882
883    /// Called once, when the widget leaves the tree. The state is never used again.
884    fn dispose(&mut self) {}
885
886    /// The widget this state stands for. Called whenever the tree is rebuilt.
887    ///
888    /// It only *reads* the state. What a handler changes goes through
889    /// [`StateContext::callback`] or [`StateContext::handle`]; changing the state from
890    /// inside `build` itself panics, because a build that changes what it builds from
891    /// never settles.
892    fn build(&self, cx: &StateContext<Self>) -> Box<dyn Widget>;
893}
894
895pub(crate) struct StateCell<S: State> {
896    state: RefCell<S>,
897    widget: RefCell<Rc<S::Widget>>,
898    mounted: Cell<bool>,
899}
900
901/// A way to reach a widget's state from outside its `build`: from a handler, a listener, a
902/// timer. Cheap to clone.
903pub struct StateHandle<S: State> {
904    cell: Rc<StateCell<S>>,
905}
906
907impl<S: State> Clone for StateHandle<S> {
908    fn clone(&self) -> Self {
909        Self {
910            cell: self.cell.clone(),
911        }
912    }
913}
914
915impl<S: State> StateHandle<S> {
916    /// Changes the state, and asks for a rebuild.
917    ///
918    /// Does nothing once the widget has left the tree: a timer that outlives its widget
919    /// finds nothing to change.
920    pub fn set_state(&self, change: impl FnOnce(&mut S)) {
921        if !self.cell.mounted.get() {
922            return;
923        }
924        match self.cell.state.try_borrow_mut() {
925            Ok(mut state) => change(&mut state),
926            Err(_) => panic!(
927                "set_state was called while the state was being read — from inside `build`, or \
928                 from inside another `set_state`. Change state from a handler, not while building"
929            ),
930        }
931        request_rebuild();
932    }
933
934    /// Reads the state.
935    pub fn read<R>(&self, read: impl FnOnce(&S) -> R) -> R {
936        read(&self.cell.state.borrow())
937    }
938
939    /// The widget's current configuration.
940    pub fn widget(&self) -> Rc<S::Widget> {
941        self.cell.widget.borrow().clone()
942    }
943
944    /// Whether the widget is still in the tree.
945    pub fn is_mounted(&self) -> bool {
946        self.cell.mounted.get()
947    }
948
949    /// A handler that changes the state, for anything that takes a [`Callback`].
950    pub fn callback(&self, change: impl Fn(&mut S) + 'static) -> Callback {
951        let handle = self.clone();
952        Callback::new(move || handle.set_state(|state| change(state)))
953    }
954
955    /// A handler for a widget that reports a **value** — a field's text, a slider's position, a
956    /// switch's new setting: `TextField::new(..).on_input(cx.handler(|s, text: String| s.name = text))`.
957    ///
958    /// It answers the value with a [`Callback`] that makes the change when it is delivered, which
959    /// is what every widget's setter takes and what leaves no doubt about the message type — a
960    /// closure that returns nothing could be a handler for any.
961    pub fn handler<T: Clone + 'static>(
962        &self,
963        change: impl Fn(&mut S, T) + 'static,
964    ) -> impl Fn(T) -> Callback + 'static {
965        let handle = self.clone();
966        let change = Rc::new(change);
967        move |value| {
968            let (handle, change) = (handle.clone(), change.clone());
969            Callback::new(move || handle.set_state(|state| change(state, value.clone())))
970        }
971    }
972}
973
974/// What a [`State`] is told when it builds and when its lifecycle runs: everything a
975/// [`BuildContext`] offers (this dereferences to one), and the state's own handle.
976pub struct StateContext<'c, 'a, S: State> {
977    cx: &'c BuildContext<'a>,
978    cell: &'c Rc<StateCell<S>>,
979}
980
981impl<'a, S: State> Deref for StateContext<'_, 'a, S> {
982    type Target = BuildContext<'a>;
983
984    fn deref(&self) -> &BuildContext<'a> {
985        self.cx
986    }
987}
988
989impl<S: State> StateContext<'_, '_, S> {
990    /// The widget's current configuration.
991    pub fn widget(&self) -> Rc<S::Widget> {
992        self.cell.widget.borrow().clone()
993    }
994
995    /// A handle to this state that a handler, a listener or a timer can own.
996    pub fn handle(&self) -> StateHandle<S> {
997        StateHandle {
998            cell: self.cell.clone(),
999        }
1000    }
1001
1002    /// A handler that changes this state — [`StateHandle::callback`], from here.
1003    pub fn callback(&self, change: impl Fn(&mut S) + 'static) -> Callback {
1004        self.handle().callback(change)
1005    }
1006
1007    /// A handler that changes this state from a value the widget reports —
1008    /// [`StateHandle::handler`], from here.
1009    pub fn handler<T: Clone + 'static>(
1010        &self,
1011        change: impl Fn(&mut S, T) + 'static,
1012    ) -> impl Fn(T) -> Callback + 'static {
1013        self.handle().handler(change)
1014    }
1015}
1016
1017// ---------------------------------------------------------------------------------------
1018// The node
1019// ---------------------------------------------------------------------------------------
1020
1021type Builder<Msg> = Box<dyn FnOnce(&BuildContext) -> Box<dyn Widget<Msg>>>;
1022
1023/// A component in the tree: a widget that is built into another when the tree is.
1024///
1025/// It is transparent: once built it *is* the widget it built, the same box, the same
1026/// children, the same behaviour, so wrapping something in a component changes nothing about
1027/// where it lands. Made by [`StatelessWidget::into_widget`] and
1028/// [`StatefulWidget::into_widget`].
1029pub struct Component<Msg = Callback> {
1030    inner: LazyChild<Msg>,
1031    key: Option<u64>,
1032}
1033
1034impl Component<Callback> {
1035    /// A stateless component.
1036    pub fn stateless<W: StatelessWidget>(widget: W) -> Self {
1037        Self::from_builder(
1038            TypeId::of::<W>(),
1039            Box::new(move |cx: &BuildContext| widget.build(cx)),
1040        )
1041    }
1042
1043    /// A stateful component.
1044    pub fn stateful<W: StatefulWidget>(widget: W) -> Self {
1045        Self::from_builder(
1046            TypeId::of::<W>(),
1047            Box::new(move |cx: &BuildContext| {
1048                let Mounted {
1049                    cell,
1050                    hooks,
1051                    previous,
1052                } = cx.runtime.states.mount(cx.key, Rc::new(widget));
1053                let _ = cx.hooks.set(hooks);
1054                let state_cx = StateContext { cx, cell: &cell };
1055                match previous {
1056                    None => cell.state.borrow_mut().init_state(&state_cx),
1057                    Some(old) => cell.state.borrow_mut().did_update_widget(&old, &state_cx),
1058                }
1059                let built = cell.state.borrow().build(&state_cx);
1060                built
1061            }),
1062        )
1063    }
1064}
1065
1066impl<Msg> Component<Msg> {
1067    fn from_builder(kind: TypeId, build: Builder<Msg>) -> Self {
1068        Self {
1069            inner: LazyChild {
1070                kind,
1071                build: RefCell::new(Some(build)),
1072                built: OnceCell::new(),
1073                unbuilt: Unbuilt,
1074            },
1075            key: None,
1076        }
1077    }
1078
1079    /// Gives this component an identity that does not depend on where it sits: its state
1080    /// follows the key when its siblings move.
1081    pub fn with_key(mut self, key: impl Hash) -> Self {
1082        let mut hasher = std::collections::hash_map::DefaultHasher::new();
1083        key.hash(&mut hasher);
1084        self.key = Some(hasher.finish());
1085        self
1086    }
1087
1088    /// A component is its child's box: nothing to add.
1089    fn restyle(&self, base: Style) -> Style {
1090        base
1091    }
1092}
1093
1094/// The child a component builds, made the first time the tree reaches it.
1095struct LazyChild<Msg> {
1096    kind: TypeId,
1097    build: RefCell<Option<Builder<Msg>>>,
1098    built: OnceCell<Box<dyn Widget<Msg>>>,
1099    unbuilt: Unbuilt,
1100}
1101
1102impl<Msg> LazyChild<Msg> {
1103    /// Builds the child if it has not been, then lets *it* expand: what it built may be a
1104    /// component too.
1105    fn expand(&self, id: WidgetId, runtime: &Runtime, theme: &Theme) {
1106        if self.built.get().is_none() {
1107            let build = self.build.borrow_mut().take();
1108            if let Some(build) = build {
1109                let depth = DEPTH.with(|d| d.get());
1110                let cx = BuildContext::new((id, depth), self.kind, runtime, theme);
1111                let child = build(&cx);
1112                let _ = self.built.set(child);
1113            }
1114        }
1115        if let Some(child) = self.built.get() {
1116            let _deeper = Deeper::enter();
1117            child.expand(id, runtime, theme);
1118        }
1119    }
1120
1121    fn build_in(&self, id: WidgetId, runtime: &Runtime, theme: &Theme) {
1122        if let Some(child) = self.built.get() {
1123            child.build_in(id, runtime, theme);
1124        }
1125    }
1126
1127    fn build_themed(&self, theme: &Theme) {
1128        if let Some(child) = self.built.get() {
1129            child.build_themed(theme);
1130        }
1131    }
1132
1133    fn children(&self) -> &[Box<dyn Widget<Msg>>] {
1134        match self.built.get() {
1135            Some(child) => child.children(),
1136            None => {
1137                #[cfg(debug_assertions)]
1138                if self.build.borrow().is_some() {
1139                    panic!(
1140                        "a component's children were read before it was built: a traversal \
1141                         reached this tree before the walk that builds components did. Call \
1142                         `build_deferred(tree, &theme, &runtime)` on it first"
1143                    );
1144                }
1145                &[]
1146            }
1147        }
1148    }
1149}
1150
1151impl<Msg> Deref for LazyChild<Msg> {
1152    type Target = dyn Widget<Msg>;
1153
1154    fn deref(&self) -> &(dyn Widget<Msg> + 'static) {
1155        match self.built.get() {
1156            Some(child) => child.as_ref(),
1157            None => &self.unbuilt,
1158        }
1159    }
1160}
1161
1162/// What a component answers as before it is built: nothing.
1163struct Unbuilt;
1164
1165impl<Msg> Widget<Msg> for Unbuilt {
1166    fn style(&self) -> Style {
1167        Style::default()
1168    }
1169
1170    fn children(&self) -> &[Box<dyn Widget<Msg>>] {
1171        &[]
1172    }
1173
1174    fn paint(&self, _bounds: Rect, _status: Status, _theme: &Theme, _scene: &mut Scene) {}
1175
1176    fn on_click(&self) -> Option<Msg> {
1177        None
1178    }
1179
1180    fn debug_name(&self) -> &'static str {
1181        "Component"
1182    }
1183}
1184
1185crate::transparent::forward_transparent!(Component {
1186    /// A component's key is its own and never its child's: the parent reads it to place
1187    /// the component *before* the component has been built, and an answer that changed
1188    /// when the child arrived would move the place under the state that lives there.
1189    fn key(&self) -> Option<u64> {
1190        self.key
1191    }
1192
1193    fn positioned(&self) -> Option<crate::positioned::Positioning> {
1194        self.inner.positioned()
1195    }
1196
1197    fn theme_override(
1198        &self,
1199        inherited: &crate::theme::Theme,
1200    ) -> Option<Box<crate::theme::Theme>> {
1201        self.inner.theme_override(inherited)
1202    }
1203
1204    fn media_override(&self, inherited: crate::MediaQuery) -> Option<crate::MediaQuery> {
1205        self.inner.media_override(inherited)
1206    }
1207
1208    fn scaffold_override(&self) -> Option<crate::ScaffoldInfo> {
1209        self.inner.scaffold_override()
1210    }
1211    fn scroll_behavior_override(&self) -> Option<crate::ScrollBehavior> {
1212        self.inner.scroll_behavior_override()
1213    }
1214
1215    fn autofill_group(&self) -> bool {
1216        self.inner.autofill_group()
1217    }
1218    /// Forwarded: the area's bar is the area's, whatever wraps it.
1219    fn area_toolbar(
1220        &self,
1221        context: crate::ToolbarContext,
1222    ) -> Option<Option<&dyn crate::widget::Widget<Msg>>> {
1223        self.inner.area_toolbar(context)
1224    }
1225});
1226
1227/// A component made of a function: `component(|cx| …)`. The closure is called whenever the
1228/// tree is rebuilt, and may call hooks.
1229///
1230/// ```
1231/// use frus_widgets::{component, text};
1232///
1233/// let clock = component(|cx| {
1234///     let ticks = cx.use_state(|| 0);
1235///     Box::new(text(format!("{} ticks", ticks.get())))
1236/// });
1237/// # let _ = clock;
1238/// ```
1239pub fn component(build: impl Fn(&BuildContext) -> Box<dyn Widget> + 'static) -> Component {
1240    Component::stateless(build)
1241}
1242
1243#[cfg(test)]
1244mod tests {
1245    use super::*;
1246    use crate::{build_deferred, keyed, Container, Flex};
1247    use frus_layout::Dimension;
1248
1249    type Builder = Rc<dyn Fn(&BuildContext) -> Box<dyn Widget>>;
1250    type Log = Rc<RefCell<Vec<String>>>;
1251    type Handle = Rc<RefCell<Option<StateHandle<LifeState>>>>;
1252
1253    fn log() -> Log {
1254        Rc::new(RefCell::new(Vec::new()))
1255    }
1256
1257    fn taken(log: &Log) -> Vec<String> {
1258        std::mem::take(&mut *log.borrow_mut())
1259    }
1260
1261    /// One frame of the shell's, less the drawing: the tree is built, and what it did not
1262    /// reach is let go.
1263    fn frame(runtime: &Runtime, root: &dyn Widget) {
1264        runtime.states.begin_build();
1265        build_deferred(root, &Theme::default(), runtime);
1266        runtime.states.end_frame();
1267    }
1268
1269    /// A stateful widget that says what happens to it.
1270    struct Life {
1271        tag: u32,
1272        log: Log,
1273        out: Handle,
1274    }
1275
1276    struct LifeState {
1277        log: Log,
1278        out: Handle,
1279        count: i32,
1280    }
1281
1282    impl StatefulWidget for Life {
1283        type State = LifeState;
1284        fn create_state(&self) -> LifeState {
1285            LifeState {
1286                log: self.log.clone(),
1287                out: self.out.clone(),
1288                count: 0,
1289            }
1290        }
1291    }
1292
1293    impl State for LifeState {
1294        type Widget = Life;
1295        fn init_state(&mut self, cx: &StateContext<Self>) {
1296            self.log
1297                .borrow_mut()
1298                .push(format!("init {}", cx.widget().tag));
1299            *self.out.borrow_mut() = Some(cx.handle());
1300        }
1301        fn did_update_widget(&mut self, old: &Life, cx: &StateContext<Self>) {
1302            self.log
1303                .borrow_mut()
1304                .push(format!("update {}->{}", old.tag, cx.widget().tag));
1305        }
1306        fn dispose(&mut self) {
1307            self.log.borrow_mut().push("dispose".to_string());
1308        }
1309        fn build(&self, _cx: &StateContext<Self>) -> Box<dyn Widget> {
1310            Box::new(Container::new().width(10.0 + self.count as f32).height(4.0))
1311        }
1312    }
1313
1314    fn life(tag: u32, log: &Log, out: &Handle) -> Component {
1315        Life {
1316            tag,
1317            log: log.clone(),
1318            out: out.clone(),
1319        }
1320        .into_widget()
1321    }
1322
1323    fn handle() -> Handle {
1324        Rc::new(RefCell::new(None))
1325    }
1326
1327    fn width(widget: &dyn Widget) -> Dimension {
1328        widget.style().width
1329    }
1330
1331    #[test]
1332    fn a_stateless_component_is_the_widget_it_builds() {
1333        let runtime = Runtime::default();
1334        let root = component(|_| Box::new(Container::new().width(120.0).height(30.0)));
1335        frame(&runtime, &root);
1336        assert_eq!(width(&root), Dimension::Length(120.0));
1337        assert_eq!(root.style().height, Dimension::Length(30.0));
1338    }
1339
1340    #[test]
1341    fn a_state_is_made_once_and_survives_rebuilds() {
1342        let (runtime, log, out) = (Runtime::default(), log(), handle());
1343        let first = life(1, &log, &out);
1344        frame(&runtime, &first);
1345        assert_eq!(taken(&log), ["init 1"]);
1346        assert_eq!(width(&first), Dimension::Length(10.0));
1347
1348        // the state is changed from outside, as a handler does
1349        out.borrow().as_ref().unwrap().set_state(|s| s.count = 5);
1350        assert!(take_rebuild_request(), "set_state asks for a rebuild");
1351
1352        let second = life(2, &log, &out);
1353        frame(&runtime, &second);
1354        assert_eq!(
1355            taken(&log),
1356            ["update 1->2"],
1357            "the same place, the same kind: the state is kept and told what changed"
1358        );
1359        assert_eq!(
1360            width(&second),
1361            Dimension::Length(15.0),
1362            "and it is the kept one"
1363        );
1364    }
1365
1366    #[test]
1367    fn a_handler_carries_the_value_a_widget_reports_into_the_state() {
1368        let (runtime, log, out) = (Runtime::default(), log(), handle());
1369        frame(&runtime, &life(1, &log, &out));
1370        let handle = out.borrow().clone().unwrap();
1371        let on_value = handle.handler(|s, by: i32| s.count += by);
1372        let (three, four) = (on_value(3), on_value(4));
1373        assert_eq!(
1374            handle.read(|s| s.count),
1375            0,
1376            "nothing changes until it is delivered"
1377        );
1378        three.call();
1379        four.call();
1380        four.call();
1381        assert_eq!(
1382            handle.read(|s| s.count),
1383            11,
1384            "each delivery makes the change"
1385        );
1386        assert!(take_rebuild_request(), "and it asks for the rebuild");
1387    }
1388
1389    #[test]
1390    fn a_component_that_blocks_back_does_so_for_one_build_only() {
1391        let runtime = Runtime::default();
1392        let open = Rc::new(Cell::new(true));
1393        let asked = open.clone();
1394        let root = component(move |cx| {
1395            cx.block_back(asked.get());
1396            Box::new(Container::new().width(4.0).height(4.0))
1397        });
1398        frame(&runtime, &root);
1399        assert!(back_blocked(), "an open menu is in the way");
1400        open.set(false);
1401        frame(&runtime, &root);
1402        assert!(!back_blocked(), "each build says it afresh");
1403    }
1404
1405    #[test]
1406    fn a_state_is_disposed_when_a_rebuild_no_longer_reaches_it() {
1407        let (runtime, log, out) = (Runtime::default(), log(), handle());
1408        frame(&runtime, &Flex::column().child(life(1, &log, &out)));
1409        taken(&log);
1410        assert_eq!(runtime.states.len(), 1);
1411
1412        frame(&runtime, &Flex::column().child(Container::new()));
1413        assert_eq!(taken(&log), ["dispose"]);
1414        assert!(runtime.states.is_empty());
1415        let handle = out.borrow().clone().unwrap();
1416        assert!(!handle.is_mounted());
1417        take_rebuild_request();
1418        handle.set_state(|s| s.count = 9);
1419        assert!(
1420            !take_rebuild_request(),
1421            "a state that has left cannot ask for a rebuild"
1422        );
1423    }
1424
1425    #[test]
1426    fn a_different_kind_of_widget_in_the_same_place_starts_over() {
1427        let (runtime, log, out) = (Runtime::default(), log(), handle());
1428        frame(&runtime, &Flex::column().child(life(1, &log, &out)));
1429        taken(&log);
1430        // a stateless component that keeps hooks takes the place the state had
1431        let other = component(|cx| {
1432            cx.use_state(|| 0u8);
1433            Box::new(Container::new())
1434        });
1435        frame(&runtime, &Flex::column().child(other));
1436        assert_eq!(
1437            taken(&log),
1438            ["dispose"],
1439            "the old kind is disposed, not reused"
1440        );
1441    }
1442
1443    #[test]
1444    fn a_key_carries_the_state_wherever_the_siblings_go() {
1445        let (runtime, log) = (Runtime::default(), log());
1446        let (a, b) = (handle(), handle());
1447        let column = |order: [u32; 2]| {
1448            let mut column = Flex::column();
1449            for tag in order {
1450                let out = if tag == 1 { &a } else { &b };
1451                column = column.child(keyed(tag, life(tag, &log, out)));
1452            }
1453            column
1454        };
1455        frame(&runtime, &column([1, 2]));
1456        a.borrow().as_ref().unwrap().set_state(|s| s.count = 1);
1457        b.borrow().as_ref().unwrap().set_state(|s| s.count = 2);
1458        taken(&log);
1459
1460        frame(&runtime, &column([2, 1]));
1461        let counts = |h: &Handle| h.borrow().as_ref().unwrap().read(|s| s.count);
1462        assert_eq!(
1463            (counts(&a), counts(&b)),
1464            (1, 2),
1465            "each state stayed with its key"
1466        );
1467        assert_eq!(runtime.states.len(), 2);
1468        assert!(
1469            !taken(&log)
1470                .iter()
1471                .any(|line| line == "dispose" || line.starts_with("init")),
1472            "and neither was made again or let go"
1473        );
1474    }
1475
1476    #[test]
1477    fn a_component_that_builds_a_component_keeps_both_states() {
1478        let (runtime, log, out) = (Runtime::default(), log(), handle());
1479        let (inner_log, inner_out) = (log.clone(), out.clone());
1480        let outer = component(move |cx| {
1481            cx.use_state(|| 0);
1482            // built *directly*: the same place in the tree as this component
1483            Box::new(life(7, &inner_log, &inner_out))
1484        });
1485        frame(&runtime, &outer);
1486        assert_eq!(
1487            runtime.states.len(),
1488            2,
1489            "one for the hooks, one for the state"
1490        );
1491        assert_eq!(taken(&log), ["init 7"]);
1492    }
1493
1494    #[test]
1495    fn hooks_keep_their_values_between_builds() {
1496        let runtime = Runtime::default();
1497        let seen = Rc::new(RefCell::new(Vec::new()));
1498        // One closure, built again by every frame: the same kind of component each time.
1499        let build = {
1500            let seen = seen.clone();
1501            Rc::new(move |cx: &BuildContext| -> Box<dyn Widget> {
1502                let taps = cx.use_state(|| 10);
1503                let scratch = cx.use_ref(|| 0);
1504                scratch.update(|n| *n += 1);
1505                taps.update(|n| *n += 1);
1506                seen.borrow_mut().push((taps.get(), scratch.get()));
1507                Box::new(Container::new())
1508            })
1509        };
1510        let make = |build: &Builder| {
1511            let build = build.clone();
1512            Component::stateless(move |cx: &BuildContext| build(cx))
1513        };
1514        let build: Builder = build;
1515        frame(&runtime, &make(&build));
1516        assert!(
1517            take_rebuild_request(),
1518            "changing a state hook asks for a rebuild"
1519        );
1520        frame(&runtime, &make(&build));
1521        frame(&runtime, &make(&build));
1522        assert_eq!(*seen.borrow(), [(11, 1), (12, 2), (13, 3)]);
1523    }
1524
1525    #[test]
1526    fn use_memo_recomputes_only_when_its_dependencies_change() {
1527        let runtime = Runtime::default();
1528        let computed = Rc::new(Cell::new(0));
1529        let dep = Rc::new(Cell::new(1));
1530        let build: Builder = {
1531            let (computed, dep) = (computed.clone(), dep.clone());
1532            Rc::new(move |cx| {
1533                let d = dep.get();
1534                let value = cx.use_memo(d, || {
1535                    computed.set(computed.get() + 1);
1536                    d * 100
1537                });
1538                Box::new(Container::new().width(*value as f32))
1539            })
1540        };
1541        let make = || {
1542            let build = build.clone();
1543            Component::stateless(move |cx: &BuildContext| build(cx))
1544        };
1545        let root = make();
1546        frame(&runtime, &root);
1547        assert_eq!(
1548            (computed.get(), width(&root)),
1549            (1, Dimension::Length(100.0))
1550        );
1551        let root = make();
1552        frame(&runtime, &root);
1553        assert_eq!(computed.get(), 1, "the same dependency: not computed again");
1554        dep.set(2);
1555        let root = make();
1556        frame(&runtime, &root);
1557        assert_eq!(
1558            (computed.get(), width(&root)),
1559            (2, Dimension::Length(200.0))
1560        );
1561    }
1562
1563    #[test]
1564    fn effects_run_after_the_build_and_clean_up_before_the_next_and_at_the_end() {
1565        let (runtime, log) = (Runtime::default(), log());
1566        let dep = Rc::new(Cell::new(1));
1567        let build: Builder = {
1568            let (log, dep) = (log.clone(), dep.clone());
1569            Rc::new(move |cx| {
1570                let d = dep.get();
1571                let log = log.clone();
1572                log.borrow_mut().push(format!("build {d}"));
1573                cx.use_effect(d, move || {
1574                    log.borrow_mut().push(format!("effect {d}"));
1575                    move || log.borrow_mut().push(format!("cleanup {d}"))
1576                });
1577                Box::new(Container::new())
1578            })
1579        };
1580        let make = || {
1581            let build = build.clone();
1582            Component::stateless(move |cx: &BuildContext| build(cx))
1583        };
1584        frame(&runtime, &make());
1585        assert_eq!(
1586            taken(&log),
1587            ["build 1", "effect 1"],
1588            "after the build, not during it"
1589        );
1590
1591        frame(&runtime, &make());
1592        assert_eq!(
1593            taken(&log),
1594            ["build 1"],
1595            "the same dependency: nothing runs"
1596        );
1597
1598        dep.set(2);
1599        frame(&runtime, &make());
1600        assert_eq!(
1601            taken(&log),
1602            ["build 2", "cleanup 1", "effect 2"],
1603            "the old one is undone before the new one starts"
1604        );
1605
1606        // leaving the tree runs the last cleanup
1607        frame(&runtime, &Flex::column().child(Container::new()));
1608        assert_eq!(taken(&log), ["cleanup 2"]);
1609    }
1610
1611    #[test]
1612    #[should_panic(expected = "different order")]
1613    fn hooks_called_in_a_different_order_are_caught() {
1614        let runtime = Runtime::default();
1615        let first = Rc::new(Cell::new(true));
1616        let build: Builder = {
1617            let first = first.clone();
1618            Rc::new(move |cx| {
1619                if first.get() {
1620                    cx.use_state(|| 1i32);
1621                }
1622                cx.use_ref(String::new);
1623                Box::new(Container::new())
1624            })
1625        };
1626        let make = || {
1627            let build = build.clone();
1628            Component::stateless(move |cx: &BuildContext| build(cx))
1629        };
1630        frame(&runtime, &make());
1631        first.set(false);
1632        frame(&runtime, &make());
1633    }
1634
1635    #[test]
1636    #[should_panic(expected = "set_state was called while")]
1637    fn changing_state_from_inside_build_is_refused() {
1638        struct Greedy;
1639        struct GreedyState;
1640        impl StatefulWidget for Greedy {
1641            type State = GreedyState;
1642            fn create_state(&self) -> GreedyState {
1643                GreedyState
1644            }
1645        }
1646        impl State for GreedyState {
1647            type Widget = Greedy;
1648            fn build(&self, cx: &StateContext<Self>) -> Box<dyn Widget> {
1649                cx.handle().set_state(|_| {});
1650                Box::new(Container::new())
1651            }
1652        }
1653        frame(&Runtime::default(), &Greedy.into_widget());
1654    }
1655
1656    #[test]
1657    fn an_interval_is_asked_for_by_each_build_that_wants_it() {
1658        let runtime = Runtime::default();
1659        let on = Rc::new(Cell::new(true));
1660        let build: Builder = {
1661            let on = on.clone();
1662            Rc::new(move |cx| {
1663                if on.get() {
1664                    cx.use_interval(std::time::Duration::from_secs(1), || {});
1665                    cx.use_interval(std::time::Duration::from_secs(5), || {});
1666                }
1667                Box::new(Container::new())
1668            })
1669        };
1670        let make = || {
1671            let build = build.clone();
1672            Component::stateless(move |cx: &BuildContext| build(cx))
1673        };
1674        frame(&runtime, &make());
1675        let first = intervals();
1676        assert_eq!(first.len(), 2);
1677        assert_ne!(first[0].id, first[1].id, "two timers are told apart");
1678
1679        frame(&runtime, &make());
1680        let second = intervals();
1681        assert_eq!(
1682            first.iter().map(|i| i.id).collect::<Vec<_>>(),
1683            second.iter().map(|i| i.id).collect::<Vec<_>>(),
1684            "and each is the same timer from one build to the next"
1685        );
1686
1687        on.set(false);
1688        frame(&runtime, &make());
1689        assert!(
1690            intervals().is_empty(),
1691            "a build that stops asking stops the timer"
1692        );
1693    }
1694
1695    #[test]
1696    fn a_service_provided_to_the_runtime_is_reachable_from_a_build() {
1697        let runtime = Runtime::default();
1698        runtime.states.provide(Rc::new(41u32));
1699        let root = component(|cx| {
1700            let n = cx.service::<u32>().expect("provided");
1701            Box::new(Container::new().width(*n as f32 + 1.0))
1702        });
1703        frame(&runtime, &root);
1704        assert_eq!(width(&root), Dimension::Length(42.0));
1705        assert!(runtime.states.service::<String>().is_none());
1706    }
1707}