Skip to main content

compose_rust/runtime/
state.rs

1//! Observable state: what a composition reads, and what tells it to run again.
2//!
3//! A [`MutableState`] remembers which scopes read it during composition. Writing it, from
4//! the UI thread or from a worker, queues its id for the runtime that observed it and asks
5//! for a frame. At the next frame the runtime looks up who read that id and runs exactly
6//! those scopes again, and nothing else. This is Compose's snapshot invalidation without
7//! the isolation of separate snapshots: a write is visible to the next read at once, and
8//! what is deferred to the frame is the recomposition, not the value.
9//!
10//! A worker never touches the composition. It writes a value behind a lock and pushes an
11//! id onto a queue, and the UI thread does the rest inside its own Host call.
12
13use super::composer::Observer;
14use super::{with_composer, with_composer_if_idle};
15use crate::schema::{DesignSystem, NotificationPermission};
16use crate::window::WindowSize;
17use std::any::Any;
18use std::cell::{Cell, RefCell};
19use std::collections::HashMap;
20use std::fmt;
21use std::marker::PhantomData;
22use std::panic::Location;
23use std::rc::{Rc, Weak};
24use std::sync::atomic::{AtomicU64, Ordering};
25use std::sync::{Arc, Mutex, MutexGuard};
26
27use super::composer::RuntimeSignal;
28
29/// Ids for states and derived states. Shared between the two so an observer list never
30/// confuses one with the other.
31static NEXT_STATE_ID: AtomicU64 = AtomicU64::new(1);
32
33fn next_state_id() -> u64 {
34    NEXT_STATE_ID.fetch_add(1, Ordering::Relaxed)
35}
36
37fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
38    mutex.lock().unwrap_or_else(|poison| poison.into_inner())
39}
40
41/// When a write counts as a change. Compose calls this a `SnapshotMutationPolicy`.
42pub struct SnapshotMutationPolicy<T> {
43    equivalent: fn(&T, &T) -> bool,
44}
45
46impl<T> Clone for SnapshotMutationPolicy<T> {
47    fn clone(&self) -> Self {
48        *self
49    }
50}
51
52impl<T> Copy for SnapshotMutationPolicy<T> {}
53
54/// A write of an equal value changes nothing and runs nothing again.
55pub fn structural_equality_policy<T: PartialEq>() -> SnapshotMutationPolicy<T> {
56    SnapshotMutationPolicy {
57        equivalent: |left, right| left == right,
58    }
59}
60
61/// Every write is a change, whatever it wrote.
62pub fn never_equal_policy<T>() -> SnapshotMutationPolicy<T> {
63    SnapshotMutationPolicy {
64        equivalent: |_, _| false,
65    }
66}
67
68struct StateCell<T> {
69    id: u64,
70    value: Mutex<T>,
71    policy: SnapshotMutationPolicy<T>,
72    /// The runtimes that have read this state, so a write knows whom to tell.
73    runtimes: Mutex<Vec<std::sync::Weak<RuntimeSignal>>>,
74}
75
76impl<T> StateCell<T> {
77    fn notify(&self) {
78        let mut told = false;
79        lock(&self.runtimes).retain(|runtime| match runtime.upgrade() {
80            Some(signal) => {
81                signal.push_dirty(self.id);
82                told = true;
83                true
84            }
85            None => false,
86        });
87        // Inside a Host call the change is picked up before the call returns. Anywhere
88        // else, a worker above all, nothing would look until something asked for a frame.
89        if told && !crate::boundary::in_host_call() {
90            crate::boundary::request_frame_from_worker();
91        }
92    }
93}
94
95// ----- the arena ---------------------------------------------------------------------
96
97/// A place in the arena: the slot's index, and the generation it had when the state was
98/// put there. A slot that is freed and used again has a higher generation, so a handle
99/// to the old state never reaches the new one.
100#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
101pub(crate) struct StateKey {
102    index: u32,
103    generation: u32,
104}
105
106struct Entry {
107    generation: u32,
108    cell: Option<Arc<dyn Any + Send + Sync>>,
109}
110
111struct Arena {
112    entries: Vec<Entry>,
113    free: Vec<u32>,
114}
115
116/// Every state's value lives here, not in the handle. One lock for the table, and a lock
117/// of its own for each value, so a long read of one state does not hold up the others.
118static ARENA: Mutex<Arena> = Mutex::new(Arena {
119    entries: Vec::new(),
120    free: Vec::new(),
121});
122
123/// The handles that have been warned about, for a write after the state was freed.
124static WARNED: Mutex<Vec<StateKey>> = Mutex::new(Vec::new());
125
126fn allocate(cell: Arc<dyn Any + Send + Sync>) -> StateKey {
127    let mut arena = lock(&ARENA);
128    if let Some(index) = arena.free.pop() {
129        let entry = &mut arena.entries[index as usize];
130        entry.cell = Some(cell);
131        return StateKey {
132            index,
133            generation: entry.generation,
134        };
135    }
136    let index = arena.entries.len() as u32;
137    arena.entries.push(Entry {
138        generation: 0,
139        cell: Some(cell),
140    });
141    StateKey {
142        index,
143        generation: 0,
144    }
145}
146
147/// Frees a state and gives its value back to be dropped by the caller, once, with no lock
148/// held. `None` when the key was already freed.
149pub(crate) fn release_state(key: StateKey) -> Option<Box<dyn Any>> {
150    let mut arena = lock(&ARENA);
151    let entry = arena.entries.get_mut(key.index as usize)?;
152    if entry.generation != key.generation {
153        return None;
154    }
155    let cell = entry.cell.take()?;
156    entry.generation = entry.generation.wrapping_add(1);
157    arena.free.push(key.index);
158    Some(Box::new(cell))
159}
160
161// ----- who owns a state --------------------------------------------------------------
162
163thread_local! {
164    /// The states made since each open `remember` began, and where that call is.
165    static PENDING: RefCell<Vec<StateKey>> = const { RefCell::new(Vec::new()) };
166    static SITES: RefCell<Vec<&'static Location<'static>>> = const { RefCell::new(Vec::new()) };
167}
168
169/// The span of a `remember` call's initialiser. States made inside it belong to the
170/// slot the call fills.
171pub(crate) struct Capture {
172    mark: usize,
173}
174
175impl Capture {
176    pub(crate) fn begin(site: &'static Location<'static>) -> Self {
177        let mark = PENDING.with_borrow(Vec::len);
178        SITES.with_borrow_mut(|sites| sites.push(site));
179        Self { mark }
180    }
181
182    /// The states made since `begin`.
183    pub(crate) fn finish(self) -> Vec<StateKey> {
184        PENDING.with_borrow_mut(|pending| pending.split_off(self.mark.min(pending.len())))
185    }
186}
187
188impl Drop for Capture {
189    fn drop(&mut self) {
190        SITES.with_borrow_mut(|sites| {
191            sites.pop();
192        });
193        PENDING.with_borrow_mut(|pending| pending.truncate(self.mark));
194    }
195}
196
197/// Why a state could not be read or written.
198#[derive(Clone, Copy, Debug, PartialEq, Eq)]
199pub enum StateError {
200    /// The `remember` call that made the state has left the composition, and the state
201    /// went with it.
202    Released,
203}
204
205impl fmt::Display for StateError {
206    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
207        match self {
208            Self::Released => formatter.write_str(
209                "Released: the state left the composition with the remember call that made it",
210            ),
211        }
212    }
213}
214
215impl std::error::Error for StateError {}
216
217/// A handle to a value that composition observes. Compose's `MutableState`.
218///
219/// The handle is `Copy`: it names a place in an arena, and the value is owned by the
220/// composition. The value lives as long as the `remember` call that made it stays in the
221/// composition and is dropped, once, when that call's group is removed. A handle used
222/// after that is released: `set` and `update` do nothing, `get` panics, and the `try_`
223/// forms return [`StateError::Released`]. A slot reused for another state has a new
224/// generation, so an old handle never reaches the new value.
225///
226/// Two handles are equal when they name the same state, which is what makes a state a
227/// stable parameter: passing the same state to a composable again does not make it run.
228/// Equal values in two different states are not equal handles.
229///
230/// A handle is `Send` and `Sync`, so a worker thread can take one by `move` and write it.
231/// The write is visible at once; the composables that read it run again on the next
232/// frame, which the write asks for. A read on a worker clones the value and subscribes to
233/// nothing.
234///
235/// Reading takes a lock for as long as the read lasts. Reading the same state again inside
236/// [`MutableState::with`] would wait on itself.
237pub struct MutableState<T> {
238    key: StateKey,
239    /// The `remember` call that made the state, for the message of a read that fails.
240    site: &'static Location<'static>,
241    marker: PhantomData<fn() -> T>,
242}
243
244impl<T> Clone for MutableState<T> {
245    fn clone(&self) -> Self {
246        *self
247    }
248}
249
250impl<T> Copy for MutableState<T> {}
251
252impl<T> PartialEq for MutableState<T> {
253    fn eq(&self, other: &Self) -> bool {
254        self.key == other.key
255    }
256}
257
258impl<T> Eq for MutableState<T> {}
259
260impl<T> std::hash::Hash for MutableState<T> {
261    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
262        self.key.hash(state);
263    }
264}
265
266impl<T> fmt::Debug for MutableState<T> {
267    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
268        formatter
269            .debug_struct("MutableState")
270            .field("index", &self.key.index)
271            .field("generation", &self.key.generation)
272            .finish()
273    }
274}
275
276/// A state whose writes of an equal value change nothing. Compose's `mutableStateOf`.
277///
278/// ```ignore
279/// let count = remember(|| mutable_state_of(0));
280/// Text(format!("{}", count.get()));
281/// Button("More").on_click(move || count.update(|count| *count += 1));
282/// ```
283///
284/// Made inside `remember`, the state belongs to that call. Made anywhere else, it is not
285/// freed until the program ends.
286#[track_caller]
287pub fn mutable_state_of<T: PartialEq + Send + 'static>(value: T) -> MutableState<T> {
288    mutable_state_of_with_policy(value, structural_equality_policy())
289}
290
291/// A state with a policy of its own for what counts as a change.
292#[track_caller]
293pub fn mutable_state_of_with_policy<T: Send + 'static>(
294    value: T,
295    policy: SnapshotMutationPolicy<T>,
296) -> MutableState<T> {
297    let here = Location::caller();
298    let cell: Arc<dyn Any + Send + Sync> = Arc::new(StateCell {
299        id: next_state_id(),
300        value: Mutex::new(value),
301        policy,
302        runtimes: Mutex::new(Vec::new()),
303    });
304    let key = allocate(cell);
305    let site = SITES.with_borrow(|sites| sites.last().copied());
306    if site.is_some() {
307        PENDING.with_borrow_mut(|pending| pending.push(key));
308    }
309    MutableState {
310        key,
311        site: site.unwrap_or(here),
312        marker: PhantomData,
313    }
314}
315
316impl<T: Send + 'static> MutableState<T> {
317    fn resolve(&self) -> Option<Arc<StateCell<T>>> {
318        let cell = {
319            let arena = lock(&ARENA);
320            let entry = arena.entries.get(self.key.index as usize)?;
321            if entry.generation != self.key.generation {
322                return None;
323            }
324            entry.cell.clone()?
325        };
326        cell.downcast::<StateCell<T>>().ok()
327    }
328
329    fn released(&self) -> ! {
330        panic!(
331            "Released: this MutableState was used after the remember call at {}:{} left the composition",
332            self.site.file(),
333            self.site.line()
334        )
335    }
336
337    fn warn_released(&self) {
338        if !cfg!(debug_assertions) {
339            return;
340        }
341        {
342            let mut warned = lock(&WARNED);
343            if warned.contains(&self.key) {
344                return;
345            }
346            warned.push(self.key);
347        }
348        eprintln!(
349            "compose-rust: a write to a released MutableState was ignored (made by the remember call at {}:{})",
350            self.site.file(),
351            self.site.line()
352        );
353    }
354
355    /// How many warnings this handle has caused: one in a debug build after its first
356    /// write while released, never in a release build.
357    #[doc(hidden)]
358    pub fn released_warning_count(&self) -> usize {
359        lock(&WARNED).iter().filter(|key| **key == self.key).count()
360    }
361
362    /// Whether the state is still in the composition.
363    pub fn is_released(&self) -> bool {
364        self.resolve().is_none()
365    }
366
367    /// The value, recorded as read by the scope composing now. Panics if the state is
368    /// released.
369    pub fn get(&self) -> T
370    where
371        T: Clone,
372    {
373        self.try_get().unwrap_or_else(|_| self.released())
374    }
375
376    /// Like [`MutableState::get`], and `Err(StateError::Released)` if the state is
377    /// released.
378    pub fn try_get(&self) -> Result<T, StateError>
379    where
380        T: Clone,
381    {
382        let cell = self.resolve().ok_or(StateError::Released)?;
383        Self::track(&cell);
384        let value = lock(&cell.value).clone();
385        Ok(value)
386    }
387
388    /// The value, without recording a read. A scope that only peeks is not run again when
389    /// the value changes. Panics if the state is released.
390    pub fn get_untracked(&self) -> T
391    where
392        T: Clone,
393    {
394        match self.resolve() {
395            Some(cell) => lock(&cell.value).clone(),
396            None => self.released(),
397        }
398    }
399
400    /// Reads the value in place, recorded as read. Panics if the state is released.
401    pub fn with<R>(&self, read: impl FnOnce(&T) -> R) -> R {
402        match self.resolve() {
403            Some(cell) => {
404                Self::track(&cell);
405                let value = lock(&cell.value);
406                read(&value)
407            }
408            None => self.released(),
409        }
410    }
411
412    /// Replaces the value. Under the default policy an equal value changes nothing. Does
413    /// nothing if the state is released.
414    pub fn set(&self, value: T) {
415        if self.try_set(value).is_err() {
416            self.warn_released();
417        }
418    }
419
420    /// Like [`MutableState::set`], and `Err(StateError::Released)` if the state is
421    /// released.
422    pub fn try_set(&self, value: T) -> Result<(), StateError> {
423        let cell = self.resolve().ok_or(StateError::Released)?;
424        {
425            let mut current = lock(&cell.value);
426            if (cell.policy.equivalent)(&current, &value) {
427                return Ok(());
428            }
429            *current = value;
430        }
431        cell.notify();
432        Ok(())
433    }
434
435    /// Changes the value in place. Always a change: there is no old value left to compare
436    /// against without a copy, and a caller who wants the comparison can use `set`. Does
437    /// nothing if the state is released.
438    pub fn update(&self, change: impl FnOnce(&mut T)) {
439        if self.try_update(change).is_err() {
440            self.warn_released();
441        }
442    }
443
444    /// Like [`MutableState::update`], and `Err(StateError::Released)` if the state is
445    /// released.
446    pub fn try_update(&self, change: impl FnOnce(&mut T)) -> Result<(), StateError> {
447        let cell = self.resolve().ok_or(StateError::Released)?;
448        change(&mut lock(&cell.value));
449        cell.notify();
450        Ok(())
451    }
452
453    /// This state as a read-only [`State`].
454    pub fn as_state(&self) -> MutableState<T> {
455        *self
456    }
457
458    fn track(cell: &Arc<StateCell<T>>) {
459        let fresh = with_composer_if_idle(|composer| {
460            composer
461                .record_read(cell.id)
462                .then(|| Arc::clone(&composer.signal))
463        })
464        .flatten();
465        if let Some(signal) = fresh {
466            let mut runtimes = lock(&cell.runtimes);
467            if !runtimes
468                .iter()
469                .any(|known| std::ptr::eq(known.as_ptr(), Arc::as_ptr(&signal)))
470            {
471                runtimes.push(Arc::downgrade(&signal));
472            }
473        }
474    }
475}
476
477/// Something composition can read. Compose's `State`.
478pub trait State<T> {
479    /// The value, recorded as read by the scope composing now.
480    fn value(&self) -> T;
481}
482
483impl<T: Clone + Send + 'static> State<T> for MutableState<T> {
484    fn value(&self) -> T {
485        self.get()
486    }
487}
488
489impl<T: Clone + PartialEq + 'static> State<T> for DerivedState<T> {
490    fn value(&self) -> T {
491        self.get()
492    }
493}
494
495/// A derived state's half of change propagation.
496pub(crate) trait DerivedNode {
497    /// Computes the value again and says whether it changed.
498    fn refresh(&self) -> bool;
499    /// Notes that the value is out of date, to be computed when next read.
500    fn mark_dirty(&self);
501}
502
503struct DerivedCell<T> {
504    id: u64,
505    compute: Box<dyn Fn() -> T>,
506    value: RefCell<Option<T>>,
507    dirty: Cell<bool>,
508}
509
510/// A value computed from other states, which tells its readers only when the result
511/// changes. Compose's `derivedStateOf`.
512///
513/// A list that changes on every keystroke and a screen that only cares whether the list is
514/// empty is the case it is for: the screen observes the derived boolean, and the boolean
515/// is recomputed on every change but only runs the screen again when it flips.
516///
517/// It belongs to the UI thread. Remember it, so it is computed once per change rather than
518/// once per composition.
519pub struct DerivedState<T> {
520    cell: Rc<DerivedCell<T>>,
521}
522
523impl<T> Clone for DerivedState<T> {
524    fn clone(&self) -> Self {
525        Self {
526            cell: Rc::clone(&self.cell),
527        }
528    }
529}
530
531impl<T> PartialEq for DerivedState<T> {
532    fn eq(&self, other: &Self) -> bool {
533        Rc::ptr_eq(&self.cell, &other.cell)
534    }
535}
536
537/// A state computed from the states `calculation` reads.
538///
539/// ```ignore
540/// let empty = remember(move || derived_state_of(move || tasks.with(Vec::is_empty)));
541/// if empty.get() { Text("Nothing to do") }
542/// ```
543pub fn derived_state_of<T: Clone + PartialEq + 'static>(
544    calculation: impl Fn() -> T + 'static,
545) -> DerivedState<T> {
546    DerivedState {
547        cell: Rc::new(DerivedCell {
548            id: next_state_id(),
549            compute: Box::new(calculation),
550            value: RefCell::new(None),
551            dirty: Cell::new(true),
552        }),
553    }
554}
555
556impl<T: Clone + PartialEq + 'static> DerivedState<T> {
557    /// The value, recorded as read by the scope composing now.
558    pub fn get(&self) -> T {
559        let in_runtime = with_composer_if_idle(|_| ()).is_some();
560        if !in_runtime {
561            // Nothing outside a runtime can say when the inputs change, so nothing is
562            // cached: the value is computed fresh.
563            return (self.cell.compute)();
564        }
565        if self.cell.dirty.get() || self.cell.value.borrow().is_none() {
566            self.recompute();
567        }
568        with_composer_if_idle(|composer| composer.record_read(self.cell.id));
569        self.cell
570            .value
571            .borrow()
572            .clone()
573            .expect("a derived state holds a value once computed")
574    }
575
576    fn recompute(&self) -> bool {
577        let id = self.cell.id;
578        let node: Rc<dyn DerivedNode> = self.cell.clone();
579        let weak = Rc::downgrade(&node);
580        with_composer(|composer| {
581            if let Some(deps) = composer.derived_deps.remove(&id) {
582                for dep in deps {
583                    composer.unobserve(dep, Observer::Derived(id));
584                }
585            }
586            composer.ambient.deriveds.insert(id, weak);
587            composer.derived_stack.push(id);
588        });
589        let computed = (self.cell.compute)();
590        with_composer(|composer| {
591            composer.derived_stack.pop();
592        });
593        self.cell.dirty.set(false);
594        let mut stored = self.cell.value.borrow_mut();
595        let changed = stored.as_ref() != Some(&computed);
596        *stored = Some(computed);
597        changed
598    }
599}
600
601impl<T: Clone + PartialEq + 'static> DerivedNode for DerivedCell<T> {
602    fn refresh(&self) -> bool {
603        // The same as `DerivedState::recompute`, reached through the cell.
604        let state = DerivedState {
605            cell: rc_from_cell(self),
606        };
607        state.recompute()
608    }
609
610    fn mark_dirty(&self) {
611        self.dirty.set(true);
612    }
613}
614
615/// Recovers the `Rc` a derived cell lives in. Every cell is created inside an `Rc` by
616/// `derived_state_of` and only reached through one, so the registry's weak handle is
617/// always upgraded before this is called and the strong count is at least one.
618fn rc_from_cell<T>(cell: &DerivedCell<T>) -> Rc<DerivedCell<T>> {
619    let pointer = cell as *const DerivedCell<T>;
620    // SAFETY: `cell` is the value of an `Rc<DerivedCell<T>>` allocation (see above), and
621    // a strong reference is held by the caller for the duration of this call, so
622    // incrementing the count and rebuilding a second `Rc` is sound.
623    unsafe {
624        Rc::increment_strong_count(pointer);
625        Rc::from_raw(pointer)
626    }
627}
628
629/// Values the Renderer reports, as states, so a composable that reads one runs again when
630/// the report changes and nothing else does.
631pub(crate) struct Ambient {
632    pub(crate) window: MutableState<WindowSize>,
633    pub(crate) design: MutableState<DesignSystem>,
634    pub(crate) permission: MutableState<NotificationPermission>,
635    /// What each window of a multi-window application last measured, as a state per
636    /// window, made when a composable in that window first reads it.
637    pub(crate) windows: HashMap<u32, WindowAmbient>,
638    pub(crate) deriveds: HashMap<u64, Weak<dyn DerivedNode>>,
639}
640
641/// One window's size as a state, and the subscription that moves it.
642pub(crate) struct WindowAmbient {
643    state: MutableState<WindowSize>,
644    _subscription: crate::window::WindowSubscription,
645}
646
647impl Drop for WindowAmbient {
648    fn drop(&mut self) {
649        drop(release_state(self.state.key));
650    }
651}
652
653impl Drop for Ambient {
654    fn drop(&mut self) {
655        let values = [
656            release_state(self.window.key),
657            release_state(self.design.key),
658            release_state(self.permission.key),
659        ];
660        drop(values);
661    }
662}
663
664impl Ambient {
665    pub(crate) fn new() -> Self {
666        Self {
667            window: mutable_state_of(crate::window::window_size()),
668            design: mutable_state_of(crate::design::design_system()),
669            permission: mutable_state_of(crate::notification::notification_permission()),
670            windows: HashMap::new(),
671            deriveds: HashMap::new(),
672        }
673    }
674}
675
676/// Runs again whatever read the states written since the last look.
677///
678/// Scopes are marked invalid. A derived state that somebody reads is computed again on
679/// the spot, and its readers are marked only if its value moved; one nobody reads is
680/// marked out of date and left for its next read. Called with the composer free, because
681/// computing a derived state reads states.
682pub(crate) fn apply_state_changes() {
683    loop {
684        let Some(mut changed) = with_composer(|composer| {
685            let mut scratch = std::mem::take(&mut composer.dirty_scratch);
686            std::mem::swap(&mut *lock(&composer.signal.dirty), &mut scratch);
687            scratch
688        }) else {
689            return;
690        };
691        if changed.is_empty() {
692            with_composer(|composer| composer.dirty_scratch = changed);
693            return;
694        }
695        let mut index = 0;
696        while index < changed.len() {
697            let state = changed[index];
698            index += 1;
699            let observers = with_composer(|composer| {
700                composer.observers.get(&state).cloned().unwrap_or_default()
701            })
702            .unwrap_or_default();
703            for observer in observers {
704                match observer {
705                    Observer::Scope(group, generation) => {
706                        with_composer(|composer| composer.invalidate(group, generation));
707                    }
708                    Observer::Derived(derived) => {
709                        let (node, watched) = with_composer(|composer| {
710                            let node = composer
711                                .ambient
712                                .deriveds
713                                .get(&derived)
714                                .and_then(Weak::upgrade);
715                            let watched = composer
716                                .observers
717                                .get(&derived)
718                                .is_some_and(|list| !list.is_empty());
719                            (node, watched)
720                        })
721                        .unwrap_or((None, false));
722                        let Some(node) = node else {
723                            continue;
724                        };
725                        if watched {
726                            if node.refresh() && !changed.contains(&derived) {
727                                changed.push(derived);
728                            }
729                        } else {
730                            node.mark_dirty();
731                        }
732                    }
733                }
734            }
735        }
736        changed.clear();
737        with_composer(|composer| composer.dirty_scratch = changed);
738    }
739}
740
741/// The window's size, recorded as read: the composable runs again when the Renderer
742/// reports a different size class.
743///
744/// In a multi-window application it is the size of the window the composable is in,
745/// and the composable runs again only when that window's class changes.
746pub fn current_window_size() -> WindowSize {
747    let window = crate::windows::current_window_id();
748    if window != 0 {
749        if let Some(state) = window_state(window) {
750            return state.get();
751        }
752    }
753    match with_composer_if_idle(|composer| composer.ambient.window) {
754        Some(state) => state.get(),
755        None => crate::window::window_size(),
756    }
757}
758
759/// The state that follows one window's size, made on first use.
760fn window_state(window: u32) -> Option<MutableState<WindowSize>> {
761    with_composer_if_idle(|composer| {
762        if let Some(entry) = composer.ambient.windows.get(&window) {
763            return entry.state;
764        }
765        let state = mutable_state_of(crate::window::window_size_of(window));
766        // The ambient owns this state, not a `remember` call that happens to be open.
767        PENDING.with_borrow_mut(|pending| pending.retain(|key| *key != state.key));
768        let follow = state;
769        let subscription = crate::window::subscribe_window(
770            window,
771            Arc::new(move || follow.set(crate::window::window_size_of(window))),
772        );
773        composer.ambient.windows.insert(
774            window,
775            WindowAmbient {
776                state,
777                _subscription: subscription,
778            },
779        );
780        state
781    })
782}
783
784/// The design system the Renderer resolved the theme to, recorded as read.
785///
786/// Not how an application chooses a design system, and not a way to paint by hand: it is
787/// for the screen that is a different screen under a different system, such as a
788/// calculator whose keys differ.
789pub fn current_design_system() -> DesignSystem {
790    match with_composer_if_idle(|composer| composer.ambient.design) {
791        Some(state) => state.get(),
792        None => crate::design::design_system(),
793    }
794}
795
796/// Whether notifications may be shown, recorded as read.
797pub fn current_notification_permission() -> NotificationPermission {
798    match with_composer_if_idle(|composer| composer.ambient.permission) {
799        Some(state) => state.get(),
800        None => crate::notification::notification_permission(),
801    }
802}
803
804const NODE_SIZE: u64 = super::composer::call_site("compose_rust::remember_node_size", 0);
805const NOTIFICATION_ACTIVATED: u64 =
806    super::composer::call_site("compose_rust::on_notification_activated", 0);
807
808/// The slot a node size lives in: the state the screen reads, and the subscription that
809/// moves it. Leaving the composition drops both, and the measurement with them.
810struct NodeSizeSlot {
811    token: u32,
812    state: MutableState<WindowSize>,
813    _subscription: crate::window::NodeSizeSubscription,
814}
815
816/// Follows one node's measured size, recorded as read. Compose has no single equivalent;
817/// it is `onSizeChanged` with the class judgement made for you.
818///
819/// Give the token to the node with `Modifier.observe_size(size.token())`. Nothing is
820/// measured until a node carries it, and the composable reading it runs again only when
821/// the node's size class changes.
822pub fn remember_node_size() -> crate::window::NodeSize {
823    let _group = super::group_guard(NODE_SIZE);
824    let found = with_composer_if_idle(|composer| {
825        if !composer.composing() {
826            return None;
827        }
828        if let Some(slot) = composer.slot_mut::<NodeSizeSlot>() {
829            return Some((slot.token, slot.state));
830        }
831        let token = crate::window::next_node_token();
832        let capture = Capture::begin(Location::caller());
833        let state = mutable_state_of(crate::window::node_size(token));
834        let owned = capture.finish();
835        let follow = state;
836        let subscription = crate::window::subscribe_node(
837            token,
838            std::sync::Arc::new(move || follow.set(crate::window::node_size(token))),
839        );
840        composer.remember_new_owning(
841            NodeSizeSlot {
842                token,
843                state,
844                _subscription: subscription,
845            },
846            owned,
847        );
848        Some((token, state))
849    })
850    .flatten();
851    match found {
852        Some((token, state)) => {
853            // Read through the state, so this scope runs again when the class changes;
854            // the value itself is the module's.
855            let _ = state.get();
856            crate::window::NodeSize::of(token)
857        }
858        None => crate::window::NodeSize::of(0),
859    }
860}
861
862/// A notification handler, shared between the slot and the registration that calls it.
863type ActivationHandler = Rc<RefCell<Box<dyn FnMut(crate::NotificationActivation)>>>;
864
865/// The slot a notification handler lives in: the latest handler, and the registration
866/// that lasts as long as the slot does.
867struct ActivationSlot {
868    handler: ActivationHandler,
869    _subscription: crate::notification::ActivationSubscription,
870}
871
872/// Calls `handler` when the user presses a notification this application posted, for as
873/// long as this call site is in the composition.
874///
875/// The handler is replaced by the latest one on every composition, so it always sees the
876/// states it captured this time.
877pub fn on_notification_activated(handler: impl FnMut(crate::NotificationActivation) + 'static) {
878    let _group = super::group_guard(NOTIFICATION_ACTIVATED);
879    let handler: Box<dyn FnMut(crate::NotificationActivation)> = Box::new(handler);
880    let mut handler = Some(handler);
881    let old = with_composer_if_idle(|composer| {
882        if !composer.composing() {
883            return None;
884        }
885        if let Some(slot) = composer.slot_mut::<ActivationSlot>() {
886            let new = handler.take().expect("the handler is installed once");
887            return Some(std::mem::replace(&mut *slot.handler.borrow_mut(), new));
888        }
889        let shared = Rc::new(RefCell::new(
890            handler.take().expect("the handler is installed once"),
891        ));
892        let forward = Rc::clone(&shared);
893        let subscription = crate::notification::on_activation(Rc::new(
894            move |activation: crate::NotificationActivation| (*forward.borrow_mut())(activation),
895        ));
896        composer.remember_new(ActivationSlot {
897            handler: shared,
898            _subscription: subscription,
899        });
900        None
901    })
902    .flatten();
903    // The handler it replaced is dropped here, with the composer free.
904    drop(old);
905}
906
907const WINDOW_REQUESTED: u64 = super::composer::call_site("compose_rust::on_window_requested", 0);
908const APPLICATION_REOPEN: u64 =
909    super::composer::call_site("compose_rust::on_application_reopen", 0);
910
911/// The slot a registered handler lives in: the latest handler, and the registration that
912/// lasts as long as the slot does.
913type SharedHandler<A> = Rc<RefCell<Box<dyn FnMut(A)>>>;
914
915struct HandlerSlot<A: 'static> {
916    handler: SharedHandler<A>,
917    _subscription: Box<dyn std::any::Any>,
918}
919
920/// Registers `subscribe`'s handler once for this call site and replaces the closure it
921/// calls on every composition, so it always sees the states it captured this time. The
922/// registration ends when the call site leaves the composition.
923fn install_handler<A: 'static>(
924    key: u64,
925    handler: Box<dyn FnMut(A)>,
926    subscribe: impl FnOnce(Rc<dyn Fn(A)>) -> Box<dyn std::any::Any>,
927) {
928    let _group = super::group_guard(key);
929    let mut handler = Some(handler);
930    let mut subscribe = Some(subscribe);
931    let old = with_composer_if_idle(|composer| {
932        if !composer.composing() {
933            return None;
934        }
935        if let Some(slot) = composer.slot_mut::<HandlerSlot<A>>() {
936            let new = handler.take().expect("the handler is installed once");
937            return Some(std::mem::replace(&mut *slot.handler.borrow_mut(), new));
938        }
939        let shared = Rc::new(RefCell::new(
940            handler.take().expect("the handler is installed once"),
941        ));
942        let forward = Rc::clone(&shared);
943        let subscription = (subscribe.take().expect("the handler is installed once"))(Rc::new(
944            move |argument: A| (*forward.borrow_mut())(argument),
945        ));
946        composer.remember_new(HandlerSlot {
947            handler: shared,
948            _subscription: subscription,
949        });
950        None
951    })
952    .flatten();
953    // The handler it replaced is dropped here, with the composer free.
954    drop(old);
955}
956
957/// Calls `handler` for each window the system or the user made that this application has
958/// no `Window` node for, for as long as this call site is in the composition.
959/// The handler puts a node with that `RestoreKey` into the tree, or the Renderer closes the
960/// window.
961pub fn on_window_requested(handler: impl FnMut(crate::windows::WindowRequest) + 'static) {
962    install_handler::<crate::windows::WindowRequest>(
963        WINDOW_REQUESTED,
964        Box::new(handler),
965        |forward| Box::new(crate::windows::on_window_request(forward)),
966    );
967}
968
969/// Calls `handler` when the user opens the application again while it has no window, which
970/// is the Dock icon on macOS. The handler puts a `Window` node into the tree.
971pub fn on_application_reopen(handler: impl FnMut() + 'static) {
972    let mut handler = handler;
973    install_handler::<()>(
974        APPLICATION_REOPEN,
975        Box::new(move |(): ()| handler()),
976        |forward| {
977            Box::new(crate::windows::on_application_reopen_handler(Rc::new(
978                move || forward(()),
979            )))
980        },
981    );
982}