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> = self.pending_events.drain(..).collect();
234
235 for event in events {
236 let widget_hooks = self
237 .by_widget
238 .get(&event.widget_id)
239 .cloned()
240 .unwrap_or_default();
241 let phase_hooks = self.by_phase.get(&event.phase).cloned().unwrap_or_default();
242
243 for hook_id in widget_hooks {
244 if phase_hooks.contains(&hook_id) {
245 if let Some(callback) = self.callbacks.get_mut(&hook_id) {
246 callback(event.clone());
247 }
248 }
249 }
250 }
251 }
252
253 pub fn pending_count(&self) -> usize {
255 self.pending_events.len()
256 }
257
258 pub fn tick(&mut self) {
260 self.timestamp += 1;
261 }
262
263 pub fn timestamp(&self) -> u64 {
265 self.timestamp
266 }
267
268 pub fn hook_count(&self) -> usize {
270 self.hooks.len()
271 }
272
273 pub fn has_hooks(&self, widget_id: WidgetId) -> bool {
275 self.by_widget
276 .get(&widget_id)
277 .is_some_and(|h| !h.is_empty())
278 }
279
280 pub fn clear(&mut self) {
282 self.hooks.clear();
283 self.callbacks.clear();
284 self.by_widget.clear();
285 self.by_phase.clear();
286 self.pending_events.clear();
287 }
288}
289
290impl Default for LifecycleManager {
291 fn default() -> Self {
292 Self::new()
293 }
294}
295
296impl std::fmt::Debug for LifecycleManager {
297 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
298 f.debug_struct("LifecycleManager")
299 .field("next_id", &self.next_id)
300 .field("hook_count", &self.hooks.len())
301 .field("timestamp", &self.timestamp)
302 .field("pending_count", &self.pending_events.len())
303 .finish()
304 }
305}
306
307pub struct Effect {
309 effect: Option<Box<dyn FnOnce() -> Option<Box<dyn FnOnce() + Send>> + Send>>,
311 cleanup: Option<Box<dyn FnOnce() + Send>>,
313 deps: Vec<u64>,
315}
316
317impl Effect {
318 pub fn new<F>(effect: F) -> Self
320 where
321 F: FnOnce() -> Option<Box<dyn FnOnce() + Send>> + Send + 'static,
322 {
323 Self {
324 effect: Some(Box::new(effect)),
325 cleanup: None,
326 deps: Vec::new(),
327 }
328 }
329
330 pub fn with_deps<F>(effect: F, deps: Vec<u64>) -> Self
332 where
333 F: FnOnce() -> Option<Box<dyn FnOnce() + Send>> + Send + 'static,
334 {
335 Self {
336 effect: Some(Box::new(effect)),
337 cleanup: None,
338 deps,
339 }
340 }
341
342 pub fn deps_changed(&self, new_deps: &[u64]) -> bool {
344 if self.deps.len() != new_deps.len() {
345 return true;
346 }
347 self.deps.iter().zip(new_deps).any(|(a, b)| a != b)
348 }
349
350 pub fn run(&mut self, new_deps: Option<&[u64]>) -> bool {
352 let should_run = match new_deps {
354 Some(deps) if !self.deps_changed(deps) => false,
355 _ => true,
356 };
357
358 if !should_run {
359 return false;
360 }
361
362 if let Some(cleanup) = self.cleanup.take() {
364 cleanup();
365 }
366
367 if let Some(effect) = self.effect.take() {
369 self.cleanup = effect();
370 }
371
372 if let Some(deps) = new_deps {
374 self.deps = deps.to_vec();
375 }
376
377 true
378 }
379
380 pub fn cleanup(&mut self) {
382 if let Some(cleanup) = self.cleanup.take() {
383 cleanup();
384 }
385 }
386
387 pub fn has_cleanup(&self) -> bool {
389 self.cleanup.is_some()
390 }
391}
392
393impl std::fmt::Debug for Effect {
394 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
395 f.debug_struct("Effect")
396 .field("has_effect", &self.effect.is_some())
397 .field("has_cleanup", &self.cleanup.is_some())
398 .field("deps", &self.deps)
399 .finish()
400 }
401}
402
403#[derive(Debug, Default)]
405pub struct EffectManager {
406 effects: HashMap<WidgetId, Vec<Effect>>,
408}
409
410impl EffectManager {
411 pub fn new() -> Self {
413 Self::default()
414 }
415
416 pub fn add(&mut self, widget_id: WidgetId, effect: Effect) {
418 self.effects.entry(widget_id).or_default().push(effect);
419 }
420
421 pub fn run_effects(&mut self, widget_id: WidgetId, deps: Option<&[u64]>) {
423 if let Some(effects) = self.effects.get_mut(&widget_id) {
424 for effect in effects {
425 effect.run(deps);
426 }
427 }
428 }
429
430 pub fn cleanup_widget(&mut self, widget_id: WidgetId) {
432 if let Some(effects) = self.effects.get_mut(&widget_id) {
433 for effect in effects {
434 effect.cleanup();
435 }
436 }
437 self.effects.remove(&widget_id);
438 }
439
440 pub fn widget_count(&self) -> usize {
442 self.effects.len()
443 }
444
445 pub fn effect_count(&self) -> usize {
447 self.effects.values().map(std::vec::Vec::len).sum()
448 }
449
450 pub fn clear(&mut self) {
452 for effects in self.effects.values_mut() {
453 for effect in effects {
454 effect.cleanup();
455 }
456 }
457 self.effects.clear();
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464 use std::sync::atomic::{AtomicUsize, Ordering};
465 use std::sync::Arc;
466
467 #[test]
469 fn test_lifecycle_phase_equality() {
470 assert_eq!(LifecyclePhase::Mount, LifecyclePhase::Mount);
471 assert_ne!(LifecyclePhase::Mount, LifecyclePhase::Unmount);
472 }
473
474 #[test]
476 fn test_lifecycle_event_new() {
477 let event = LifecycleEvent::new(WidgetId::new(1), LifecyclePhase::Mount, 42);
478 assert_eq!(event.widget_id, WidgetId::new(1));
479 assert_eq!(event.phase, LifecyclePhase::Mount);
480 assert_eq!(event.timestamp, 42);
481 }
482
483 #[test]
485 fn test_hook_id() {
486 let id1 = HookId::new(1);
487 let id2 = HookId::new(1);
488 let id3 = HookId::new(2);
489
490 assert_eq!(id1, id2);
491 assert_ne!(id1, id3);
492 }
493
494 #[test]
496 fn test_manager_new() {
497 let manager = LifecycleManager::new();
498 assert_eq!(manager.hook_count(), 0);
499 assert_eq!(manager.timestamp(), 0);
500 }
501
502 #[test]
503 fn test_manager_register() {
504 let mut manager = LifecycleManager::new();
505 let widget_id = WidgetId::new(1);
506
507 let hook_id = manager.register(widget_id, vec![LifecyclePhase::Mount], Box::new(|_| {}));
508
509 assert_eq!(manager.hook_count(), 1);
510 assert!(manager.has_hooks(widget_id));
511 assert!(!manager.has_hooks(WidgetId::new(999)));
512 assert_eq!(hook_id.0, 0);
513 }
514
515 #[test]
516 fn test_manager_on_mount() {
517 let mut manager = LifecycleManager::new();
518 let widget_id = WidgetId::new(1);
519
520 let _hook_id = manager.on_mount(widget_id, Box::new(|_| {}));
521 assert_eq!(manager.hook_count(), 1);
522 }
523
524 #[test]
525 fn test_manager_on_unmount() {
526 let mut manager = LifecycleManager::new();
527 let widget_id = WidgetId::new(1);
528
529 let _hook_id = manager.on_unmount(widget_id, Box::new(|_| {}));
530 assert_eq!(manager.hook_count(), 1);
531 }
532
533 #[test]
534 fn test_manager_emit() {
535 let counter = Arc::new(AtomicUsize::new(0));
536 let counter_clone = counter.clone();
537
538 let mut manager = LifecycleManager::new();
539 let widget_id = WidgetId::new(1);
540
541 manager.on_mount(
542 widget_id,
543 Box::new(move |_| {
544 counter_clone.fetch_add(1, Ordering::SeqCst);
545 }),
546 );
547
548 manager.emit(widget_id, LifecyclePhase::Mount);
549 assert_eq!(counter.load(Ordering::SeqCst), 1);
550
551 manager.emit(widget_id, LifecyclePhase::Mount);
553 assert_eq!(counter.load(Ordering::SeqCst), 2);
554 }
555
556 #[test]
557 fn test_manager_emit_wrong_phase() {
558 let counter = Arc::new(AtomicUsize::new(0));
559 let counter_clone = counter.clone();
560
561 let mut manager = LifecycleManager::new();
562 let widget_id = WidgetId::new(1);
563
564 manager.on_mount(
565 widget_id,
566 Box::new(move |_| {
567 counter_clone.fetch_add(1, Ordering::SeqCst);
568 }),
569 );
570
571 manager.emit(widget_id, LifecyclePhase::Unmount);
573 assert_eq!(counter.load(Ordering::SeqCst), 0);
574 }
575
576 #[test]
577 fn test_manager_queue_and_flush() {
578 let counter = Arc::new(AtomicUsize::new(0));
579 let counter_clone = counter.clone();
580
581 let mut manager = LifecycleManager::new();
582 let widget_id = WidgetId::new(1);
583
584 manager.on_mount(
585 widget_id,
586 Box::new(move |_| {
587 counter_clone.fetch_add(1, Ordering::SeqCst);
588 }),
589 );
590
591 manager.queue(widget_id, LifecyclePhase::Mount);
592 manager.queue(widget_id, LifecyclePhase::Mount);
593 assert_eq!(manager.pending_count(), 2);
594 assert_eq!(counter.load(Ordering::SeqCst), 0);
595
596 manager.flush();
597 assert_eq!(manager.pending_count(), 0);
598 assert_eq!(counter.load(Ordering::SeqCst), 2);
599 }
600
601 #[test]
602 fn test_manager_unregister() {
603 let mut manager = LifecycleManager::new();
604 let widget_id = WidgetId::new(1);
605
606 let hook_id = manager.on_mount(widget_id, Box::new(|_| {}));
607 assert_eq!(manager.hook_count(), 1);
608
609 let removed = manager.unregister(hook_id);
610 assert!(removed);
611 assert_eq!(manager.hook_count(), 0);
612 assert!(!manager.has_hooks(widget_id));
613 }
614
615 #[test]
616 fn test_manager_unregister_widget() {
617 let mut manager = LifecycleManager::new();
618 let widget_id = WidgetId::new(1);
619
620 manager.on_mount(widget_id, Box::new(|_| {}));
621 manager.on_unmount(widget_id, Box::new(|_| {}));
622 manager.on_update(widget_id, Box::new(|_| {}));
623 assert_eq!(manager.hook_count(), 3);
624
625 manager.unregister_widget(widget_id);
626 assert_eq!(manager.hook_count(), 0);
627 }
628
629 #[test]
630 fn test_manager_tick() {
631 let mut manager = LifecycleManager::new();
632 assert_eq!(manager.timestamp(), 0);
633
634 manager.tick();
635 assert_eq!(manager.timestamp(), 1);
636
637 manager.tick();
638 manager.tick();
639 assert_eq!(manager.timestamp(), 3);
640 }
641
642 #[test]
643 fn test_manager_clear() {
644 let mut manager = LifecycleManager::new();
645 let widget_id = WidgetId::new(1);
646
647 manager.on_mount(widget_id, Box::new(|_| {}));
648 manager.queue(widget_id, LifecyclePhase::Mount);
649
650 manager.clear();
651 assert_eq!(manager.hook_count(), 0);
652 assert_eq!(manager.pending_count(), 0);
653 }
654
655 #[test]
656 fn test_manager_multiple_widgets() {
657 let counter1 = Arc::new(AtomicUsize::new(0));
658 let counter2 = Arc::new(AtomicUsize::new(0));
659 let c1 = counter1.clone();
660 let c2 = counter2.clone();
661
662 let mut manager = LifecycleManager::new();
663
664 manager.on_mount(
665 WidgetId::new(1),
666 Box::new(move |_| {
667 c1.fetch_add(1, Ordering::SeqCst);
668 }),
669 );
670 manager.on_mount(
671 WidgetId::new(2),
672 Box::new(move |_| {
673 c2.fetch_add(1, Ordering::SeqCst);
674 }),
675 );
676
677 manager.emit(WidgetId::new(1), LifecyclePhase::Mount);
678 assert_eq!(counter1.load(Ordering::SeqCst), 1);
679 assert_eq!(counter2.load(Ordering::SeqCst), 0);
680
681 manager.emit(WidgetId::new(2), LifecyclePhase::Mount);
682 assert_eq!(counter1.load(Ordering::SeqCst), 1);
683 assert_eq!(counter2.load(Ordering::SeqCst), 1);
684 }
685
686 #[test]
688 fn test_effect_new() {
689 let effect = Effect::new(|| None);
690 assert!(!effect.has_cleanup());
691 }
692
693 #[test]
694 fn test_effect_with_deps() {
695 let effect = Effect::with_deps(|| None, vec![1, 2, 3]);
696 assert_eq!(effect.deps, vec![1, 2, 3]);
697 }
698
699 #[test]
700 fn test_effect_deps_changed() {
701 let effect = Effect::with_deps(|| None, vec![1, 2, 3]);
702
703 assert!(!effect.deps_changed(&[1, 2, 3]));
704 assert!(effect.deps_changed(&[1, 2, 4]));
705 assert!(effect.deps_changed(&[1, 2]));
706 assert!(effect.deps_changed(&[1, 2, 3, 4]));
707 }
708
709 #[test]
710 fn test_effect_run() {
711 let counter = Arc::new(AtomicUsize::new(0));
712 let c = counter.clone();
713
714 let mut effect = Effect::new(move || {
715 c.fetch_add(1, Ordering::SeqCst);
716 None
717 });
718
719 effect.run(None);
720 assert_eq!(counter.load(Ordering::SeqCst), 1);
721
722 effect.run(None);
724 assert_eq!(counter.load(Ordering::SeqCst), 1);
725 }
726
727 #[test]
728 fn test_effect_cleanup() {
729 let cleanup_counter = Arc::new(AtomicUsize::new(0));
730 let cc = cleanup_counter.clone();
731
732 let mut effect = Effect::new(move || {
733 let cc = cc.clone();
734 Some(Box::new(move || {
735 cc.fetch_add(1, Ordering::SeqCst);
736 }) as Box<dyn FnOnce() + Send>)
737 });
738
739 effect.run(None);
740 assert!(effect.has_cleanup());
741 assert_eq!(cleanup_counter.load(Ordering::SeqCst), 0);
742
743 effect.cleanup();
744 assert!(!effect.has_cleanup());
745 assert_eq!(cleanup_counter.load(Ordering::SeqCst), 1);
746 }
747
748 #[test]
750 fn test_effect_manager_new() {
751 let manager = EffectManager::new();
752 assert_eq!(manager.widget_count(), 0);
753 assert_eq!(manager.effect_count(), 0);
754 }
755
756 #[test]
757 fn test_effect_manager_add() {
758 let mut manager = EffectManager::new();
759 let widget_id = WidgetId::new(1);
760
761 manager.add(widget_id, Effect::new(|| None));
762 manager.add(widget_id, Effect::new(|| None));
763
764 assert_eq!(manager.widget_count(), 1);
765 assert_eq!(manager.effect_count(), 2);
766 }
767
768 #[test]
769 fn test_effect_manager_run_effects() {
770 let counter = Arc::new(AtomicUsize::new(0));
771 let c = counter.clone();
772
773 let mut manager = EffectManager::new();
774 let widget_id = WidgetId::new(1);
775
776 manager.add(
777 widget_id,
778 Effect::new(move || {
779 c.fetch_add(1, Ordering::SeqCst);
780 None
781 }),
782 );
783
784 manager.run_effects(widget_id, None);
785 assert_eq!(counter.load(Ordering::SeqCst), 1);
786 }
787
788 #[test]
789 fn test_effect_manager_cleanup_widget() {
790 let cleanup_counter = Arc::new(AtomicUsize::new(0));
791 let cc = cleanup_counter.clone();
792
793 let mut manager = EffectManager::new();
794 let widget_id = WidgetId::new(1);
795
796 manager.add(
797 widget_id,
798 Effect::new(move || {
799 let cc = cc.clone();
800 Some(Box::new(move || {
801 cc.fetch_add(1, Ordering::SeqCst);
802 }) as Box<dyn FnOnce() + Send>)
803 }),
804 );
805
806 manager.run_effects(widget_id, None);
807 assert_eq!(manager.effect_count(), 1);
808
809 manager.cleanup_widget(widget_id);
810 assert_eq!(manager.effect_count(), 0);
811 assert_eq!(cleanup_counter.load(Ordering::SeqCst), 1);
812 }
813
814 #[test]
815 fn test_effect_manager_clear() {
816 let mut manager = EffectManager::new();
817
818 manager.add(WidgetId::new(1), Effect::new(|| None));
819 manager.add(WidgetId::new(2), Effect::new(|| None));
820
821 manager.clear();
822 assert_eq!(manager.widget_count(), 0);
823 assert_eq!(manager.effect_count(), 0);
824 }
825}