1#![allow(clippy::unwrap_used, clippy::disallowed_methods)]
2use crate::widget::WidgetId;
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum LifecyclePhase {
13 Mount,
15 Update,
17 Unmount,
19 BeforePaint,
21 AfterPaint,
23 Focus,
25 Blur,
27 Visible,
29 Hidden,
31}
32
33pub type LifecycleCallback = Box<dyn FnMut(LifecycleEvent) + Send>;
35
36#[derive(Debug, Clone)]
38pub struct LifecycleEvent {
39 pub widget_id: WidgetId,
41 pub phase: LifecyclePhase,
43 pub timestamp: u64,
45}
46
47impl LifecycleEvent {
48 pub fn new(widget_id: WidgetId, phase: LifecyclePhase, timestamp: u64) -> Self {
50 Self {
51 widget_id,
52 phase,
53 timestamp,
54 }
55 }
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
60pub struct HookId(pub u64);
61
62impl HookId {
63 pub const fn new(id: u64) -> Self {
65 Self(id)
66 }
67}
68
69#[derive(Debug)]
71struct HookRegistration {
72 #[allow(dead_code)]
73 id: HookId,
74 widget_id: WidgetId,
75 phases: Vec<LifecyclePhase>,
76}
77
78pub struct LifecycleManager {
80 next_id: u64,
82 hooks: HashMap<HookId, HookRegistration>,
84 callbacks: HashMap<HookId, LifecycleCallback>,
86 by_widget: HashMap<WidgetId, Vec<HookId>>,
88 by_phase: HashMap<LifecyclePhase, Vec<HookId>>,
90 timestamp: u64,
92 pending_events: Vec<LifecycleEvent>,
94}
95
96impl LifecycleManager {
97 pub fn new() -> Self {
99 Self {
100 next_id: 0,
101 hooks: HashMap::new(),
102 callbacks: HashMap::new(),
103 by_widget: HashMap::new(),
104 by_phase: HashMap::new(),
105 timestamp: 0,
106 pending_events: Vec::new(),
107 }
108 }
109
110 pub fn register(
114 &mut self,
115 widget_id: WidgetId,
116 phases: Vec<LifecyclePhase>,
117 callback: LifecycleCallback,
118 ) -> HookId {
119 let id = HookId::new(self.next_id);
120 self.next_id += 1;
121
122 let registration = HookRegistration {
123 id,
124 widget_id,
125 phases: phases.clone(),
126 };
127
128 self.hooks.insert(id, registration);
129 self.callbacks.insert(id, callback);
130
131 self.by_widget.entry(widget_id).or_default().push(id);
133
134 for phase in phases {
136 self.by_phase.entry(phase).or_default().push(id);
137 }
138
139 id
140 }
141
142 pub fn on_mount(&mut self, widget_id: WidgetId, callback: LifecycleCallback) -> HookId {
144 self.register(widget_id, vec![LifecyclePhase::Mount], callback)
145 }
146
147 pub fn on_unmount(&mut self, widget_id: WidgetId, callback: LifecycleCallback) -> HookId {
149 self.register(widget_id, vec![LifecyclePhase::Unmount], callback)
150 }
151
152 pub fn on_update(&mut self, widget_id: WidgetId, callback: LifecycleCallback) -> HookId {
154 self.register(widget_id, vec![LifecyclePhase::Update], callback)
155 }
156
157 pub fn on_focus(&mut self, widget_id: WidgetId, callback: LifecycleCallback) -> HookId {
159 self.register(widget_id, vec![LifecyclePhase::Focus], callback)
160 }
161
162 pub fn on_blur(&mut self, widget_id: WidgetId, callback: LifecycleCallback) -> HookId {
164 self.register(widget_id, vec![LifecyclePhase::Blur], callback)
165 }
166
167 pub fn unregister(&mut self, hook_id: HookId) -> bool {
169 if let Some(registration) = self.hooks.remove(&hook_id) {
170 self.callbacks.remove(&hook_id);
171
172 if let Some(hooks) = self.by_widget.get_mut(®istration.widget_id) {
174 hooks.retain(|&id| id != hook_id);
175 }
176
177 for phase in ®istration.phases {
179 if let Some(hooks) = self.by_phase.get_mut(phase) {
180 hooks.retain(|&id| id != hook_id);
181 }
182 }
183
184 true
185 } else {
186 false
187 }
188 }
189
190 pub fn unregister_widget(&mut self, widget_id: WidgetId) {
192 if let Some(hook_ids) = self.by_widget.remove(&widget_id) {
193 for hook_id in hook_ids {
194 if let Some(registration) = self.hooks.remove(&hook_id) {
195 self.callbacks.remove(&hook_id);
196
197 for phase in ®istration.phases {
198 if let Some(hooks) = self.by_phase.get_mut(phase) {
199 hooks.retain(|&id| id != hook_id);
200 }
201 }
202 }
203 }
204 }
205 }
206
207 pub fn emit(&mut self, widget_id: WidgetId, phase: LifecyclePhase) {
209 let event = LifecycleEvent::new(widget_id, phase, self.timestamp);
210
211 let widget_hooks = self.by_widget.get(&widget_id).cloned().unwrap_or_default();
213 let phase_hooks = self.by_phase.get(&phase).cloned().unwrap_or_default();
214
215 for hook_id in widget_hooks {
217 if phase_hooks.contains(&hook_id) {
218 if let Some(callback) = self.callbacks.get_mut(&hook_id) {
219 callback(event.clone());
220 }
221 }
222 }
223 }
224
225 pub fn queue(&mut self, widget_id: WidgetId, phase: LifecyclePhase) {
227 let event = LifecycleEvent::new(widget_id, phase, self.timestamp);
228 self.pending_events.push(event);
229 }
230
231 pub fn flush(&mut self) {
233 let events: Vec<LifecycleEvent> = std::mem::take(&mut self.pending_events);
236
237 for event in events {
238 let widget_hooks = self
239 .by_widget
240 .get(&event.widget_id)
241 .cloned()
242 .unwrap_or_default();
243 let phase_hooks = self.by_phase.get(&event.phase).cloned().unwrap_or_default();
244
245 for hook_id in widget_hooks {
246 if phase_hooks.contains(&hook_id) {
247 if let Some(callback) = self.callbacks.get_mut(&hook_id) {
248 callback(event.clone());
249 }
250 }
251 }
252 }
253 }
254
255 pub fn pending_count(&self) -> usize {
257 self.pending_events.len()
258 }
259
260 pub fn tick(&mut self) {
262 self.timestamp += 1;
263 }
264
265 pub fn timestamp(&self) -> u64 {
267 self.timestamp
268 }
269
270 pub fn hook_count(&self) -> usize {
272 self.hooks.len()
273 }
274
275 pub fn has_hooks(&self, widget_id: WidgetId) -> bool {
277 self.by_widget
278 .get(&widget_id)
279 .is_some_and(|h| !h.is_empty())
280 }
281
282 pub fn clear(&mut self) {
284 self.hooks.clear();
285 self.callbacks.clear();
286 self.by_widget.clear();
287 self.by_phase.clear();
288 self.pending_events.clear();
289 }
290}
291
292impl Default for LifecycleManager {
293 fn default() -> Self {
294 Self::new()
295 }
296}
297
298impl std::fmt::Debug for LifecycleManager {
299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
300 f.debug_struct("LifecycleManager")
301 .field("next_id", &self.next_id)
302 .field("hook_count", &self.hooks.len())
303 .field("timestamp", &self.timestamp)
304 .field("pending_count", &self.pending_events.len())
305 .finish()
306 }
307}
308
309pub struct Effect {
311 effect: Option<Box<dyn FnOnce() -> Option<Box<dyn FnOnce() + Send>> + Send>>,
313 cleanup: Option<Box<dyn FnOnce() + Send>>,
315 deps: Vec<u64>,
317}
318
319impl Effect {
320 pub fn new<F>(effect: F) -> Self
322 where
323 F: FnOnce() -> Option<Box<dyn FnOnce() + Send>> + Send + 'static,
324 {
325 Self {
326 effect: Some(Box::new(effect)),
327 cleanup: None,
328 deps: Vec::new(),
329 }
330 }
331
332 pub fn with_deps<F>(effect: F, deps: Vec<u64>) -> Self
334 where
335 F: FnOnce() -> Option<Box<dyn FnOnce() + Send>> + Send + 'static,
336 {
337 Self {
338 effect: Some(Box::new(effect)),
339 cleanup: None,
340 deps,
341 }
342 }
343
344 pub fn deps_changed(&self, new_deps: &[u64]) -> bool {
346 if self.deps.len() != new_deps.len() {
347 return true;
348 }
349 self.deps.iter().zip(new_deps).any(|(a, b)| a != b)
350 }
351
352 pub fn run(&mut self, new_deps: Option<&[u64]>) -> bool {
354 let should_run = match new_deps {
356 Some(deps) if !self.deps_changed(deps) => false,
357 _ => true,
358 };
359
360 if !should_run {
361 return false;
362 }
363
364 if let Some(cleanup) = self.cleanup.take() {
366 cleanup();
367 }
368
369 if let Some(effect) = self.effect.take() {
371 self.cleanup = effect();
372 }
373
374 if let Some(deps) = new_deps {
376 self.deps = deps.to_vec();
377 }
378
379 true
380 }
381
382 pub fn cleanup(&mut self) {
384 if let Some(cleanup) = self.cleanup.take() {
385 cleanup();
386 }
387 }
388
389 pub fn has_cleanup(&self) -> bool {
391 self.cleanup.is_some()
392 }
393}
394
395impl std::fmt::Debug for Effect {
396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
397 f.debug_struct("Effect")
398 .field("has_effect", &self.effect.is_some())
399 .field("has_cleanup", &self.cleanup.is_some())
400 .field("deps", &self.deps)
401 .finish()
402 }
403}
404
405#[derive(Debug, Default)]
407pub struct EffectManager {
408 effects: HashMap<WidgetId, Vec<Effect>>,
410}
411
412impl EffectManager {
413 pub fn new() -> Self {
415 Self::default()
416 }
417
418 pub fn add(&mut self, widget_id: WidgetId, effect: Effect) {
420 self.effects.entry(widget_id).or_default().push(effect);
421 }
422
423 pub fn run_effects(&mut self, widget_id: WidgetId, deps: Option<&[u64]>) {
425 if let Some(effects) = self.effects.get_mut(&widget_id) {
426 for effect in effects {
427 effect.run(deps);
428 }
429 }
430 }
431
432 pub fn cleanup_widget(&mut self, widget_id: WidgetId) {
434 if let Some(effects) = self.effects.get_mut(&widget_id) {
435 for effect in effects {
436 effect.cleanup();
437 }
438 }
439 self.effects.remove(&widget_id);
440 }
441
442 pub fn widget_count(&self) -> usize {
444 self.effects.len()
445 }
446
447 pub fn effect_count(&self) -> usize {
449 self.effects.values().map(std::vec::Vec::len).sum()
450 }
451
452 pub fn clear(&mut self) {
454 for effects in self.effects.values_mut() {
455 for effect in effects {
456 effect.cleanup();
457 }
458 }
459 self.effects.clear();
460 }
461}
462
463#[cfg(test)]
464mod tests {
465 use super::*;
466 use std::sync::atomic::{AtomicUsize, Ordering};
467 use std::sync::Arc;
468
469 #[test]
471 fn test_lifecycle_phase_equality() {
472 assert_eq!(LifecyclePhase::Mount, LifecyclePhase::Mount);
473 assert_ne!(LifecyclePhase::Mount, LifecyclePhase::Unmount);
474 }
475
476 #[test]
478 fn test_lifecycle_event_new() {
479 let event = LifecycleEvent::new(WidgetId::new(1), LifecyclePhase::Mount, 42);
480 assert_eq!(event.widget_id, WidgetId::new(1));
481 assert_eq!(event.phase, LifecyclePhase::Mount);
482 assert_eq!(event.timestamp, 42);
483 }
484
485 #[test]
487 fn test_hook_id() {
488 let id1 = HookId::new(1);
489 let id2 = HookId::new(1);
490 let id3 = HookId::new(2);
491
492 assert_eq!(id1, id2);
493 assert_ne!(id1, id3);
494 }
495
496 #[test]
498 fn test_manager_new() {
499 let manager = LifecycleManager::new();
500 assert_eq!(manager.hook_count(), 0);
501 assert_eq!(manager.timestamp(), 0);
502 }
503
504 #[test]
505 fn test_manager_register() {
506 let mut manager = LifecycleManager::new();
507 let widget_id = WidgetId::new(1);
508
509 let hook_id = manager.register(widget_id, vec![LifecyclePhase::Mount], Box::new(|_| {}));
510
511 assert_eq!(manager.hook_count(), 1);
512 assert!(manager.has_hooks(widget_id));
513 assert!(!manager.has_hooks(WidgetId::new(999)));
514 assert_eq!(hook_id.0, 0);
515 }
516
517 #[test]
518 fn test_manager_on_mount() {
519 let mut manager = LifecycleManager::new();
520 let widget_id = WidgetId::new(1);
521
522 let _hook_id = manager.on_mount(widget_id, Box::new(|_| {}));
523 assert_eq!(manager.hook_count(), 1);
524 }
525
526 #[test]
527 fn test_manager_on_unmount() {
528 let mut manager = LifecycleManager::new();
529 let widget_id = WidgetId::new(1);
530
531 let _hook_id = manager.on_unmount(widget_id, Box::new(|_| {}));
532 assert_eq!(manager.hook_count(), 1);
533 }
534
535 #[test]
536 fn test_manager_emit() {
537 let counter = Arc::new(AtomicUsize::new(0));
538 let counter_clone = counter.clone();
539
540 let mut manager = LifecycleManager::new();
541 let widget_id = WidgetId::new(1);
542
543 manager.on_mount(
544 widget_id,
545 Box::new(move |_| {
546 counter_clone.fetch_add(1, Ordering::SeqCst);
547 }),
548 );
549
550 manager.emit(widget_id, LifecyclePhase::Mount);
551 assert_eq!(counter.load(Ordering::SeqCst), 1);
552
553 manager.emit(widget_id, LifecyclePhase::Mount);
555 assert_eq!(counter.load(Ordering::SeqCst), 2);
556 }
557
558 #[test]
559 fn test_manager_emit_wrong_phase() {
560 let counter = Arc::new(AtomicUsize::new(0));
561 let counter_clone = counter.clone();
562
563 let mut manager = LifecycleManager::new();
564 let widget_id = WidgetId::new(1);
565
566 manager.on_mount(
567 widget_id,
568 Box::new(move |_| {
569 counter_clone.fetch_add(1, Ordering::SeqCst);
570 }),
571 );
572
573 manager.emit(widget_id, LifecyclePhase::Unmount);
575 assert_eq!(counter.load(Ordering::SeqCst), 0);
576 }
577
578 #[test]
579 fn test_manager_queue_and_flush() {
580 let counter = Arc::new(AtomicUsize::new(0));
581 let counter_clone = counter.clone();
582
583 let mut manager = LifecycleManager::new();
584 let widget_id = WidgetId::new(1);
585
586 manager.on_mount(
587 widget_id,
588 Box::new(move |_| {
589 counter_clone.fetch_add(1, Ordering::SeqCst);
590 }),
591 );
592
593 manager.queue(widget_id, LifecyclePhase::Mount);
594 manager.queue(widget_id, LifecyclePhase::Mount);
595 assert_eq!(manager.pending_count(), 2);
596 assert_eq!(counter.load(Ordering::SeqCst), 0);
597
598 manager.flush();
599 assert_eq!(manager.pending_count(), 0);
600 assert_eq!(counter.load(Ordering::SeqCst), 2);
601 }
602
603 #[test]
604 fn test_manager_unregister() {
605 let mut manager = LifecycleManager::new();
606 let widget_id = WidgetId::new(1);
607
608 let hook_id = manager.on_mount(widget_id, Box::new(|_| {}));
609 assert_eq!(manager.hook_count(), 1);
610
611 let removed = manager.unregister(hook_id);
612 assert!(removed);
613 assert_eq!(manager.hook_count(), 0);
614 assert!(!manager.has_hooks(widget_id));
615 }
616
617 #[test]
618 fn test_manager_unregister_widget() {
619 let mut manager = LifecycleManager::new();
620 let widget_id = WidgetId::new(1);
621
622 manager.on_mount(widget_id, Box::new(|_| {}));
623 manager.on_unmount(widget_id, Box::new(|_| {}));
624 manager.on_update(widget_id, Box::new(|_| {}));
625 assert_eq!(manager.hook_count(), 3);
626
627 manager.unregister_widget(widget_id);
628 assert_eq!(manager.hook_count(), 0);
629 }
630
631 #[test]
632 fn test_manager_tick() {
633 let mut manager = LifecycleManager::new();
634 assert_eq!(manager.timestamp(), 0);
635
636 manager.tick();
637 assert_eq!(manager.timestamp(), 1);
638
639 manager.tick();
640 manager.tick();
641 assert_eq!(manager.timestamp(), 3);
642 }
643
644 #[test]
645 fn test_manager_clear() {
646 let mut manager = LifecycleManager::new();
647 let widget_id = WidgetId::new(1);
648
649 manager.on_mount(widget_id, Box::new(|_| {}));
650 manager.queue(widget_id, LifecyclePhase::Mount);
651
652 manager.clear();
653 assert_eq!(manager.hook_count(), 0);
654 assert_eq!(manager.pending_count(), 0);
655 }
656
657 #[test]
658 fn test_manager_multiple_widgets() {
659 let counter1 = Arc::new(AtomicUsize::new(0));
660 let counter2 = Arc::new(AtomicUsize::new(0));
661 let c1 = counter1.clone();
662 let c2 = counter2.clone();
663
664 let mut manager = LifecycleManager::new();
665
666 manager.on_mount(
667 WidgetId::new(1),
668 Box::new(move |_| {
669 c1.fetch_add(1, Ordering::SeqCst);
670 }),
671 );
672 manager.on_mount(
673 WidgetId::new(2),
674 Box::new(move |_| {
675 c2.fetch_add(1, Ordering::SeqCst);
676 }),
677 );
678
679 manager.emit(WidgetId::new(1), LifecyclePhase::Mount);
680 assert_eq!(counter1.load(Ordering::SeqCst), 1);
681 assert_eq!(counter2.load(Ordering::SeqCst), 0);
682
683 manager.emit(WidgetId::new(2), LifecyclePhase::Mount);
684 assert_eq!(counter1.load(Ordering::SeqCst), 1);
685 assert_eq!(counter2.load(Ordering::SeqCst), 1);
686 }
687
688 #[test]
690 fn test_effect_new() {
691 let effect = Effect::new(|| None);
692 assert!(!effect.has_cleanup());
693 }
694
695 #[test]
696 fn test_effect_with_deps() {
697 let effect = Effect::with_deps(|| None, vec![1, 2, 3]);
698 assert_eq!(effect.deps, vec![1, 2, 3]);
699 }
700
701 #[test]
702 fn test_effect_deps_changed() {
703 let effect = Effect::with_deps(|| None, vec![1, 2, 3]);
704
705 assert!(!effect.deps_changed(&[1, 2, 3]));
706 assert!(effect.deps_changed(&[1, 2, 4]));
707 assert!(effect.deps_changed(&[1, 2]));
708 assert!(effect.deps_changed(&[1, 2, 3, 4]));
709 }
710
711 #[test]
712 fn test_effect_run() {
713 let counter = Arc::new(AtomicUsize::new(0));
714 let c = counter.clone();
715
716 let mut effect = Effect::new(move || {
717 c.fetch_add(1, Ordering::SeqCst);
718 None
719 });
720
721 effect.run(None);
722 assert_eq!(counter.load(Ordering::SeqCst), 1);
723
724 effect.run(None);
726 assert_eq!(counter.load(Ordering::SeqCst), 1);
727 }
728
729 #[test]
730 fn test_effect_cleanup() {
731 let cleanup_counter = Arc::new(AtomicUsize::new(0));
732 let cc = cleanup_counter.clone();
733
734 let mut effect = Effect::new(move || {
735 let cc = cc;
736 let b: Box<dyn FnOnce() + Send> = Box::new(move || {
737 cc.fetch_add(1, Ordering::SeqCst);
738 });
739 Some(b)
740 });
741
742 effect.run(None);
743 assert!(effect.has_cleanup());
744 assert_eq!(cleanup_counter.load(Ordering::SeqCst), 0);
745
746 effect.cleanup();
747 assert!(!effect.has_cleanup());
748 assert_eq!(cleanup_counter.load(Ordering::SeqCst), 1);
749 }
750
751 #[test]
753 fn test_effect_manager_new() {
754 let manager = EffectManager::new();
755 assert_eq!(manager.widget_count(), 0);
756 assert_eq!(manager.effect_count(), 0);
757 }
758
759 #[test]
760 fn test_effect_manager_add() {
761 let mut manager = EffectManager::new();
762 let widget_id = WidgetId::new(1);
763
764 manager.add(widget_id, Effect::new(|| None));
765 manager.add(widget_id, Effect::new(|| None));
766
767 assert_eq!(manager.widget_count(), 1);
768 assert_eq!(manager.effect_count(), 2);
769 }
770
771 #[test]
772 fn test_effect_manager_run_effects() {
773 let counter = Arc::new(AtomicUsize::new(0));
774 let c = counter.clone();
775
776 let mut manager = EffectManager::new();
777 let widget_id = WidgetId::new(1);
778
779 manager.add(
780 widget_id,
781 Effect::new(move || {
782 c.fetch_add(1, Ordering::SeqCst);
783 None
784 }),
785 );
786
787 manager.run_effects(widget_id, None);
788 assert_eq!(counter.load(Ordering::SeqCst), 1);
789 }
790
791 #[test]
792 fn test_effect_manager_cleanup_widget() {
793 let cleanup_counter = Arc::new(AtomicUsize::new(0));
794 let cc = cleanup_counter.clone();
795
796 let mut manager = EffectManager::new();
797 let widget_id = WidgetId::new(1);
798
799 manager.add(
800 widget_id,
801 Effect::new(move || {
802 let cc = cc;
803 let b: Box<dyn FnOnce() + Send> = Box::new(move || {
804 cc.fetch_add(1, Ordering::SeqCst);
805 });
806 Some(b)
807 }),
808 );
809
810 manager.run_effects(widget_id, None);
811 assert_eq!(manager.effect_count(), 1);
812
813 manager.cleanup_widget(widget_id);
814 assert_eq!(manager.effect_count(), 0);
815 assert_eq!(cleanup_counter.load(Ordering::SeqCst), 1);
816 }
817
818 #[test]
819 fn test_effect_manager_clear() {
820 let mut manager = EffectManager::new();
821
822 manager.add(WidgetId::new(1), Effect::new(|| None));
823 manager.add(WidgetId::new(2), Effect::new(|| None));
824
825 manager.clear();
826 assert_eq!(manager.widget_count(), 0);
827 assert_eq!(manager.effect_count(), 0);
828 }
829}