1use super::composer::Observer;
14use super::{with_composer, with_composer_if_idle};
15use crate::schema::{DesignSystem, NotificationPermission};
16use crate::window::WindowSize;
17use std::cell::{Cell, RefCell};
18use std::collections::HashMap;
19use std::fmt;
20use std::rc::{Rc, Weak};
21use std::sync::atomic::{AtomicU64, Ordering};
22use std::sync::{Arc, Mutex, MutexGuard};
23
24use super::composer::RuntimeSignal;
25
26static NEXT_STATE_ID: AtomicU64 = AtomicU64::new(1);
29
30fn next_state_id() -> u64 {
31 NEXT_STATE_ID.fetch_add(1, Ordering::Relaxed)
32}
33
34fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
35 mutex.lock().unwrap_or_else(|poison| poison.into_inner())
36}
37
38pub struct SnapshotMutationPolicy<T> {
40 equivalent: fn(&T, &T) -> bool,
41}
42
43impl<T> Clone for SnapshotMutationPolicy<T> {
44 fn clone(&self) -> Self {
45 *self
46 }
47}
48
49impl<T> Copy for SnapshotMutationPolicy<T> {}
50
51pub fn structural_equality_policy<T: PartialEq>() -> SnapshotMutationPolicy<T> {
53 SnapshotMutationPolicy {
54 equivalent: |left, right| left == right,
55 }
56}
57
58pub fn never_equal_policy<T>() -> SnapshotMutationPolicy<T> {
60 SnapshotMutationPolicy {
61 equivalent: |_, _| false,
62 }
63}
64
65struct StateCell<T> {
66 id: u64,
67 value: Mutex<T>,
68 policy: SnapshotMutationPolicy<T>,
69 runtimes: Mutex<Vec<std::sync::Weak<RuntimeSignal>>>,
71}
72
73pub struct MutableState<T> {
86 cell: Arc<StateCell<T>>,
87}
88
89impl<T> Clone for MutableState<T> {
90 fn clone(&self) -> Self {
91 Self {
92 cell: Arc::clone(&self.cell),
93 }
94 }
95}
96
97impl<T> PartialEq for MutableState<T> {
98 fn eq(&self, other: &Self) -> bool {
99 Arc::ptr_eq(&self.cell, &other.cell)
100 }
101}
102
103impl<T> Eq for MutableState<T> {}
104
105impl<T: fmt::Debug> fmt::Debug for MutableState<T> {
106 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
107 formatter
108 .debug_tuple("MutableState")
109 .field(&*lock(&self.cell.value))
110 .finish()
111 }
112}
113
114pub fn mutable_state_of<T: PartialEq + 'static>(value: T) -> MutableState<T> {
122 mutable_state_of_with_policy(value, structural_equality_policy())
123}
124
125pub fn mutable_state_of_with_policy<T: 'static>(
127 value: T,
128 policy: SnapshotMutationPolicy<T>,
129) -> MutableState<T> {
130 MutableState {
131 cell: Arc::new(StateCell {
132 id: next_state_id(),
133 value: Mutex::new(value),
134 policy,
135 runtimes: Mutex::new(Vec::new()),
136 }),
137 }
138}
139
140impl<T: 'static> MutableState<T> {
141 pub fn get(&self) -> T
143 where
144 T: Clone,
145 {
146 self.track();
147 lock(&self.cell.value).clone()
148 }
149
150 pub fn get_untracked(&self) -> T
153 where
154 T: Clone,
155 {
156 lock(&self.cell.value).clone()
157 }
158
159 pub fn with<R>(&self, read: impl FnOnce(&T) -> R) -> R {
161 self.track();
162 read(&lock(&self.cell.value))
163 }
164
165 pub fn set(&self, value: T) {
167 {
168 let mut current = lock(&self.cell.value);
169 if (self.cell.policy.equivalent)(¤t, &value) {
170 return;
171 }
172 *current = value;
173 }
174 self.notify();
175 }
176
177 pub fn update(&self, change: impl FnOnce(&mut T)) {
180 change(&mut lock(&self.cell.value));
181 self.notify();
182 }
183
184 pub fn as_state(&self) -> MutableState<T> {
186 self.clone()
187 }
188
189 fn track(&self) {
190 let fresh = with_composer_if_idle(|composer| {
191 composer
192 .record_read(self.cell.id)
193 .then(|| Arc::clone(&composer.signal))
194 })
195 .flatten();
196 if let Some(signal) = fresh {
197 let mut runtimes = lock(&self.cell.runtimes);
198 if !runtimes
199 .iter()
200 .any(|known| std::ptr::eq(known.as_ptr(), Arc::as_ptr(&signal)))
201 {
202 runtimes.push(Arc::downgrade(&signal));
203 }
204 }
205 }
206
207 fn notify(&self) {
208 let mut told = false;
209 lock(&self.cell.runtimes).retain(|runtime| match runtime.upgrade() {
210 Some(signal) => {
211 signal.push_dirty(self.cell.id);
212 told = true;
213 true
214 }
215 None => false,
216 });
217 if told && !crate::boundary::in_host_call() {
220 crate::boundary::request_frame_from_worker();
221 }
222 }
223}
224
225pub trait State<T> {
227 fn value(&self) -> T;
229}
230
231impl<T: Clone + 'static> State<T> for MutableState<T> {
232 fn value(&self) -> T {
233 self.get()
234 }
235}
236
237impl<T: Clone + PartialEq + 'static> State<T> for DerivedState<T> {
238 fn value(&self) -> T {
239 self.get()
240 }
241}
242
243pub(crate) trait DerivedNode {
245 fn refresh(&self) -> bool;
247 fn mark_dirty(&self);
249}
250
251struct DerivedCell<T> {
252 id: u64,
253 compute: Box<dyn Fn() -> T>,
254 value: RefCell<Option<T>>,
255 dirty: Cell<bool>,
256}
257
258pub struct DerivedState<T> {
268 cell: Rc<DerivedCell<T>>,
269}
270
271impl<T> Clone for DerivedState<T> {
272 fn clone(&self) -> Self {
273 Self {
274 cell: Rc::clone(&self.cell),
275 }
276 }
277}
278
279impl<T> PartialEq for DerivedState<T> {
280 fn eq(&self, other: &Self) -> bool {
281 Rc::ptr_eq(&self.cell, &other.cell)
282 }
283}
284
285pub fn derived_state_of<T: Clone + PartialEq + 'static>(
292 calculation: impl Fn() -> T + 'static,
293) -> DerivedState<T> {
294 DerivedState {
295 cell: Rc::new(DerivedCell {
296 id: next_state_id(),
297 compute: Box::new(calculation),
298 value: RefCell::new(None),
299 dirty: Cell::new(true),
300 }),
301 }
302}
303
304impl<T: Clone + PartialEq + 'static> DerivedState<T> {
305 pub fn get(&self) -> T {
307 let in_runtime = with_composer_if_idle(|_| ()).is_some();
308 if !in_runtime {
309 return (self.cell.compute)();
312 }
313 if self.cell.dirty.get() || self.cell.value.borrow().is_none() {
314 self.recompute();
315 }
316 with_composer_if_idle(|composer| composer.record_read(self.cell.id));
317 self.cell
318 .value
319 .borrow()
320 .clone()
321 .expect("a derived state holds a value once computed")
322 }
323
324 fn recompute(&self) -> bool {
325 let id = self.cell.id;
326 let node: Rc<dyn DerivedNode> = self.cell.clone();
327 let weak = Rc::downgrade(&node);
328 with_composer(|composer| {
329 if let Some(deps) = composer.derived_deps.remove(&id) {
330 for dep in deps {
331 composer.unobserve(dep, Observer::Derived(id));
332 }
333 }
334 composer.ambient.deriveds.insert(id, weak);
335 composer.derived_stack.push(id);
336 });
337 let computed = (self.cell.compute)();
338 with_composer(|composer| {
339 composer.derived_stack.pop();
340 });
341 self.cell.dirty.set(false);
342 let mut stored = self.cell.value.borrow_mut();
343 let changed = stored.as_ref() != Some(&computed);
344 *stored = Some(computed);
345 changed
346 }
347}
348
349impl<T: Clone + PartialEq + 'static> DerivedNode for DerivedCell<T> {
350 fn refresh(&self) -> bool {
351 let state = DerivedState {
353 cell: rc_from_cell(self),
354 };
355 state.recompute()
356 }
357
358 fn mark_dirty(&self) {
359 self.dirty.set(true);
360 }
361}
362
363fn rc_from_cell<T>(cell: &DerivedCell<T>) -> Rc<DerivedCell<T>> {
367 let pointer = cell as *const DerivedCell<T>;
368 unsafe {
372 Rc::increment_strong_count(pointer);
373 Rc::from_raw(pointer)
374 }
375}
376
377pub(crate) struct Ambient {
380 pub(crate) window: MutableState<WindowSize>,
381 pub(crate) design: MutableState<DesignSystem>,
382 pub(crate) permission: MutableState<NotificationPermission>,
383 pub(crate) windows: HashMap<u32, WindowAmbient>,
386 pub(crate) deriveds: HashMap<u64, Weak<dyn DerivedNode>>,
387}
388
389pub(crate) struct WindowAmbient {
391 state: MutableState<WindowSize>,
392 _subscription: crate::window::WindowSubscription,
393}
394
395impl Ambient {
396 pub(crate) fn new() -> Self {
397 Self {
398 window: mutable_state_of(crate::window::window_size()),
399 design: mutable_state_of(crate::design::design_system()),
400 permission: mutable_state_of(crate::notification::notification_permission()),
401 windows: HashMap::new(),
402 deriveds: HashMap::new(),
403 }
404 }
405}
406
407pub(crate) fn apply_state_changes() {
414 loop {
415 let Some(mut changed) = with_composer(|composer| {
416 let mut scratch = std::mem::take(&mut composer.dirty_scratch);
417 std::mem::swap(&mut *lock(&composer.signal.dirty), &mut scratch);
418 scratch
419 }) else {
420 return;
421 };
422 if changed.is_empty() {
423 with_composer(|composer| composer.dirty_scratch = changed);
424 return;
425 }
426 let mut index = 0;
427 while index < changed.len() {
428 let state = changed[index];
429 index += 1;
430 let observers = with_composer(|composer| {
431 composer.observers.get(&state).cloned().unwrap_or_default()
432 })
433 .unwrap_or_default();
434 for observer in observers {
435 match observer {
436 Observer::Scope(group, generation) => {
437 with_composer(|composer| composer.invalidate(group, generation));
438 }
439 Observer::Derived(derived) => {
440 let (node, watched) = with_composer(|composer| {
441 let node = composer
442 .ambient
443 .deriveds
444 .get(&derived)
445 .and_then(Weak::upgrade);
446 let watched = composer
447 .observers
448 .get(&derived)
449 .is_some_and(|list| !list.is_empty());
450 (node, watched)
451 })
452 .unwrap_or((None, false));
453 let Some(node) = node else {
454 continue;
455 };
456 if watched {
457 if node.refresh() && !changed.contains(&derived) {
458 changed.push(derived);
459 }
460 } else {
461 node.mark_dirty();
462 }
463 }
464 }
465 }
466 }
467 changed.clear();
468 with_composer(|composer| composer.dirty_scratch = changed);
469 }
470}
471
472pub fn current_window_size() -> WindowSize {
478 let window = crate::windows::current_window_id();
479 if window != 0 {
480 if let Some(state) = window_state(window) {
481 return state.get();
482 }
483 }
484 match with_composer_if_idle(|composer| composer.ambient.window.clone()) {
485 Some(state) => state.get(),
486 None => crate::window::window_size(),
487 }
488}
489
490fn window_state(window: u32) -> Option<MutableState<WindowSize>> {
492 with_composer_if_idle(|composer| {
493 if let Some(entry) = composer.ambient.windows.get(&window) {
494 return entry.state.clone();
495 }
496 let state = mutable_state_of(crate::window::window_size_of(window));
497 let follow = state.clone();
498 let subscription = crate::window::subscribe_window(
499 window,
500 Arc::new(move || follow.set(crate::window::window_size_of(window))),
501 );
502 composer.ambient.windows.insert(
503 window,
504 WindowAmbient {
505 state: state.clone(),
506 _subscription: subscription,
507 },
508 );
509 state
510 })
511}
512
513pub fn current_design_system() -> DesignSystem {
519 match with_composer_if_idle(|composer| composer.ambient.design.clone()) {
520 Some(state) => state.get(),
521 None => crate::design::design_system(),
522 }
523}
524
525pub fn current_notification_permission() -> NotificationPermission {
527 match with_composer_if_idle(|composer| composer.ambient.permission.clone()) {
528 Some(state) => state.get(),
529 None => crate::notification::notification_permission(),
530 }
531}
532
533const NODE_SIZE: u64 = super::composer::call_site("compose_rust::remember_node_size", 0);
534const NOTIFICATION_ACTIVATED: u64 =
535 super::composer::call_site("compose_rust::on_notification_activated", 0);
536
537struct NodeSizeSlot {
540 token: u32,
541 state: MutableState<WindowSize>,
542 _subscription: crate::window::NodeSizeSubscription,
543}
544
545pub fn remember_node_size() -> crate::window::NodeSize {
552 let _group = super::group_guard(NODE_SIZE);
553 let found = with_composer_if_idle(|composer| {
554 if !composer.composing() {
555 return None;
556 }
557 if let Some(slot) = composer.slot_mut::<NodeSizeSlot>() {
558 return Some((slot.token, slot.state.clone()));
559 }
560 let token = crate::window::next_node_token();
561 let state = mutable_state_of(crate::window::node_size(token));
562 let follow = state.clone();
563 let subscription = crate::window::subscribe_node(
564 token,
565 std::sync::Arc::new(move || follow.set(crate::window::node_size(token))),
566 );
567 composer.remember_new(NodeSizeSlot {
568 token,
569 state: state.clone(),
570 _subscription: subscription,
571 });
572 Some((token, state))
573 })
574 .flatten();
575 match found {
576 Some((token, state)) => {
577 let _ = state.get();
580 crate::window::NodeSize::of(token)
581 }
582 None => crate::window::NodeSize::of(0),
583 }
584}
585
586type ActivationHandler = Rc<RefCell<Box<dyn FnMut(crate::NotificationActivation)>>>;
588
589struct ActivationSlot {
592 handler: ActivationHandler,
593 _subscription: crate::notification::ActivationSubscription,
594}
595
596pub fn on_notification_activated(handler: impl FnMut(crate::NotificationActivation) + 'static) {
602 let _group = super::group_guard(NOTIFICATION_ACTIVATED);
603 let handler: Box<dyn FnMut(crate::NotificationActivation)> = Box::new(handler);
604 let mut handler = Some(handler);
605 let old = with_composer_if_idle(|composer| {
606 if !composer.composing() {
607 return None;
608 }
609 if let Some(slot) = composer.slot_mut::<ActivationSlot>() {
610 let new = handler.take().expect("the handler is installed once");
611 return Some(std::mem::replace(&mut *slot.handler.borrow_mut(), new));
612 }
613 let shared = Rc::new(RefCell::new(
614 handler.take().expect("the handler is installed once"),
615 ));
616 let forward = Rc::clone(&shared);
617 let subscription = crate::notification::on_activation(Rc::new(
618 move |activation: crate::NotificationActivation| (*forward.borrow_mut())(activation),
619 ));
620 composer.remember_new(ActivationSlot {
621 handler: shared,
622 _subscription: subscription,
623 });
624 None
625 })
626 .flatten();
627 drop(old);
629}
630
631const WINDOW_REQUESTED: u64 = super::composer::call_site("compose_rust::on_window_requested", 0);
632const APPLICATION_REOPEN: u64 =
633 super::composer::call_site("compose_rust::on_application_reopen", 0);
634
635type SharedHandler<A> = Rc<RefCell<Box<dyn FnMut(A)>>>;
638
639struct HandlerSlot<A: 'static> {
640 handler: SharedHandler<A>,
641 _subscription: Box<dyn std::any::Any>,
642}
643
644fn install_handler<A: 'static>(
648 key: u64,
649 handler: Box<dyn FnMut(A)>,
650 subscribe: impl FnOnce(Rc<dyn Fn(A)>) -> Box<dyn std::any::Any>,
651) {
652 let _group = super::group_guard(key);
653 let mut handler = Some(handler);
654 let mut subscribe = Some(subscribe);
655 let old = with_composer_if_idle(|composer| {
656 if !composer.composing() {
657 return None;
658 }
659 if let Some(slot) = composer.slot_mut::<HandlerSlot<A>>() {
660 let new = handler.take().expect("the handler is installed once");
661 return Some(std::mem::replace(&mut *slot.handler.borrow_mut(), new));
662 }
663 let shared = Rc::new(RefCell::new(
664 handler.take().expect("the handler is installed once"),
665 ));
666 let forward = Rc::clone(&shared);
667 let subscription = (subscribe.take().expect("the handler is installed once"))(Rc::new(
668 move |argument: A| (*forward.borrow_mut())(argument),
669 ));
670 composer.remember_new(HandlerSlot {
671 handler: shared,
672 _subscription: subscription,
673 });
674 None
675 })
676 .flatten();
677 drop(old);
679}
680
681pub fn on_window_requested(handler: impl FnMut(crate::windows::WindowRequest) + 'static) {
686 install_handler::<crate::windows::WindowRequest>(
687 WINDOW_REQUESTED,
688 Box::new(handler),
689 |forward| Box::new(crate::windows::on_window_request(forward)),
690 );
691}
692
693pub fn on_application_reopen(handler: impl FnMut() + 'static) {
696 let mut handler = handler;
697 install_handler::<()>(
698 APPLICATION_REOPEN,
699 Box::new(move |(): ()| handler()),
700 |forward| {
701 Box::new(crate::windows::on_application_reopen_handler(Rc::new(
702 move || forward(()),
703 )))
704 },
705 );
706}