1use super::arena::NodeId;
2use super::runtime::WindowToken;
3use super::scope::{ScopeArena, ScopeError, ScopeId, ScopeState};
4use crate::element::{
5 Callback, CallbackSource, ColorScheme, IntoPayloadCallback, View, WindowSize, WindowVisuals,
6};
7use crate::reference::{HostRequest, WindowEndpoint, WindowRef};
8use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
9use std::any::{Any, TypeId};
10use std::cell::RefCell;
11use std::collections::VecDeque;
12use std::fmt;
13use std::marker::PhantomData;
14use std::mem::size_of;
15use std::rc::Rc;
16use std::sync::atomic::{AtomicU64, Ordering};
17use std::sync::{Arc, Mutex, Weak};
18
19pub(crate) const BACKGROUND_MESSAGE_QUEUE_CAPACITY: usize = 4_096;
20pub(crate) const BACKGROUND_TASK_CAPACITY: usize = 64;
21pub(crate) const LOCAL_MESSAGE_QUEUE_CAPACITY: usize = 4_096;
22#[derive(Clone, Debug, Eq, Hash, PartialEq)]
26pub struct EffectKey(EffectKeyKind);
27
28#[derive(Clone, Debug, Eq, Hash, PartialEq)]
29enum EffectKeyKind {
30 Integer(u64),
31 String(Rc<str>),
32}
33
34impl From<u64> for EffectKey {
35 fn from(value: u64) -> Self {
36 Self(EffectKeyKind::Integer(value))
37 }
38}
39
40impl From<u32> for EffectKey {
41 fn from(value: u32) -> Self {
42 Self(EffectKeyKind::Integer(value.into()))
43 }
44}
45
46impl From<usize> for EffectKey {
47 fn from(value: usize) -> Self {
48 Self(EffectKeyKind::Integer(u64::try_from(value).unwrap()))
49 }
50}
51
52impl From<String> for EffectKey {
53 fn from(value: String) -> Self {
54 Self(EffectKeyKind::String(value.into()))
55 }
56}
57
58impl From<&str> for EffectKey {
59 fn from(value: &str) -> Self {
60 Self(EffectKeyKind::String(value.into()))
61 }
62}
63
64#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
65pub(crate) struct ContextId(u64);
66
67static NEXT_CONTEXT_ID: AtomicU64 = AtomicU64::new(1);
68#[derive(Clone, Debug)]
72pub struct Context<T> {
73 default: T,
74 id: ContextId,
75}
76
77impl<T> Context<T> {
78 pub fn new(default: T) -> Self {
80 Self {
81 default,
82 id: ContextId(NEXT_CONTEXT_ID.fetch_add(1, Ordering::Relaxed)),
83 }
84 }
85
86 #[cfg(test)]
87 pub(crate) fn id(&self) -> ContextId {
88 self.id
89 }
90}
91
92impl<T> PartialEq for Context<T> {
93 fn eq(&self, other: &Self) -> bool {
94 self.id == other.id
95 }
96}
97
98impl<T> Eq for Context<T> {}
99
100#[derive(Clone)]
101pub(crate) struct ContextProvision {
102 pub(crate) id: ContextId,
103 pub(crate) value: Rc<dyn Any>,
104 value_type: TypeId,
105 equals: fn(&dyn Any, &dyn Any) -> bool,
106}
107
108impl ContextProvision {
109 pub(crate) fn new<T: Clone + PartialEq + 'static>(context: &Context<T>, value: T) -> Self {
110 Self {
111 id: context.id,
112 value: Rc::new(value),
113 value_type: TypeId::of::<T>(),
114 equals: |left, right| {
115 left.downcast_ref::<T>()
116 .zip(right.downcast_ref::<T>())
117 .is_some_and(|(left, right)| left == right)
118 },
119 }
120 }
121}
122
123impl fmt::Debug for ContextProvision {
124 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
125 formatter
126 .debug_struct("ContextProvision")
127 .field("id", &self.id)
128 .field("value_type", &self.value_type)
129 .finish()
130 }
131}
132
133impl PartialEq for ContextProvision {
134 fn eq(&self, other: &Self) -> bool {
135 self.id == other.id
136 && self.value_type == other.value_type
137 && (Rc::ptr_eq(&self.value, &other.value)
138 || (self.equals)(self.value.as_ref(), other.value.as_ref()))
139 }
140}
141
142#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
143pub(crate) struct ContextDependency {
144 pub(crate) id: ContextId,
145 pub(crate) provider: Option<NodeId>,
146}
147
148#[derive(Clone, Default)]
149pub(crate) enum ContextDependencies {
150 #[default]
151 Empty,
152 One(ContextDependency),
153 Many(HashSet<ContextDependency>),
154}
155
156impl ContextDependencies {
157 pub(crate) fn contains(&self, dependency: &ContextDependency) -> bool {
158 match self {
159 Self::Empty => false,
160 Self::One(value) => value == dependency,
161 Self::Many(values) => values.contains(dependency),
162 }
163 }
164
165 fn insert(&mut self, dependency: ContextDependency) {
166 match self {
167 Self::Empty => *self = Self::One(dependency),
168 Self::One(value) if *value == dependency => {}
169 Self::One(value) => {
170 let mut values = HashSet::default();
171 values.insert(*value);
172 values.insert(dependency);
173 *self = Self::Many(values);
174 }
175 Self::Many(values) => {
176 values.insert(dependency);
177 }
178 }
179 }
180
181 fn is_empty(&self) -> bool {
182 matches!(self, Self::Empty)
183 }
184
185 pub(crate) fn iter(&self) -> ContextDependencyIter<'_> {
186 match self {
187 Self::Empty => ContextDependencyIter::Empty,
188 Self::One(value) => ContextDependencyIter::One(Some(value)),
189 Self::Many(values) => ContextDependencyIter::Many(values.iter()),
190 }
191 }
192
193 fn len(&self) -> usize {
194 match self {
195 Self::Empty => 0,
196 Self::One(_) => 1,
197 Self::Many(values) => values.len(),
198 }
199 }
200}
201
202impl PartialEq for ContextDependencies {
203 fn eq(&self, other: &Self) -> bool {
204 self.len() == other.len() && self.iter().all(|value| other.contains(value))
205 }
206}
207
208impl Eq for ContextDependencies {}
209
210pub(crate) enum ContextDependencyIter<'a> {
211 Empty,
212 One(Option<&'a ContextDependency>),
213 Many(std::collections::hash_set::Iter<'a, ContextDependency>),
214}
215
216impl<'a> Iterator for ContextDependencyIter<'a> {
217 type Item = &'a ContextDependency;
218
219 fn next(&mut self) -> Option<Self::Item> {
220 match self {
221 Self::Empty => None,
222 Self::One(value) => value.take(),
223 Self::Many(values) => values.next(),
224 }
225 }
226}
227
228#[derive(Clone, Default)]
229enum ContextValues {
230 #[default]
231 Empty,
232 One {
233 id: ContextId,
234 provider: NodeId,
235 value_type: TypeId,
236 value: Rc<dyn Any>,
237 },
238 Many(HashMap<ContextId, (NodeId, TypeId, Rc<dyn Any>)>),
239}
240
241#[derive(Clone, Default)]
242pub(crate) struct ContextSnapshot {
243 values: ContextValues,
244}
245
246impl ContextSnapshot {
247 pub(crate) fn insert(&mut self, provider: NodeId, provision: &ContextProvision) {
248 match &mut self.values {
249 ContextValues::Empty => {
250 self.values = ContextValues::One {
251 id: provision.id,
252 provider,
253 value_type: provision.value_type,
254 value: Rc::clone(&provision.value),
255 };
256 }
257 ContextValues::One { id, .. } if *id == provision.id => {}
258 ContextValues::One { .. } => {
259 let ContextValues::One {
260 id,
261 provider: previous_provider,
262 value_type,
263 value,
264 } = std::mem::take(&mut self.values)
265 else {
266 unreachable!()
267 };
268 let mut values = HashMap::default();
269 values.insert(id, (previous_provider, value_type, value));
270 values.insert(
271 provision.id,
272 (provider, provision.value_type, Rc::clone(&provision.value)),
273 );
274 self.values = ContextValues::Many(values);
275 }
276 ContextValues::Many(values) => {
277 values.entry(provision.id).or_insert_with(|| {
278 (provider, provision.value_type, Rc::clone(&provision.value))
279 });
280 }
281 }
282 }
283
284 fn get<T: Clone + 'static>(&self, context: &Context<T>) -> Option<(NodeId, T)> {
285 let (provider, value_type, value) = match &self.values {
286 ContextValues::Empty => return None,
287 ContextValues::One {
288 id,
289 provider,
290 value_type,
291 value,
292 } if *id == context.id => (provider, value_type, value),
293 ContextValues::One { .. } => return None,
294 ContextValues::Many(values) => {
295 let (provider, value_type, value) = values.get(&context.id)?;
296 (provider, value_type, value)
297 }
298 };
299 assert_eq!(*value_type, TypeId::of::<T>(), "context type mismatch");
300 Some((*provider, value.downcast_ref::<T>().unwrap().clone()))
301 }
302}
303
304#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
305pub(crate) struct ComponentToken {
306 window: WindowToken,
307 scope: ScopeId,
308}
309
310impl ComponentToken {
311 pub(crate) fn scope(self) -> ScopeId {
312 self.scope
313 }
314}
315
316#[derive(Clone, Debug, Eq, PartialEq)]
318pub enum ComponentDeclarationError {
319 EffectKey(EffectKey),
321 ColorSchemeObservation,
323 WindowSizeObservation,
325 WindowTitle,
327 WindowVisuals,
329}
330
331struct MessageEnvelope {
332 control: Option<Arc<TaskControl>>,
333 token: ComponentToken,
334 payload: Box<dyn Any>,
335}
336
337struct BackgroundEnvelope {
338 control: Arc<TaskControl>,
339 delivery: BackgroundDelivery,
340 payload: Box<dyn Any + Send>,
341 rejection: Option<Box<dyn Any + Send>>,
342 token: ComponentToken,
343}
344
345#[derive(Clone, Copy, Eq, PartialEq)]
346enum BackgroundDelivery {
347 Completion,
348 Rejection,
349}
350
351impl BackgroundEnvelope {
352 fn reject(mut self) -> Option<Self> {
353 if self.delivery == BackgroundDelivery::Rejection {
354 return Some(self);
355 }
356 if !self.control.reject() {
357 return None;
358 }
359 self.payload = self.rejection.take()?;
360 self.delivery = BackgroundDelivery::Rejection;
361 Some(self)
362 }
363}
364
365enum PendingEnvelope {
366 Background(BackgroundEnvelope),
367 Local(MessageEnvelope),
368}
369
370impl PendingEnvelope {
371 fn control(&self) -> Option<&Arc<TaskControl>> {
372 match self {
373 Self::Background(envelope) => Some(&envelope.control),
374 Self::Local(envelope) => envelope.control.as_ref(),
375 }
376 }
377
378 fn token(&self) -> ComponentToken {
379 match self {
380 Self::Background(envelope) => envelope.token,
381 Self::Local(envelope) => envelope.token,
382 }
383 }
384}
385
386struct BackgroundQueue {
387 envelopes: VecDeque<BackgroundEnvelope>,
388 open: bool,
389 tasks: HashMap<ScopeId, Vec<Weak<TaskControl>>>,
390 wake: Option<Arc<dyn Fn() -> bool + Send + Sync>>,
391 wake_pending: bool,
392}
393
394#[derive(Clone)]
395struct TaskSpawner {
396 limiter: Arc<TaskLimiter>,
397 queue: Arc<Mutex<BackgroundQueue>>,
398 token: ComponentToken,
399}
400
401#[derive(Default)]
402struct TaskLimiter {
403 active: std::sync::atomic::AtomicUsize,
404}
405
406impl TaskLimiter {
407 fn acquire(self: &Arc<Self>) -> Option<TaskSlot> {
408 self.active
409 .try_update(Ordering::AcqRel, Ordering::Acquire, |active| {
410 (active < BACKGROUND_TASK_CAPACITY).then_some(active + 1)
411 })
412 .ok()
413 .map(|_| TaskSlot(Arc::clone(self)))
414 }
415}
416
417struct TaskSlot(Arc<TaskLimiter>);
418
419impl Drop for TaskSlot {
420 fn drop(&mut self) {
421 self.0.active.fetch_sub(1, Ordering::AcqRel);
422 }
423}
424
425#[derive(Clone, Debug)]
427pub struct CancellationToken {
428 control: Arc<TaskControl>,
429}
430
431impl CancellationToken {
432 pub fn is_cancelled(&self) -> bool {
434 self.control.status() == ComponentTaskStatus::Cancelled
435 }
436}
437
438#[derive(Clone, Copy, Debug, Eq, PartialEq)]
440#[repr(u8)]
441pub enum ComponentTaskStatus {
442 Running,
444 Queued,
446 Delivered,
448 Cancelled,
450 Rejected,
452}
453
454#[derive(Debug)]
455struct TaskControl {
456 status: std::sync::atomic::AtomicU8,
457}
458
459impl TaskControl {
460 fn new() -> Self {
461 Self {
462 status: std::sync::atomic::AtomicU8::new(ComponentTaskStatus::Running as u8),
463 }
464 }
465
466 fn queue(&self) -> bool {
467 self.status
468 .compare_exchange(
469 ComponentTaskStatus::Running as u8,
470 ComponentTaskStatus::Queued as u8,
471 Ordering::AcqRel,
472 Ordering::Acquire,
473 )
474 .is_ok()
475 }
476
477 fn deliver(&self) -> bool {
478 self.status
479 .compare_exchange(
480 ComponentTaskStatus::Queued as u8,
481 ComponentTaskStatus::Delivered as u8,
482 Ordering::AcqRel,
483 Ordering::Acquire,
484 )
485 .is_ok()
486 }
487
488 fn reject(&self) -> bool {
489 self.finish(ComponentTaskStatus::Rejected)
490 }
491
492 fn finish(&self, status: ComponentTaskStatus) -> bool {
493 let mut current = self.status.load(Ordering::Acquire);
494 while current == ComponentTaskStatus::Running as u8
495 || current == ComponentTaskStatus::Queued as u8
496 {
497 match self.status.compare_exchange_weak(
498 current,
499 status as u8,
500 Ordering::AcqRel,
501 Ordering::Acquire,
502 ) {
503 Ok(_) => return true,
504 Err(actual) => current = actual,
505 }
506 }
507 false
508 }
509
510 fn status(&self) -> ComponentTaskStatus {
511 match self.status.load(Ordering::Acquire) {
512 0 => ComponentTaskStatus::Running,
513 1 => ComponentTaskStatus::Queued,
514 2 => ComponentTaskStatus::Delivered,
515 3 => ComponentTaskStatus::Cancelled,
516 4 => ComponentTaskStatus::Rejected,
517 _ => unreachable!(),
518 }
519 }
520
521 fn cancel(&self) {
522 self.finish(ComponentTaskStatus::Cancelled);
523 }
524}
525
526#[derive(Clone)]
531pub struct ComponentTask {
532 control: Arc<TaskControl>,
533 queue: Arc<Mutex<BackgroundQueue>>,
534 token: ComponentToken,
535}
536
537impl ComponentTask {
538 pub fn cancel(&self) {
540 self.control.cancel();
541 let mut queue = self.queue.lock().unwrap();
542 queue
543 .envelopes
544 .retain(|envelope| !Arc::ptr_eq(&envelope.control, &self.control));
545 }
546
547 pub fn is_cancelled(&self) -> bool {
549 self.status() == ComponentTaskStatus::Cancelled
550 }
551
552 pub fn status(&self) -> ComponentTaskStatus {
554 self.control.status()
555 }
556}
557
558impl fmt::Debug for ComponentTask {
559 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
560 formatter
561 .debug_struct("ComponentTask")
562 .field("status", &self.status())
563 .field("token", &self.token)
564 .finish()
565 }
566}
567
568impl TaskSpawner {
569 fn spawn<M, F>(&self, work: F) -> ComponentTask
570 where
571 M: Send + 'static,
572 F: FnOnce(CancellationToken) -> M + Send + 'static,
573 {
574 self.spawn_inner(work, None)
575 }
576
577 fn spawn_with_rejection<M, F>(&self, work: F, rejection: M) -> ComponentTask
578 where
579 M: Send + 'static,
580 F: FnOnce(CancellationToken) -> M + Send + 'static,
581 {
582 self.spawn_inner(work, Some(Box::new(rejection)))
583 }
584
585 fn spawn_inner<M, F>(&self, work: F, rejection: Option<Box<dyn Any + Send>>) -> ComponentTask
586 where
587 M: Send + 'static,
588 F: FnOnce(CancellationToken) -> M + Send + 'static,
589 {
590 let control = Arc::new(TaskControl::new());
591 let rejection = Arc::new(Mutex::new(rejection));
592 let task = ComponentTask {
593 control: Arc::clone(&control),
594 queue: Arc::clone(&self.queue),
595 token: self.token,
596 };
597 let Some(slot) = self.limiter.acquire() else {
598 self.queue_rejection(&control, rejection.lock().unwrap().take());
599 return task;
600 };
601 {
602 let mut queue = self.queue.lock().unwrap();
603 if !queue.open {
604 control.reject();
605 return task;
606 }
607 queue
608 .tasks
609 .entry(self.token.scope)
610 .or_default()
611 .push(Arc::downgrade(&control));
612 }
613 let queue = Arc::clone(&self.queue);
614 let token = self.token;
615 let thread_control = Arc::clone(&control);
616 let thread_rejection = Arc::clone(&rejection);
617 windows_threading::submit(move || {
618 let _slot = slot;
619 let message = work(CancellationToken {
620 control: Arc::clone(&thread_control),
621 });
622 let wake = {
623 let mut background = queue.lock().unwrap();
624 let registered = background.tasks.get_mut(&token.scope).is_some_and(|tasks| {
625 let before = tasks.len();
626 tasks.retain(|task| {
627 task.upgrade()
628 .is_some_and(|task| !Arc::ptr_eq(&task, &thread_control))
629 });
630 tasks.len() != before
631 });
632 if background
633 .tasks
634 .get(&token.scope)
635 .is_some_and(Vec::is_empty)
636 {
637 background.tasks.remove(&token.scope);
638 }
639 if !registered
640 || thread_control.status() == ComponentTaskStatus::Cancelled
641 || !background.open
642 {
643 thread_control.cancel();
644 return;
645 }
646 if background.envelopes.len() >= BACKGROUND_MESSAGE_QUEUE_CAPACITY {
647 drop(background);
648 Self::queue_rejection_shared(
649 &queue,
650 &thread_control,
651 token,
652 thread_rejection.lock().unwrap().take(),
653 );
654 return;
655 }
656 if !thread_control.queue() {
657 return;
658 }
659 background.envelopes.push_back(BackgroundEnvelope {
660 control: Arc::clone(&thread_control),
661 delivery: BackgroundDelivery::Completion,
662 payload: Box::new(message),
663 rejection: thread_rejection.lock().unwrap().take(),
664 token,
665 });
666 Self::background_wake(&mut background)
667 };
668 Self::wake_or_reject(&queue, wake);
669 });
670 task
671 }
672
673 fn background_wake(queue: &mut BackgroundQueue) -> Option<Arc<dyn Fn() -> bool + Send + Sync>> {
674 let wake = (!queue.wake_pending).then(|| queue.wake.clone()).flatten();
675 queue.wake_pending |= wake.is_some();
676 wake
677 }
678
679 fn queue_rejection(&self, control: &Arc<TaskControl>, rejection: Option<Box<dyn Any + Send>>) {
680 Self::queue_rejection_shared(&self.queue, control, self.token, rejection);
681 }
682
683 fn queue_rejection_shared(
684 queue: &Arc<Mutex<BackgroundQueue>>,
685 control: &Arc<TaskControl>,
686 token: ComponentToken,
687 rejection: Option<Box<dyn Any + Send>>,
688 ) {
689 let Some(payload) = rejection else {
690 control.reject();
691 return;
692 };
693 let mut background = queue.lock().unwrap();
694 if !background.open {
695 control.reject();
696 return;
697 }
698 if !control.reject() {
699 return;
700 }
701 background.envelopes.push_back(BackgroundEnvelope {
702 control: Arc::clone(control),
703 delivery: BackgroundDelivery::Rejection,
704 payload,
705 rejection: None,
706 token,
707 });
708 let wake = Self::background_wake(&mut background);
709 drop(background);
710 Self::wake_or_reject(queue, wake);
711 }
712
713 fn wake_or_reject(
714 queue: &Arc<Mutex<BackgroundQueue>>,
715 wake: Option<Arc<dyn Fn() -> bool + Send + Sync>>,
716 ) {
717 if wake.is_some_and(|wake| !wake()) {
718 let mut queue = queue.lock().unwrap();
719 queue.wake_pending = false;
720 queue.envelopes = queue
721 .envelopes
722 .drain(..)
723 .filter_map(BackgroundEnvelope::reject)
724 .collect();
725 }
726 }
727}
728
729struct ComponentQueue {
730 active: HashSet<ScopeId>,
731 envelopes: VecDeque<MessageEnvelope>,
732 open: bool,
733 wake: Option<Rc<dyn Fn()>>,
734}
735
736pub struct LocalSender<M> {
741 queue: Rc<RefCell<ComponentQueue>>,
742 token: ComponentToken,
743 marker: PhantomData<fn(M)>,
744}
745
746impl<M> Clone for LocalSender<M> {
747 fn clone(&self) -> Self {
748 Self {
749 queue: Rc::clone(&self.queue),
750 token: self.token,
751 marker: PhantomData,
752 }
753 }
754}
755
756impl<M: 'static> LocalSender<M> {
757 pub fn send(&self, message: M) -> bool {
759 let wake = {
760 let mut queue = self.queue.borrow_mut();
761 if !queue.open
762 || !queue.active.contains(&self.token.scope)
763 || queue.envelopes.len() >= LOCAL_MESSAGE_QUEUE_CAPACITY
764 {
765 return false;
766 }
767 let wake = queue
768 .envelopes
769 .is_empty()
770 .then(|| queue.wake.clone())
771 .flatten();
772 queue.envelopes.push_back(MessageEnvelope {
773 control: None,
774 token: self.token,
775 payload: Box::new(message),
776 });
777 wake
778 };
779 if let Some(wake) = wake {
780 wake();
781 }
782 true
783 }
784
785 pub fn callback<T, F>(&self, map: F) -> Callback<T>
790 where
791 F: Fn(T) -> M + 'static,
792 {
793 let sender = self.clone();
794 if size_of::<F>() == 0 {
795 let source = CallbackSource::new(Rc::as_ptr(&self.queue) as usize, self.token);
796 Callback::new_identified(source, TypeId::of::<F>(), move |value| {
797 sender.send(map(value))
798 })
799 } else {
800 Callback::new_with_acceptance(move |value| sender.send(map(value)))
801 }
802 }
803
804 pub fn message(&self, message: M) -> Callback<()>
806 where
807 M: Clone,
808 {
809 self.callback(move |()| message.clone())
810 }
811}
812
813pub struct ComponentContext<C: Component> {
817 sender: LocalSender<C::Message>,
818 tasks: TaskSpawner,
819 window: WindowRef,
820}
821
822impl<C: Component> ComponentContext<C> {
823 #[must_use = "false means there is no active component publication"]
825 pub fn open_window(&self, root: View) -> bool {
826 self.window.request_open(root)
827 }
828
829 pub fn sender(&self) -> LocalSender<C::Message> {
831 self.sender.clone()
832 }
833
834 pub fn spawn_background<F>(&self, work: F) -> ComponentTask
839 where
840 C::Message: Send,
841 F: FnOnce(CancellationToken) -> C::Message + Send + 'static,
842 {
843 self.tasks.spawn(work)
844 }
845
846 pub fn spawn_background_with_rejection<F>(&self, work: F, rejected: C::Message) -> ComponentTask
851 where
852 C::Message: Send,
853 F: FnOnce(CancellationToken) -> C::Message + Send + 'static,
854 {
855 self.tasks.spawn_with_rejection(work, rejected)
856 }
857
858 pub fn window(&self) -> WindowRef {
860 self.window.clone()
861 }
862}
863
864#[derive(Default)]
865enum SingleDeclaration<T> {
866 #[default]
867 Empty,
868 Value(T),
869 Duplicate,
870}
871
872impl<T> SingleDeclaration<T> {
873 fn declare(&mut self, value: T) {
874 *self = if matches!(self, Self::Empty) {
875 Self::Value(value)
876 } else {
877 Self::Duplicate
878 };
879 }
880
881 fn resolve<E>(self, duplicate: E) -> Result<Option<T>, E> {
882 match self {
883 Self::Empty => Ok(None),
884 Self::Value(value) => Ok(Some(value)),
885 Self::Duplicate => Err(duplicate),
886 }
887 }
888}
889
890pub struct ViewContext<C: Component> {
895 contexts: ContextSnapshot,
896 effects: ComponentEffects,
897 reads: ContextDependencies,
898 sender: LocalSender<C::Message>,
899 color_scheme_observation: SingleDeclaration<Callback<ColorScheme>>,
900 window_size_observation: SingleDeclaration<Callback<WindowSize>>,
901 window_title: SingleDeclaration<String>,
902 window_visuals: SingleDeclaration<WindowVisuals>,
903}
904
905impl<C: Component> ViewContext<C> {
906 pub fn on_color_scheme(&mut self, callback: impl IntoPayloadCallback<ColorScheme>) {
908 self.color_scheme_observation
909 .declare(callback.into_payload_callback());
910 }
911
912 pub fn on_window_size(&mut self, callback: impl IntoPayloadCallback<WindowSize>) {
914 self.window_size_observation
915 .declare(callback.into_payload_callback());
916 }
917
918 pub fn window_title(&mut self, title: impl Into<String>) {
920 self.window_title.declare(title.into());
921 }
922
923 pub fn window_visuals(&mut self, visuals: WindowVisuals) {
925 self.window_visuals.declare(visuals);
926 }
927
928 pub fn sender(&self) -> LocalSender<C::Message> {
930 self.sender.clone()
931 }
932
933 pub fn callback<T>(&self, map: impl Fn(T) -> C::Message + 'static) -> Callback<T> {
935 self.sender.callback(map)
936 }
937
938 pub fn forward(&self) -> Callback<C::Message> {
940 self.sender.callback(std::convert::identity)
941 }
942
943 pub fn message(&self, message: C::Message) -> Callback<()>
945 where
946 C::Message: Clone,
947 {
948 self.sender.message(message)
949 }
950
951 pub fn use_context<T: Clone + 'static>(&mut self, context: &Context<T>) -> T {
953 let resolved = self.contexts.get(context);
954 self.reads.insert(ContextDependency {
955 id: context.id,
956 provider: resolved.as_ref().map(|(provider, _)| *provider),
957 });
958 resolved.map_or_else(|| context.default.clone(), |(_, value)| value)
959 }
960
961 pub fn use_effect<D>(
967 &mut self,
968 key: impl Into<EffectKey>,
969 dependency: D,
970 setup: impl FnOnce() -> Option<Box<dyn FnOnce()>> + 'static,
971 ) where
972 D: PartialEq + 'static,
973 {
974 self.effects.use_effect(key.into(), dependency, setup);
975 }
976}
977
978pub trait Component: Sized + 'static {
984 type Input: Clone + PartialEq + 'static;
986 type Message: 'static;
988
989 fn create(input: &Self::Input, context: &ComponentContext<Self>) -> Self;
991 fn input_changed(&mut self, _input: &Self::Input, _context: &ComponentContext<Self>) {}
993 fn update(&mut self, _message: Self::Message, _context: &ComponentContext<Self>) {}
995 fn view(&self, input: &Self::Input, context: &mut ViewContext<Self>) -> View;
997}
998
999trait ErasedComponentFactory {
1000 fn apply_input(&self, store: &mut ComponentStore, token: ComponentToken) -> bool;
1001 fn as_any(&self) -> &dyn Any;
1002 fn component_type(&self) -> TypeId;
1003 fn equals(&self, other: &dyn ErasedComponentFactory) -> bool;
1004 fn reserve(&self, store: &mut ComponentStore) -> ComponentToken;
1005 fn type_name(&self) -> &'static str;
1006}
1007
1008struct TypedComponentFactory<C: Component> {
1009 input: C::Input,
1010}
1011
1012impl<C: Component> ErasedComponentFactory for TypedComponentFactory<C> {
1013 fn apply_input(&self, store: &mut ComponentStore, token: ComponentToken) -> bool {
1014 store.apply_input(token, &self.input)
1015 }
1016
1017 fn as_any(&self) -> &dyn Any {
1018 self
1019 }
1020
1021 fn component_type(&self) -> TypeId {
1022 TypeId::of::<C>()
1023 }
1024
1025 fn equals(&self, other: &dyn ErasedComponentFactory) -> bool {
1026 other.component_type() == TypeId::of::<C>()
1027 && other
1028 .as_any()
1029 .downcast_ref::<Self>()
1030 .is_some_and(|other| self.input == other.input)
1031 }
1032
1033 fn reserve(&self, store: &mut ComponentStore) -> ComponentToken {
1034 store.reserve_component::<C>(self.input.clone())
1035 }
1036
1037 fn type_name(&self) -> &'static str {
1038 std::any::type_name::<C>()
1039 }
1040}
1041
1042#[derive(Clone)]
1043pub(crate) struct ComponentView {
1044 factory: Rc<dyn ErasedComponentFactory>,
1045}
1046
1047impl ComponentView {
1048 pub(crate) fn new<C: Component>(input: C::Input) -> Self {
1049 Self {
1050 factory: Rc::new(TypedComponentFactory::<C> { input }),
1051 }
1052 }
1053
1054 pub(crate) fn component_type(&self) -> TypeId {
1055 self.factory.component_type()
1056 }
1057
1058 pub(crate) fn apply_input(&self, store: &mut ComponentStore, token: ComponentToken) -> bool {
1059 self.factory.apply_input(store, token)
1060 }
1061
1062 pub(crate) fn reserve(&self, store: &mut ComponentStore) -> ComponentToken {
1063 self.factory.reserve(store)
1064 }
1065}
1066
1067impl fmt::Debug for ComponentView {
1068 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1069 formatter
1070 .debug_tuple("Component")
1071 .field(&self.factory.type_name())
1072 .finish()
1073 }
1074}
1075
1076impl PartialEq for ComponentView {
1077 fn eq(&self, other: &Self) -> bool {
1078 self.factory.equals(&*other.factory)
1079 }
1080}
1081
1082trait ErasedScope {
1083 fn apply_input(&mut self, input: &dyn Any, tasks: TaskSpawner) -> bool;
1084 #[cfg(test)]
1085 fn component(&self) -> &dyn Any;
1086 fn dispatch(&mut self, message: Box<dyn Any>, tasks: TaskSpawner);
1087 #[cfg(test)]
1088 fn message_type(&self) -> TypeId;
1089 fn input_type(&self) -> TypeId;
1090 fn type_name(&self) -> &'static str;
1091 fn context_dependencies(&self) -> Option<&ContextDependencies>;
1092 fn set_context_dependencies(&mut self, dependencies: ContextDependencies);
1093 fn view(&self, contexts: ContextSnapshot)
1094 -> Result<ComponentRender, ComponentDeclarationError>;
1095 fn cleanup_effects(&self);
1096 fn commit_effects(&self);
1097 fn prepare_effects(&self);
1098}
1099
1100pub(crate) struct ComponentRender {
1101 pub(crate) color_scheme_observation: Option<Callback<ColorScheme>>,
1102 pub(crate) dependencies: ContextDependencies,
1103 pub(crate) view: View,
1104 pub(crate) window_size_observation: Option<Callback<WindowSize>>,
1105 pub(crate) window_title: Option<String>,
1106 pub(crate) window_visuals: Option<WindowVisuals>,
1107}
1108
1109#[allow(clippy::large_enum_variant)]
1111enum ComponentViewOutcome {
1112 Complete(Result<ComponentRender, ComponentDeclarationError>),
1113 Panicked(Box<dyn Any + Send>),
1114}
1115
1116type EffectCleanup = Box<dyn FnOnce()>;
1117type EffectSetup = Box<dyn FnOnce() -> Option<EffectCleanup>>;
1118
1119struct EffectSlot {
1120 cleanup: Option<EffectCleanup>,
1121 dependency: Box<dyn Any>,
1122 key: EffectKey,
1123}
1124
1125enum EffectRegistration {
1126 Retain {
1127 key: EffectKey,
1128 },
1129 Replace {
1130 dependency: Box<dyn Any>,
1131 key: EffectKey,
1132 setup: EffectSetup,
1133 },
1134}
1135
1136impl EffectRegistration {
1137 fn key(&self) -> &EffectKey {
1138 match self {
1139 Self::Retain { key } | Self::Replace { key, .. } => key,
1140 }
1141 }
1142}
1143
1144#[derive(Default)]
1145struct ComponentEffectState {
1146 registrations: Vec<EffectRegistration>,
1147 slots: Vec<EffectSlot>,
1148}
1149
1150impl ComponentEffectState {
1151 fn begin_view(&mut self) {
1152 self.registrations.clear();
1153 }
1154
1155 fn duplicate_key(&self) -> Option<&EffectKey> {
1156 self.registrations
1157 .windows(2)
1158 .next_back()
1159 .filter(|pair| pair[0].key() == pair[1].key())
1160 .map(|pair| pair[0].key())
1161 }
1162
1163 fn use_effect<D>(
1164 &mut self,
1165 key: EffectKey,
1166 dependency: D,
1167 setup: impl FnOnce() -> Option<EffectCleanup> + 'static,
1168 ) where
1169 D: PartialEq + 'static,
1170 {
1171 if self.duplicate_key().is_some() {
1172 return;
1173 }
1174 if self
1175 .registrations
1176 .iter()
1177 .any(|registration| registration.key() == &key)
1178 {
1179 self.registrations
1181 .push(EffectRegistration::Retain { key: key.clone() });
1182 self.registrations.push(EffectRegistration::Retain { key });
1183 return;
1184 }
1185 let changed = self
1186 .slots
1187 .iter()
1188 .find(|slot| slot.key == key)
1189 .and_then(|slot| slot.dependency.downcast_ref::<D>())
1190 != Some(&dependency);
1191 self.registrations.push(if changed {
1192 EffectRegistration::Replace {
1193 dependency: Box::new(dependency),
1194 key,
1195 setup: Box::new(setup),
1196 }
1197 } else {
1198 EffectRegistration::Retain { key }
1199 });
1200 }
1201
1202 fn finish_view(&self) -> Result<(), ComponentDeclarationError> {
1203 if let Some(key) = self.duplicate_key() {
1204 Err(ComponentDeclarationError::EffectKey(key.clone()))
1205 } else {
1206 Ok(())
1207 }
1208 }
1209
1210 fn prepare(&mut self) {
1211 for slot in &mut self.slots {
1212 let cleanup_required = self
1213 .registrations
1214 .iter()
1215 .find(|registration| registration.key() == &slot.key)
1216 .is_none_or(|registration| {
1217 matches!(registration, EffectRegistration::Replace { .. })
1218 });
1219 if cleanup_required && let Some(cleanup) = slot.cleanup.take() {
1220 cleanup();
1221 }
1222 }
1223 }
1224
1225 fn commit(&mut self) {
1226 debug_assert!(self.duplicate_key().is_none());
1227 let mut published = std::mem::take(&mut self.slots);
1228 for registration in self.registrations.drain(..) {
1229 let slot = match registration {
1230 EffectRegistration::Replace {
1231 dependency,
1232 key,
1233 setup,
1234 } => EffectSlot {
1235 cleanup: setup(),
1236 dependency,
1237 key,
1238 },
1239 EffectRegistration::Retain { key } => {
1240 let index = published.iter().position(|slot| slot.key == key).unwrap();
1241 published.remove(index)
1242 }
1243 };
1244 self.slots.push(slot);
1245 }
1246 }
1247
1248 fn cleanup(&mut self) {
1249 for slot in self.slots.iter_mut().rev() {
1250 if let Some(cleanup) = slot.cleanup.take() {
1251 cleanup();
1252 }
1253 }
1254 self.slots.clear();
1255 self.registrations.clear();
1256 }
1257
1258 fn is_empty(&self) -> bool {
1259 self.registrations.is_empty() && self.slots.is_empty()
1260 }
1261}
1262
1263#[derive(Default)]
1264pub(crate) struct ComponentEffects(Option<Box<ComponentEffectState>>);
1265
1266impl ComponentEffects {
1267 fn begin_view(&mut self) {
1268 if let Some(state) = self.0.as_deref_mut() {
1269 state.begin_view();
1270 }
1271 }
1272
1273 fn use_effect<D>(
1274 &mut self,
1275 key: EffectKey,
1276 dependency: D,
1277 setup: impl FnOnce() -> Option<EffectCleanup> + 'static,
1278 ) where
1279 D: PartialEq + 'static,
1280 {
1281 self.0
1282 .get_or_insert_with(Box::default)
1283 .use_effect(key, dependency, setup);
1284 }
1285
1286 fn finish_view(&self) -> Result<(), ComponentDeclarationError> {
1287 self.0
1288 .as_deref()
1289 .map_or(Ok(()), ComponentEffectState::finish_view)
1290 }
1291
1292 fn prepare(&mut self) {
1293 if let Some(state) = self.0.as_deref_mut() {
1294 state.prepare();
1295 }
1296 }
1297
1298 fn commit(&mut self) {
1299 let clear = self.0.as_deref_mut().is_some_and(|state| {
1300 state.commit();
1301 state.is_empty()
1302 });
1303 if clear {
1304 self.0 = None;
1305 }
1306 }
1307
1308 fn cleanup(&mut self) {
1309 if let Some(state) = self.0.as_deref_mut() {
1310 state.cleanup();
1311 }
1312 self.0 = None;
1313 }
1314}
1315
1316struct TypedScope<C, I, M> {
1317 component: C,
1318 context_dependencies: Option<Rc<ContextDependencies>>,
1319 effects: RefCell<ComponentEffects>,
1320 input: I,
1321 input_changed: fn(&mut C, &I, LocalSender<M>, TaskSpawner, WindowRef),
1322 sender: LocalSender<M>,
1323 update: fn(&mut C, M, LocalSender<M>, TaskSpawner, WindowRef),
1324 view: fn(
1325 &C,
1326 &I,
1327 LocalSender<M>,
1328 ComponentEffects,
1329 ContextSnapshot,
1330 ) -> (ComponentViewOutcome, ComponentEffects),
1331 window: WindowEndpoint,
1332}
1333
1334impl<C, I, M> Drop for TypedScope<C, I, M> {
1335 fn drop(&mut self) {
1336 self.effects.borrow_mut().cleanup();
1337 }
1338}
1339
1340impl<C, I, M> ErasedScope for TypedScope<C, I, M>
1341where
1342 C: 'static,
1343 I: Clone + PartialEq + 'static,
1344 M: 'static,
1345{
1346 fn apply_input(&mut self, input: &dyn Any, tasks: TaskSpawner) -> bool {
1347 let input = input.downcast_ref::<I>().unwrap();
1348 if self.input == *input {
1349 return false;
1350 }
1351 self.input = input.clone();
1352 self.window.begin();
1353 (self.input_changed)(
1354 &mut self.component,
1355 &self.input,
1356 self.sender.clone(),
1357 tasks,
1358 self.window.reference(),
1359 );
1360 self.window.finish();
1361 true
1362 }
1363
1364 #[cfg(test)]
1365 fn component(&self) -> &dyn Any {
1366 &self.component
1367 }
1368
1369 fn context_dependencies(&self) -> Option<&ContextDependencies> {
1370 self.context_dependencies.as_deref()
1371 }
1372
1373 fn set_context_dependencies(&mut self, dependencies: ContextDependencies) {
1374 self.context_dependencies = (!dependencies.is_empty()).then(|| Rc::new(dependencies));
1375 }
1376
1377 fn dispatch(&mut self, message: Box<dyn Any>, tasks: TaskSpawner) {
1378 let message = message.downcast::<M>().unwrap();
1379 self.window.begin();
1380 (self.update)(
1381 &mut self.component,
1382 *message,
1383 self.sender.clone(),
1384 tasks,
1385 self.window.reference(),
1386 );
1387 self.window.finish();
1388 }
1389
1390 #[cfg(test)]
1391 fn message_type(&self) -> TypeId {
1392 TypeId::of::<M>()
1393 }
1394
1395 fn input_type(&self) -> TypeId {
1396 TypeId::of::<I>()
1397 }
1398
1399 fn type_name(&self) -> &'static str {
1400 std::any::type_name::<C>()
1401 }
1402
1403 fn view(
1404 &self,
1405 contexts: ContextSnapshot,
1406 ) -> Result<ComponentRender, ComponentDeclarationError> {
1407 let mut effects = self.effects.take();
1408 effects.begin_view();
1409 let (outcome, effects) = (self.view)(
1410 &self.component,
1411 &self.input,
1412 self.sender.clone(),
1413 effects,
1414 contexts,
1415 );
1416 self.effects.replace(effects);
1417 match outcome {
1418 ComponentViewOutcome::Complete(render) => render,
1419 ComponentViewOutcome::Panicked(payload) => std::panic::resume_unwind(payload),
1420 }
1421 }
1422
1423 fn cleanup_effects(&self) {
1424 self.effects.borrow_mut().cleanup();
1425 }
1426
1427 fn commit_effects(&self) {
1428 self.effects.borrow_mut().commit();
1429 }
1430
1431 fn prepare_effects(&self) {
1432 self.effects.borrow_mut().prepare();
1433 }
1434}
1435
1436#[derive(Clone, Debug, Default, Eq, PartialEq)]
1437pub struct DrainReport {
1438 pub blocked: bool,
1439 pub dispatched: usize,
1440 pub dropped: usize,
1441 pub(crate) dirty: Vec<ComponentToken>,
1442}
1443
1444pub struct ComponentStore {
1445 background: Arc<Mutex<BackgroundQueue>>,
1446 context_consumers: HashMap<ContextDependency, HashSet<ScopeId>>,
1447 context_consumers_by_id: HashMap<ContextId, HashSet<ScopeId>>,
1448 drain_background_next: bool,
1449 window: WindowToken,
1450 scopes: ScopeArena<Box<dyn ErasedScope>>,
1451 task_limiter: Arc<TaskLimiter>,
1452 queue: Rc<RefCell<ComponentQueue>>,
1453 window_endpoint: WindowEndpoint,
1454}
1455
1456impl ComponentStore {
1457 pub fn new(window: WindowToken) -> Self {
1458 Self::with_task_limiter(window, Arc::new(TaskLimiter::default()))
1459 }
1460
1461 fn with_task_limiter(window: WindowToken, task_limiter: Arc<TaskLimiter>) -> Self {
1462 Self {
1463 background: Arc::new(Mutex::new(BackgroundQueue {
1464 envelopes: VecDeque::new(),
1465 open: true,
1466 tasks: HashMap::default(),
1467 wake: None,
1468 wake_pending: false,
1469 })),
1470 context_consumers: HashMap::default(),
1471 context_consumers_by_id: HashMap::default(),
1472 drain_background_next: false,
1473 window,
1474 scopes: ScopeArena::new(),
1475 task_limiter,
1476 queue: Rc::new(RefCell::new(ComponentQueue {
1477 active: HashSet::default(),
1478 envelopes: VecDeque::new(),
1479 open: true,
1480 wake: None,
1481 })),
1482 window_endpoint: WindowEndpoint::new(window),
1483 }
1484 }
1485
1486 pub fn reserve_component<C: Component>(&mut self, input: C::Input) -> ComponentToken {
1487 fn input_changed<C: Component>(
1488 component: &mut C,
1489 input: &C::Input,
1490 sender: LocalSender<C::Message>,
1491 tasks: TaskSpawner,
1492 window: WindowRef,
1493 ) {
1494 component.input_changed(
1495 input,
1496 &ComponentContext {
1497 sender,
1498 tasks,
1499 window,
1500 },
1501 );
1502 }
1503
1504 fn update<C: Component>(
1505 component: &mut C,
1506 message: C::Message,
1507 sender: LocalSender<C::Message>,
1508 tasks: TaskSpawner,
1509 window: WindowRef,
1510 ) {
1511 component.update(
1512 message,
1513 &ComponentContext {
1514 sender,
1515 tasks,
1516 window,
1517 },
1518 );
1519 }
1520
1521 fn view<C: Component>(
1522 component: &C,
1523 input: &C::Input,
1524 sender: LocalSender<C::Message>,
1525 effects: ComponentEffects,
1526 contexts: ContextSnapshot,
1527 ) -> (ComponentViewOutcome, ComponentEffects) {
1528 let mut context = ViewContext {
1529 contexts,
1530 effects,
1531 reads: ContextDependencies::default(),
1532 sender,
1533 color_scheme_observation: SingleDeclaration::default(),
1534 window_size_observation: SingleDeclaration::default(),
1535 window_title: SingleDeclaration::default(),
1536 window_visuals: SingleDeclaration::default(),
1537 };
1538 let view = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1539 component.view(input, &mut context)
1540 }));
1541 let view = match view {
1542 Ok(view) => view,
1543 Err(payload) => {
1544 return (ComponentViewOutcome::Panicked(payload), context.effects);
1545 }
1546 };
1547 let ViewContext {
1548 effects,
1549 reads: dependencies,
1550 color_scheme_observation,
1551 window_size_observation,
1552 window_title,
1553 window_visuals,
1554 ..
1555 } = context;
1556 let render = (|| {
1557 let color_scheme_observation = color_scheme_observation
1558 .resolve(ComponentDeclarationError::ColorSchemeObservation)?;
1559 let window_size_observation = window_size_observation
1560 .resolve(ComponentDeclarationError::WindowSizeObservation)?;
1561 let window_title = window_title.resolve(ComponentDeclarationError::WindowTitle)?;
1562 let window_visuals =
1563 window_visuals.resolve(ComponentDeclarationError::WindowVisuals)?;
1564 effects.finish_view()?;
1565 Ok(ComponentRender {
1566 color_scheme_observation,
1567 dependencies,
1568 view,
1569 window_size_observation,
1570 window_title,
1571 window_visuals,
1572 })
1573 })();
1574 (ComponentViewOutcome::Complete(render), effects)
1575 }
1576
1577 let background = Arc::clone(&self.background);
1578 let queue = Rc::clone(&self.queue);
1579 let task_limiter = Arc::clone(&self.task_limiter);
1580 let window = self.window;
1581 let window_endpoint = self.window_endpoint.clone();
1582 let scope = self.scopes.reserve_with(move |scope| {
1583 queue.borrow_mut().active.insert(scope);
1584 let sender = LocalSender {
1585 queue: Rc::clone(&queue),
1586 token: ComponentToken { window, scope },
1587 marker: PhantomData,
1588 };
1589 let tasks = TaskSpawner {
1590 limiter: Arc::clone(&task_limiter),
1591 queue: Arc::clone(&background),
1592 token: ComponentToken { window, scope },
1593 };
1594 window_endpoint.begin();
1595 let component = C::create(
1596 &input,
1597 &ComponentContext {
1598 sender: sender.clone(),
1599 tasks,
1600 window: window_endpoint.reference(),
1601 },
1602 );
1603 window_endpoint.finish();
1604 Box::new(TypedScope {
1605 component,
1606 context_dependencies: None,
1607 effects: RefCell::default(),
1608 input,
1609 input_changed: input_changed::<C>,
1610 sender,
1611 update: update::<C>,
1612 view: view::<C>,
1613 window: window_endpoint,
1614 }) as Box<dyn ErasedScope>
1615 });
1616 ComponentToken {
1617 window: self.window,
1618 scope,
1619 }
1620 }
1621
1622 pub fn publish(&mut self, token: ComponentToken) {
1623 self.validate_window(token);
1624 self.scopes.publish(token.scope).unwrap();
1625 }
1626
1627 pub(crate) fn restarted(&self, window: WindowToken) -> Self {
1628 Self::with_task_limiter(window, Arc::clone(&self.task_limiter))
1629 }
1630
1631 pub(crate) fn take_host_requests(&self) -> Vec<HostRequest> {
1632 self.window_endpoint.take_requests()
1633 }
1634
1635 pub(crate) fn commit_window_close(&self) {
1636 self.window_endpoint.commit_close();
1637 }
1638
1639 pub fn remove(&mut self, token: ComponentToken) {
1640 self.validate_window(token);
1641 self.clear_context_dependencies(token.scope);
1642 self.scopes.remove(token.scope).unwrap();
1643 self.cancel_scope_tasks(token.scope);
1644 self.remove_scope_messages(token.scope);
1645 }
1646
1647 #[cfg(test)]
1648 pub fn sender<M: 'static>(&self, token: ComponentToken) -> LocalSender<M> {
1649 self.validate_window(token);
1650 let scope = self.scopes.get(token.scope).unwrap();
1651 let actual = TypeId::of::<M>();
1652 let expected = scope.message_type();
1653 assert_eq!(actual, expected);
1654 LocalSender {
1655 queue: Rc::clone(&self.queue),
1656 token,
1657 marker: PhantomData,
1658 }
1659 }
1660
1661 pub fn apply_input<I: 'static>(&mut self, token: ComponentToken, input: &I) -> bool {
1662 self.validate_window(token);
1663 let tasks = self.task_spawner(token);
1664 let scope = self.scopes.get_mut(token.scope).unwrap();
1665 let actual = TypeId::of::<I>();
1666 let expected = scope.input_type();
1667 assert_eq!(actual, expected);
1668 scope.apply_input(input, tasks)
1669 }
1670
1671 #[cfg(test)]
1672 pub fn component<C: 'static>(&self, token: ComponentToken) -> &C {
1673 self.validate_window(token);
1674 let component = self.scopes.get(token.scope).unwrap().component();
1675 component.downcast_ref().unwrap()
1676 }
1677
1678 pub fn drain(&mut self, budget: usize) -> DrainReport {
1679 let mut report = DrainReport::default();
1680 self.background.lock().unwrap().wake_pending = false;
1681 for _ in 0..budget {
1682 let pop_background = || {
1683 self.background
1684 .lock()
1685 .unwrap()
1686 .envelopes
1687 .pop_front()
1688 .map(PendingEnvelope::Background)
1689 };
1690 let pop_local = || {
1691 self.queue
1692 .borrow_mut()
1693 .envelopes
1694 .pop_front()
1695 .map(PendingEnvelope::Local)
1696 };
1697 let envelope = if self.drain_background_next {
1698 pop_background().or_else(pop_local)
1699 } else {
1700 pop_local().or_else(pop_background)
1701 };
1702 let Some(envelope) = envelope else {
1703 break;
1704 };
1705 let from_background = matches!(envelope, PendingEnvelope::Background(_));
1706 self.drain_background_next = !from_background;
1707 let token = envelope.token();
1708 if token.window != self.window {
1709 if let Some(control) = envelope.control() {
1710 control.cancel();
1711 }
1712 report.dropped += 1;
1713 continue;
1714 }
1715 let state = match self.scopes.state(token.scope) {
1716 Ok(state) => state,
1717 Err(ScopeError::Stale(_)) => {
1718 if let Some(control) = envelope.control() {
1719 control.cancel();
1720 }
1721 report.dropped += 1;
1722 continue;
1723 }
1724 Err(ScopeError::InvalidTransition(_, _)) => unreachable!(),
1725 };
1726 match state {
1727 ScopeState::Reserved => {
1728 match envelope {
1729 PendingEnvelope::Background(envelope) => self
1730 .background
1731 .lock()
1732 .unwrap()
1733 .envelopes
1734 .push_front(envelope),
1735 PendingEnvelope::Local(envelope) => {
1736 self.queue.borrow_mut().envelopes.push_front(envelope);
1737 }
1738 }
1739 report.blocked = true;
1740 break;
1741 }
1742 ScopeState::Published => {
1743 if let PendingEnvelope::Background(background) = &envelope {
1744 match background.delivery {
1745 BackgroundDelivery::Completion if !background.control.deliver() => {
1746 report.dropped += 1;
1747 continue;
1748 }
1749 BackgroundDelivery::Rejection
1750 if background.control.status() != ComponentTaskStatus::Rejected =>
1751 {
1752 report.dropped += 1;
1753 continue;
1754 }
1755 BackgroundDelivery::Completion | BackgroundDelivery::Rejection => {}
1756 }
1757 } else if let Some(control) = envelope.control()
1758 && !control.deliver()
1759 {
1760 report.dropped += 1;
1761 continue;
1762 }
1763 let payload: Box<dyn Any> = match envelope {
1764 PendingEnvelope::Background(envelope) => envelope.payload,
1765 PendingEnvelope::Local(envelope) => envelope.payload,
1766 };
1767 let tasks = self.task_spawner(token);
1768 self.scopes
1769 .get_mut(token.scope)
1770 .unwrap()
1771 .dispatch(payload, tasks);
1772 report.dispatched += 1;
1773 report.dirty.push(token);
1774 }
1775 }
1776 }
1777 report
1778 }
1779
1780 pub fn pending(&self) -> usize {
1781 self.queue.borrow().envelopes.len() + self.background.lock().unwrap().envelopes.len()
1782 }
1783
1784 #[cfg(test)]
1785 pub(crate) fn exhaust_task_capacity(&self) {
1786 self.task_limiter
1787 .active
1788 .store(BACKGROUND_TASK_CAPACITY, Ordering::Release);
1789 }
1790
1791 pub(crate) fn pending_tokens(&self) -> Vec<ComponentToken> {
1792 let mut tokens = self
1793 .queue
1794 .borrow()
1795 .envelopes
1796 .iter()
1797 .map(|envelope| envelope.token)
1798 .collect::<Vec<_>>();
1799 tokens.extend(
1800 self.background
1801 .lock()
1802 .unwrap()
1803 .envelopes
1804 .iter()
1805 .map(|envelope| envelope.token),
1806 );
1807 tokens
1808 }
1809
1810 pub(crate) fn next_pending_token(&self) -> Option<ComponentToken> {
1811 let local = self
1812 .queue
1813 .borrow()
1814 .envelopes
1815 .front()
1816 .map(|envelope| envelope.token);
1817 let background = self
1818 .background
1819 .lock()
1820 .unwrap()
1821 .envelopes
1822 .front()
1823 .map(|envelope| envelope.token);
1824 if self.drain_background_next {
1825 background.or(local)
1826 } else {
1827 local.or(background)
1828 }
1829 }
1830
1831 pub(crate) fn context_dependencies(
1832 &self,
1833 token: ComponentToken,
1834 ) -> Option<&ContextDependencies> {
1835 self.validate_window(token);
1836 self.scopes.get(token.scope).unwrap().context_dependencies()
1837 }
1838
1839 pub(crate) fn set_context_dependencies(
1840 &mut self,
1841 token: ComponentToken,
1842 dependencies: ContextDependencies,
1843 ) {
1844 self.validate_window(token);
1845 let unchanged = self
1846 .scopes
1847 .get(token.scope)
1848 .unwrap()
1849 .context_dependencies()
1850 .map_or_else(
1851 || dependencies.is_empty(),
1852 |previous| previous == &dependencies,
1853 );
1854 if unchanged {
1855 return;
1856 }
1857 let previous = self
1858 .scopes
1859 .get(token.scope)
1860 .unwrap()
1861 .context_dependencies()
1862 .cloned()
1863 .unwrap_or_default();
1864 for dependency in previous
1865 .iter()
1866 .filter(|dependency| !dependencies.contains(dependency))
1867 {
1868 self.remove_context_consumer(*dependency, token.scope);
1869 }
1870 for dependency in dependencies
1871 .iter()
1872 .filter(|dependency| !previous.contains(dependency))
1873 .copied()
1874 {
1875 self.context_consumers
1876 .entry(dependency)
1877 .or_default()
1878 .insert(token.scope);
1879 self.context_consumers_by_id
1880 .entry(dependency.id)
1881 .or_default()
1882 .insert(token.scope);
1883 }
1884 self.scopes
1885 .get_mut(token.scope)
1886 .unwrap()
1887 .set_context_dependencies(dependencies);
1888 }
1889
1890 pub(crate) fn context_consumers(
1891 &self,
1892 dependency: ContextDependency,
1893 ) -> impl Iterator<Item = ScopeId> + '_ {
1894 self.context_consumers
1895 .get(&dependency)
1896 .into_iter()
1897 .flatten()
1898 .copied()
1899 }
1900
1901 pub(crate) fn context_consumers_for_id(
1902 &self,
1903 id: ContextId,
1904 ) -> impl Iterator<Item = ScopeId> + '_ {
1905 self.context_consumers_by_id
1906 .get(&id)
1907 .into_iter()
1908 .flatten()
1909 .copied()
1910 }
1911
1912 pub(crate) fn view(
1913 &self,
1914 token: ComponentToken,
1915 contexts: ContextSnapshot,
1916 ) -> Result<ComponentRender, ComponentDeclarationError> {
1917 self.validate_window(token);
1918 self.scopes.get(token.scope).unwrap().view(contexts)
1919 }
1920
1921 pub(crate) fn type_name(&self, token: ComponentToken) -> &'static str {
1922 self.validate_window(token);
1923 self.scopes.get(token.scope).unwrap().type_name()
1924 }
1925
1926 pub fn cleanup_effects(&self, token: ComponentToken) {
1927 self.validate_window(token);
1928 self.scopes.get(token.scope).unwrap().cleanup_effects();
1929 }
1930
1931 pub fn commit_effects(&self, token: ComponentToken) {
1932 self.validate_window(token);
1933 self.scopes.get(token.scope).unwrap().commit_effects();
1934 }
1935
1936 pub fn prepare_effects(&self, token: ComponentToken) {
1937 self.validate_window(token);
1938 self.scopes.get(token.scope).unwrap().prepare_effects();
1939 }
1940
1941 pub(crate) fn token(&self, scope: ScopeId) -> ComponentToken {
1942 self.scopes.state(scope).unwrap();
1943 ComponentToken {
1944 window: self.window,
1945 scope,
1946 }
1947 }
1948
1949 pub(crate) fn set_waker(&mut self, wake: Rc<dyn Fn()>) {
1950 self.queue.borrow_mut().wake = Some(wake);
1951 }
1952
1953 pub(crate) fn set_background_waker(&mut self, wake: Arc<dyn Fn() -> bool + Send + Sync>) {
1954 self.background.lock().unwrap().wake = Some(wake);
1955 }
1956
1957 pub(crate) fn close(&mut self) {
1958 self.window_endpoint.close();
1959 {
1960 let mut queue = self.queue.borrow_mut();
1961 queue.open = false;
1962 queue.active.clear();
1963 for envelope in queue.envelopes.drain(..) {
1964 if let Some(control) = envelope.control {
1965 control.cancel();
1966 }
1967 }
1968 queue.wake = None;
1969 }
1970 let mut background = self.background.lock().unwrap();
1971 background.open = false;
1972 for envelope in background.envelopes.drain(..) {
1973 envelope.control.cancel();
1974 }
1975 background.wake = None;
1976 background.wake_pending = false;
1977 for tasks in background.tasks.values() {
1978 for control in tasks.iter().filter_map(Weak::upgrade) {
1979 control.cancel();
1980 }
1981 }
1982 background.tasks.clear();
1983 self.context_consumers.clear();
1984 self.context_consumers_by_id.clear();
1985 }
1986
1987 fn cancel_scope_tasks(&mut self, scope: ScopeId) {
1988 let mut background = self.background.lock().unwrap();
1989 if let Some(tasks) = background.tasks.remove(&scope) {
1990 for control in tasks.iter().filter_map(Weak::upgrade) {
1991 control.cancel();
1992 }
1993 }
1994 for envelope in background
1995 .envelopes
1996 .iter()
1997 .filter(|envelope| envelope.token.scope == scope)
1998 {
1999 envelope.control.cancel();
2000 }
2001 background
2002 .envelopes
2003 .retain(|envelope| envelope.token.scope != scope);
2004 }
2005
2006 fn task_spawner(&self, token: ComponentToken) -> TaskSpawner {
2007 TaskSpawner {
2008 limiter: Arc::clone(&self.task_limiter),
2009 queue: Arc::clone(&self.background),
2010 token,
2011 }
2012 }
2013
2014 fn remove_scope_messages(&mut self, scope: ScopeId) {
2015 let mut queue = self.queue.borrow_mut();
2016 queue.active.remove(&scope);
2017 for envelope in queue
2018 .envelopes
2019 .iter()
2020 .filter(|envelope| envelope.token.scope == scope)
2021 {
2022 if let Some(control) = &envelope.control {
2023 control.cancel();
2024 }
2025 }
2026 queue
2027 .envelopes
2028 .retain(|envelope| envelope.token.scope != scope);
2029 }
2030
2031 fn clear_context_dependencies(&mut self, scope: ScopeId) {
2032 self.set_context_dependencies(
2033 ComponentToken {
2034 window: self.window,
2035 scope,
2036 },
2037 ContextDependencies::default(),
2038 );
2039 }
2040
2041 fn remove_context_consumer(&mut self, dependency: ContextDependency, scope: ScopeId) {
2042 if let Some(consumers) = self.context_consumers.get_mut(&dependency) {
2043 consumers.remove(&scope);
2044 if consumers.is_empty() {
2045 self.context_consumers.remove(&dependency);
2046 }
2047 }
2048 if let Some(consumers) = self.context_consumers_by_id.get_mut(&dependency.id) {
2049 consumers.remove(&scope);
2050 if consumers.is_empty() {
2051 self.context_consumers_by_id.remove(&dependency.id);
2052 }
2053 }
2054 }
2055
2056 fn validate_window(&self, token: ComponentToken) {
2057 assert_eq!(token.window, self.window);
2058 }
2059}
2060
2061impl Drop for ComponentStore {
2062 fn drop(&mut self) {
2063 self.close();
2064 }
2065}
2066
2067#[cfg(test)]
2068#[path = "component_tests.rs"]
2069mod tests;