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