1use crate::state::Command;
7use std::collections::HashMap;
8use std::sync::{Arc, Mutex};
9
10pub trait Router: Send + Sync {
12 fn navigate(&self, route: &str);
14
15 fn current_route(&self) -> String;
17}
18
19pub trait Storage: Send + Sync {
21 fn save(&self, key: &str, data: &[u8]);
23
24 fn load(&self, key: &str) -> Option<Vec<u8>>;
26
27 fn remove(&self, key: &str);
29
30 fn contains(&self, key: &str) -> bool;
32}
33
34#[derive(Debug, Default)]
36pub struct MemoryStorage {
37 data: Mutex<HashMap<String, Vec<u8>>>,
38}
39
40impl MemoryStorage {
41 #[must_use]
43 pub fn new() -> Self {
44 Self::default()
45 }
46
47 #[must_use]
49 pub fn len(&self) -> usize {
50 self.data
51 .lock()
52 .expect("MemoryStorage mutex poisoned")
53 .len()
54 }
55
56 #[must_use]
58 pub fn is_empty(&self) -> bool {
59 self.data
60 .lock()
61 .expect("MemoryStorage mutex poisoned")
62 .is_empty()
63 }
64
65 pub fn clear(&self) {
67 self.data
68 .lock()
69 .expect("MemoryStorage mutex poisoned")
70 .clear();
71 }
72}
73
74impl Storage for MemoryStorage {
75 fn save(&self, key: &str, data: &[u8]) {
76 self.data
77 .lock()
78 .expect("MemoryStorage mutex poisoned")
79 .insert(key.to_string(), data.to_vec());
80 }
81
82 fn load(&self, key: &str) -> Option<Vec<u8>> {
83 self.data
84 .lock()
85 .expect("MemoryStorage mutex poisoned")
86 .get(key)
87 .cloned()
88 }
89
90 fn remove(&self, key: &str) {
91 self.data
92 .lock()
93 .expect("MemoryStorage mutex poisoned")
94 .remove(key);
95 }
96
97 fn contains(&self, key: &str) -> bool {
98 self.data
99 .lock()
100 .expect("MemoryStorage mutex poisoned")
101 .contains_key(key)
102 }
103}
104
105#[derive(Debug)]
107pub struct MemoryRouter {
108 route: Mutex<String>,
109 history: Mutex<Vec<String>>,
110}
111
112impl Default for MemoryRouter {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118impl MemoryRouter {
119 #[must_use]
121 pub fn new() -> Self {
122 Self {
123 route: Mutex::new("/".to_string()),
124 history: Mutex::new(vec!["/".to_string()]),
125 }
126 }
127
128 #[must_use]
130 pub fn history(&self) -> Vec<String> {
131 self.history
132 .lock()
133 .expect("MemoryRouter mutex poisoned")
134 .clone()
135 }
136
137 #[must_use]
139 pub fn history_len(&self) -> usize {
140 self.history
141 .lock()
142 .expect("MemoryRouter mutex poisoned")
143 .len()
144 }
145}
146
147impl Router for MemoryRouter {
148 fn navigate(&self, route: &str) {
149 let mut current = self.route.lock().expect("MemoryRouter mutex poisoned");
150 *current = route.to_string();
151 self.history
152 .lock()
153 .expect("MemoryRouter mutex poisoned")
154 .push(route.to_string());
155 }
156
157 fn current_route(&self) -> String {
158 self.route
159 .lock()
160 .expect("MemoryRouter mutex poisoned")
161 .clone()
162 }
163}
164
165#[derive(Debug)]
167pub enum ExecutionResult<M> {
168 None,
170 Message(M),
172 Messages(Vec<M>),
174 Pending,
176}
177
178impl<M> ExecutionResult<M> {
179 #[must_use]
181 pub const fn is_none(&self) -> bool {
182 matches!(self, Self::None)
183 }
184
185 #[must_use]
187 pub const fn has_messages(&self) -> bool {
188 matches!(self, Self::Message(_) | Self::Messages(_))
189 }
190
191 pub fn into_messages(self) -> Vec<M> {
193 match self {
194 Self::None | Self::Pending => vec![],
195 Self::Message(m) => vec![m],
196 Self::Messages(ms) => ms,
197 }
198 }
199}
200
201pub struct ExecutorConfig<R, S> {
203 pub router: Arc<R>,
205 pub storage: Arc<S>,
207}
208
209impl<R: Router, S: Storage> ExecutorConfig<R, S> {
210 pub fn new(router: R, storage: S) -> Self {
212 Self {
213 router: Arc::new(router),
214 storage: Arc::new(storage),
215 }
216 }
217}
218
219pub struct CommandExecutor<R, S> {
223 config: ExecutorConfig<R, S>,
224}
225
226impl<R: Router, S: Storage> CommandExecutor<R, S> {
227 pub const fn new(config: ExecutorConfig<R, S>) -> Self {
229 Self { config }
230 }
231
232 pub fn execute<M: Send>(&self, command: Command<M>) -> ExecutionResult<M> {
237 match command {
238 Command::None => ExecutionResult::None,
239 Command::Batch(commands) => {
240 let mut messages = Vec::new();
241 for cmd in commands {
242 match self.execute(cmd) {
243 ExecutionResult::None | ExecutionResult::Pending => {}
244 ExecutionResult::Message(m) => messages.push(m),
245 ExecutionResult::Messages(ms) => messages.extend(ms),
246 }
247 }
248 if messages.is_empty() {
249 ExecutionResult::None
250 } else {
251 ExecutionResult::Messages(messages)
252 }
253 }
254 Command::Task(_) => {
255 ExecutionResult::Pending
257 }
258 Command::Navigate { route } => {
259 self.config.router.navigate(&route);
260 ExecutionResult::None
261 }
262 Command::SaveState { key } => {
263 let _ = key;
268 ExecutionResult::None
269 }
270 Command::LoadState { key, on_load } => {
271 let data = self.config.storage.load(&key);
272 let message = on_load(data);
273 ExecutionResult::Message(message)
274 }
275 }
276 }
277
278 pub fn router(&self) -> &R {
280 &self.config.router
281 }
282
283 pub fn storage(&self) -> &S {
285 &self.config.storage
286 }
287}
288
289#[must_use]
291pub fn default_executor() -> CommandExecutor<MemoryRouter, MemoryStorage> {
292 CommandExecutor::new(ExecutorConfig::new(
293 MemoryRouter::new(),
294 MemoryStorage::new(),
295 ))
296}
297
298#[derive(Debug, Clone, Copy, PartialEq, Eq)]
304pub enum FocusDirection {
305 Forward,
307 Backward,
309 Up,
311 Down,
313 Left,
315 Right,
317}
318
319#[derive(Debug, Default)]
321pub struct FocusManager {
322 focused: Option<u64>,
324 focus_ring: Vec<u64>,
326 traps: Vec<FocusTrap>,
328}
329
330#[derive(Debug)]
332pub struct FocusTrap {
333 pub widget_ids: Vec<u64>,
335 pub initial_focus: Option<u64>,
337}
338
339impl FocusManager {
340 #[must_use]
342 pub fn new() -> Self {
343 Self::default()
344 }
345
346 pub fn set_focus_ring(&mut self, widget_ids: Vec<u64>) {
348 self.focus_ring = widget_ids;
349 }
350
351 #[must_use]
353 pub const fn focused(&self) -> Option<u64> {
354 self.focused
355 }
356
357 pub fn focus(&mut self, widget_id: u64) -> bool {
359 let available = self.available_focus_ring();
360 if available.contains(&widget_id) {
361 self.focused = Some(widget_id);
362 true
363 } else {
364 false
365 }
366 }
367
368 pub fn blur(&mut self) {
370 self.focused = None;
371 }
372
373 pub fn move_focus(&mut self, direction: FocusDirection) -> Option<u64> {
375 let ring = self.available_focus_ring();
376 if ring.is_empty() {
377 return None;
378 }
379
380 let current_idx = self
381 .focused
382 .and_then(|f| ring.iter().position(|&id| id == f));
383
384 let next_idx = match direction {
385 FocusDirection::Forward | FocusDirection::Down | FocusDirection::Right => {
386 match current_idx {
387 Some(idx) => (idx + 1) % ring.len(),
388 None => 0,
389 }
390 }
391 FocusDirection::Backward | FocusDirection::Up | FocusDirection::Left => {
392 match current_idx {
393 Some(0) | None => ring.len() - 1,
394 Some(idx) => idx - 1,
395 }
396 }
397 };
398
399 let next_id = ring[next_idx];
400 self.focused = Some(next_id);
401 Some(next_id)
402 }
403
404 pub fn push_trap(&mut self, widget_ids: Vec<u64>) {
406 let initial = self.focused;
407 self.traps.push(FocusTrap {
408 widget_ids,
409 initial_focus: initial,
410 });
411 if let Some(first) = self.available_focus_ring().first().copied() {
413 self.focused = Some(first);
414 }
415 }
416
417 pub fn pop_trap(&mut self) -> Option<FocusTrap> {
419 let trap = self.traps.pop();
420 if let Some(ref t) = trap {
422 self.focused = t.initial_focus;
423 }
424 trap
425 }
426
427 #[must_use]
429 pub fn is_trapped(&self) -> bool {
430 !self.traps.is_empty()
431 }
432
433 fn available_focus_ring(&self) -> Vec<u64> {
435 if let Some(trap) = self.traps.last() {
436 trap.widget_ids.clone()
437 } else {
438 self.focus_ring.clone()
439 }
440 }
441
442 #[must_use]
444 pub fn is_focusable(&self, widget_id: u64) -> bool {
445 self.available_focus_ring().contains(&widget_id)
446 }
447}
448
449#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
455pub enum EasingFunction {
456 #[default]
458 Linear,
459 EaseInQuad,
461 EaseOutQuad,
463 EaseInOutQuad,
465 EaseInCubic,
467 EaseOutCubic,
469 EaseInOutCubic,
471 EaseOutElastic,
473 EaseOutBounce,
475}
476
477impl EasingFunction {
478 #[must_use]
480 #[allow(clippy::suboptimal_flops)]
481 pub fn apply(self, t: f32) -> f32 {
482 let t = t.clamp(0.0, 1.0);
483 match self {
484 Self::Linear => t,
485 Self::EaseInQuad => t * t,
486 Self::EaseOutQuad => 1.0 - (1.0 - t) * (1.0 - t),
487 Self::EaseInOutQuad => {
488 if t < 0.5 {
489 2.0 * t * t
490 } else {
491 1.0 - (-2.0 * t + 2.0).powi(2) / 2.0
492 }
493 }
494 Self::EaseInCubic => t * t * t,
495 Self::EaseOutCubic => 1.0 - (1.0 - t).powi(3),
496 Self::EaseInOutCubic => {
497 if t < 0.5 {
498 4.0 * t * t * t
499 } else {
500 1.0 - (-2.0 * t + 2.0).powi(3) / 2.0
501 }
502 }
503 Self::EaseOutElastic => {
504 if t == 0.0 || t == 1.0 {
505 t
506 } else {
507 let c4 = (2.0 * std::f32::consts::PI) / 3.0;
508 2.0_f32.powf(-10.0 * t) * ((t * 10.0 - 0.75) * c4).sin() + 1.0
509 }
510 }
511 Self::EaseOutBounce => {
512 let n1 = 7.5625;
513 let d1 = 2.75;
514 if t < 1.0 / d1 {
515 n1 * t * t
516 } else if t < 2.0 / d1 {
517 let t = t - 1.5 / d1;
518 n1 * t * t + 0.75
519 } else if t < 2.5 / d1 {
520 let t = t - 2.25 / d1;
521 n1 * t * t + 0.9375
522 } else {
523 let t = t - 2.625 / d1;
524 n1 * t * t + 0.984_375
525 }
526 }
527 }
528 }
529}
530
531#[derive(Debug, Clone)]
533pub struct Tween<T> {
534 pub from: T,
536 pub to: T,
538 pub duration_ms: u32,
540 pub easing: EasingFunction,
542 elapsed_ms: u32,
544}
545
546impl<T: Clone> Tween<T> {
547 pub fn new(from: T, to: T, duration_ms: u32) -> Self {
549 Self {
550 from,
551 to,
552 duration_ms,
553 easing: EasingFunction::default(),
554 elapsed_ms: 0,
555 }
556 }
557
558 #[must_use]
560 pub const fn with_easing(mut self, easing: EasingFunction) -> Self {
561 self.easing = easing;
562 self
563 }
564
565 #[must_use]
567 pub fn progress(&self) -> f32 {
568 if self.duration_ms == 0 {
569 1.0
570 } else {
571 (self.elapsed_ms as f32 / self.duration_ms as f32).min(1.0)
572 }
573 }
574
575 #[must_use]
577 pub fn eased_progress(&self) -> f32 {
578 self.easing.apply(self.progress())
579 }
580
581 #[must_use]
583 pub const fn is_complete(&self) -> bool {
584 self.elapsed_ms >= self.duration_ms
585 }
586
587 pub fn advance(&mut self, delta_ms: u32) {
589 self.elapsed_ms = self
590 .elapsed_ms
591 .saturating_add(delta_ms)
592 .min(self.duration_ms);
593 }
594
595 pub fn reset(&mut self) {
597 self.elapsed_ms = 0;
598 }
599}
600
601impl Tween<f32> {
602 #[must_use]
604 #[allow(clippy::suboptimal_flops)]
605 pub fn value(&self) -> f32 {
606 let t = self.eased_progress();
607 self.from + (self.to - self.from) * t
608 }
609}
610
611impl Tween<f64> {
612 #[must_use]
614 #[allow(clippy::suboptimal_flops)]
615 pub fn value(&self) -> f64 {
616 let t = f64::from(self.eased_progress());
617 self.from + (self.to - self.from) * t
618 }
619}
620
621#[derive(Debug, Clone, Copy, PartialEq, Eq)]
623pub enum AnimationState {
624 Idle,
626 Running,
628 Paused,
630 Completed,
632}
633
634pub type AnimationId = u64;
636
637#[derive(Debug)]
639pub struct AnimationInstance {
640 pub id: AnimationId,
642 pub tween: Tween<f32>,
644 pub state: AnimationState,
646 pub loop_count: u32,
648 pub current_loop: u32,
650 pub alternate: bool,
652 forward: bool,
654}
655
656impl AnimationInstance {
657 pub fn new(id: AnimationId, from: f32, to: f32, duration_ms: u32) -> Self {
659 Self {
660 id,
661 tween: Tween::new(from, to, duration_ms),
662 state: AnimationState::Idle,
663 loop_count: 1,
664 current_loop: 0,
665 alternate: false,
666 forward: true,
667 }
668 }
669
670 #[must_use]
672 pub const fn with_easing(mut self, easing: EasingFunction) -> Self {
673 self.tween = self.tween.with_easing(easing);
674 self
675 }
676
677 #[must_use]
679 pub const fn with_loop_count(mut self, count: u32) -> Self {
680 self.loop_count = count;
681 self
682 }
683
684 #[must_use]
686 pub const fn with_alternate(mut self, alternate: bool) -> Self {
687 self.alternate = alternate;
688 self
689 }
690
691 pub fn start(&mut self) {
693 self.state = AnimationState::Running;
694 self.current_loop = 0;
695 self.forward = true;
696 self.tween.reset();
697 }
698
699 pub fn pause(&mut self) {
701 if self.state == AnimationState::Running {
702 self.state = AnimationState::Paused;
703 }
704 }
705
706 pub fn resume(&mut self) {
708 if self.state == AnimationState::Paused {
709 self.state = AnimationState::Running;
710 }
711 }
712
713 pub fn stop(&mut self) {
715 self.state = AnimationState::Idle;
716 self.tween.reset();
717 }
718
719 #[must_use]
721 #[allow(clippy::suboptimal_flops)]
722 pub fn value(&self) -> f32 {
723 if self.forward {
724 self.tween.value()
725 } else {
726 self.tween.from
727 + (self.tween.to - self.tween.from) * (1.0 - self.tween.eased_progress())
728 }
729 }
730
731 pub fn advance(&mut self, delta_ms: u32) {
733 if self.state != AnimationState::Running {
734 return;
735 }
736
737 self.tween.advance(delta_ms);
738
739 if self.tween.is_complete() {
740 if self.loop_count == 0 || self.current_loop + 1 < self.loop_count {
742 self.current_loop += 1;
743 self.tween.reset();
744
745 if self.alternate {
746 self.forward = !self.forward;
747 }
748 } else {
749 self.state = AnimationState::Completed;
750 }
751 }
752 }
753}
754
755#[derive(Debug, Default)]
757pub struct Animator {
758 animations: Vec<AnimationInstance>,
759 next_id: AnimationId,
760}
761
762impl Animator {
763 #[must_use]
765 pub fn new() -> Self {
766 Self::default()
767 }
768
769 pub fn create(&mut self, from: f32, to: f32, duration_ms: u32) -> AnimationId {
771 let id = self.next_id;
772 self.next_id += 1;
773 self.animations
774 .push(AnimationInstance::new(id, from, to, duration_ms));
775 id
776 }
777
778 #[must_use]
780 pub fn get(&self, id: AnimationId) -> Option<&AnimationInstance> {
781 self.animations.iter().find(|a| a.id == id)
782 }
783
784 pub fn get_mut(&mut self, id: AnimationId) -> Option<&mut AnimationInstance> {
786 self.animations.iter_mut().find(|a| a.id == id)
787 }
788
789 pub fn start(&mut self, id: AnimationId) {
791 if let Some(anim) = self.get_mut(id) {
792 anim.start();
793 }
794 }
795
796 pub fn pause(&mut self, id: AnimationId) {
798 if let Some(anim) = self.get_mut(id) {
799 anim.pause();
800 }
801 }
802
803 pub fn resume(&mut self, id: AnimationId) {
805 if let Some(anim) = self.get_mut(id) {
806 anim.resume();
807 }
808 }
809
810 pub fn stop(&mut self, id: AnimationId) {
812 if let Some(anim) = self.get_mut(id) {
813 anim.stop();
814 }
815 }
816
817 pub fn remove(&mut self, id: AnimationId) {
819 self.animations.retain(|a| a.id != id);
820 }
821
822 pub fn advance(&mut self, delta_ms: u32) {
824 for anim in &mut self.animations {
825 anim.advance(delta_ms);
826 }
827 }
828
829 #[must_use]
831 pub fn value(&self, id: AnimationId) -> Option<f32> {
832 self.get(id).map(AnimationInstance::value)
833 }
834
835 #[must_use]
837 pub fn len(&self) -> usize {
838 self.animations.len()
839 }
840
841 #[must_use]
843 pub fn is_empty(&self) -> bool {
844 self.animations.is_empty()
845 }
846
847 pub fn cleanup_completed(&mut self) {
849 self.animations
850 .retain(|a| a.state != AnimationState::Completed);
851 }
852
853 #[must_use]
855 pub fn has_running(&self) -> bool {
856 self.animations
857 .iter()
858 .any(|a| a.state == AnimationState::Running)
859 }
860}
861
862#[derive(Debug)]
864pub struct Timer {
865 pub interval_ms: u32,
867 elapsed_ms: u32,
869 running: bool,
871 tick_count: u64,
873 max_ticks: u64,
875}
876
877impl Timer {
878 #[must_use]
880 pub const fn new(interval_ms: u32) -> Self {
881 Self {
882 interval_ms,
883 elapsed_ms: 0,
884 running: false,
885 tick_count: 0,
886 max_ticks: 0,
887 }
888 }
889
890 #[must_use]
892 pub const fn with_max_ticks(mut self, max: u64) -> Self {
893 self.max_ticks = max;
894 self
895 }
896
897 pub fn start(&mut self) {
899 self.running = true;
900 }
901
902 pub fn stop(&mut self) {
904 self.running = false;
905 }
906
907 pub fn reset(&mut self) {
909 self.elapsed_ms = 0;
910 self.tick_count = 0;
911 }
912
913 #[must_use]
915 pub const fn is_running(&self) -> bool {
916 self.running
917 }
918
919 #[must_use]
921 pub const fn tick_count(&self) -> u64 {
922 self.tick_count
923 }
924
925 pub fn advance(&mut self, delta_ms: u32) -> u32 {
927 if !self.running || self.interval_ms == 0 {
928 return 0;
929 }
930
931 self.elapsed_ms += delta_ms;
932 let ticks = self.elapsed_ms / self.interval_ms;
933 self.elapsed_ms %= self.interval_ms;
934
935 let mut actual_ticks = 0;
937 for _ in 0..ticks {
938 if self.max_ticks > 0 && self.tick_count >= self.max_ticks {
939 self.running = false;
940 break;
941 }
942 self.tick_count += 1;
943 actual_ticks += 1;
944 }
945
946 actual_ticks
947 }
948
949 #[must_use]
951 pub fn progress(&self) -> f32 {
952 if self.interval_ms == 0 {
953 0.0
954 } else {
955 self.elapsed_ms as f32 / self.interval_ms as f32
956 }
957 }
958}
959
960#[derive(Debug)]
962pub struct FrameTimer {
963 target_frame_us: u64,
965 last_frame_us: Option<u64>,
967 frame_times: [u64; 60],
969 frame_index: usize,
971 delta_count: usize,
973 total_frames: u64,
975}
976
977impl Default for FrameTimer {
978 fn default() -> Self {
979 Self::new(60)
980 }
981}
982
983impl FrameTimer {
984 #[must_use]
986 pub fn new(target_fps: u32) -> Self {
987 let target_frame_us = if target_fps > 0 {
988 1_000_000 / u64::from(target_fps)
989 } else {
990 16667
991 };
992 Self {
993 target_frame_us,
994 last_frame_us: None,
995 frame_times: [0; 60],
996 frame_index: 0,
997 delta_count: 0,
998 total_frames: 0,
999 }
1000 }
1001
1002 pub fn frame(&mut self, now_us: u64) {
1004 if let Some(last) = self.last_frame_us {
1005 let delta = now_us.saturating_sub(last);
1006 self.frame_times[self.frame_index] = delta;
1007 self.frame_index = (self.frame_index + 1) % 60;
1008 self.delta_count = (self.delta_count + 1).min(60);
1009 }
1010 self.last_frame_us = Some(now_us);
1011 self.total_frames += 1;
1012 }
1013
1014 #[must_use]
1016 pub fn average_frame_time_us(&self) -> u64 {
1017 if self.delta_count == 0 {
1018 return self.target_frame_us;
1019 }
1020 let sum: u64 = self.frame_times[..self.delta_count].iter().sum();
1021 sum / self.delta_count as u64
1022 }
1023
1024 #[must_use]
1026 pub fn fps(&self) -> f32 {
1027 let avg = self.average_frame_time_us();
1028 if avg == 0 {
1029 0.0
1030 } else {
1031 1_000_000.0 / avg as f32
1032 }
1033 }
1034
1035 #[must_use]
1037 pub fn is_on_target(&self) -> bool {
1038 let avg = self.average_frame_time_us();
1039 let target = self.target_frame_us;
1040 avg <= target + target / 10
1042 }
1043
1044 #[must_use]
1046 pub fn target_frame_ms(&self) -> f32 {
1047 self.target_frame_us as f32 / 1000.0
1048 }
1049
1050 #[must_use]
1052 pub const fn total_frames(&self) -> u64 {
1053 self.total_frames
1054 }
1055}
1056
1057#[derive(Debug)]
1063pub struct DataRefreshManager {
1064 tasks: Vec<RefreshTask>,
1066 current_time_ms: u64,
1068}
1069
1070#[derive(Debug, Clone)]
1072pub struct RefreshTask {
1073 pub key: String,
1075 pub interval_ms: u64,
1077 pub last_refresh_ms: u64,
1079 pub active: bool,
1081}
1082
1083impl DataRefreshManager {
1084 #[must_use]
1086 pub const fn new() -> Self {
1087 Self {
1088 tasks: Vec::new(),
1089 current_time_ms: 0,
1090 }
1091 }
1092
1093 pub fn register(&mut self, key: impl Into<String>, interval_ms: u64) {
1100 let key = key.into();
1101
1102 if let Some(task) = self.tasks.iter_mut().find(|t| t.key == key) {
1104 task.interval_ms = interval_ms;
1105 task.active = true;
1106 return;
1107 }
1108
1109 self.tasks.push(RefreshTask {
1110 key,
1111 interval_ms,
1112 last_refresh_ms: 0,
1113 active: true,
1114 });
1115 }
1116
1117 pub fn unregister(&mut self, key: &str) {
1119 self.tasks.retain(|t| t.key != key);
1120 }
1121
1122 pub fn pause(&mut self, key: &str) {
1124 if let Some(task) = self.tasks.iter_mut().find(|t| t.key == key) {
1125 task.active = false;
1126 }
1127 }
1128
1129 pub fn resume(&mut self, key: &str) {
1131 if let Some(task) = self.tasks.iter_mut().find(|t| t.key == key) {
1132 task.active = true;
1133 }
1134 }
1135
1136 pub fn update(&mut self, current_time_ms: u64) -> Vec<String> {
1140 self.current_time_ms = current_time_ms;
1141
1142 let mut to_refresh = Vec::new();
1143
1144 for task in &mut self.tasks {
1145 if !task.active {
1146 continue;
1147 }
1148
1149 let elapsed = current_time_ms.saturating_sub(task.last_refresh_ms);
1150 if elapsed >= task.interval_ms {
1151 to_refresh.push(task.key.clone());
1152 task.last_refresh_ms = current_time_ms;
1153 }
1154 }
1155
1156 to_refresh
1157 }
1158
1159 pub fn force_refresh(&mut self, key: &str) -> bool {
1161 if let Some(task) = self.tasks.iter_mut().find(|t| t.key == key) {
1162 task.last_refresh_ms = 0;
1163 true
1164 } else {
1165 false
1166 }
1167 }
1168
1169 #[must_use]
1171 pub fn tasks(&self) -> &[RefreshTask] {
1172 &self.tasks
1173 }
1174
1175 #[must_use]
1177 pub fn get_task(&self, key: &str) -> Option<&RefreshTask> {
1178 self.tasks.iter().find(|t| t.key == key)
1179 }
1180
1181 #[must_use]
1183 pub fn is_due(&self, key: &str) -> bool {
1184 if let Some(task) = self.tasks.iter().find(|t| t.key == key) {
1185 if !task.active {
1186 return false;
1187 }
1188 let elapsed = self.current_time_ms.saturating_sub(task.last_refresh_ms);
1189 elapsed >= task.interval_ms
1190 } else {
1191 false
1192 }
1193 }
1194
1195 #[must_use]
1197 pub fn time_until_refresh(&self, key: &str) -> Option<u64> {
1198 self.tasks.iter().find(|t| t.key == key).map(|task| {
1199 if !task.active {
1200 return u64::MAX;
1201 }
1202 let elapsed = self.current_time_ms.saturating_sub(task.last_refresh_ms);
1203 task.interval_ms.saturating_sub(elapsed)
1204 })
1205 }
1206}
1207
1208impl Default for DataRefreshManager {
1209 fn default() -> Self {
1210 Self::new()
1211 }
1212}
1213
1214#[derive(Debug, Clone)]
1220pub struct TransitionConfig {
1221 pub duration_ms: u32,
1223 pub easing: EasingFunction,
1225 pub delay_ms: u32,
1227}
1228
1229impl Default for TransitionConfig {
1230 fn default() -> Self {
1231 Self {
1232 duration_ms: 300,
1233 easing: EasingFunction::EaseInOutCubic,
1234 delay_ms: 0,
1235 }
1236 }
1237}
1238
1239impl TransitionConfig {
1240 #[must_use]
1242 pub const fn new(duration_ms: u32) -> Self {
1243 Self {
1244 duration_ms,
1245 easing: EasingFunction::EaseInOutCubic,
1246 delay_ms: 0,
1247 }
1248 }
1249
1250 #[must_use]
1252 pub const fn with_easing(mut self, easing: EasingFunction) -> Self {
1253 self.easing = easing;
1254 self
1255 }
1256
1257 #[must_use]
1259 pub const fn with_delay(mut self, delay_ms: u32) -> Self {
1260 self.delay_ms = delay_ms;
1261 self
1262 }
1263
1264 #[must_use]
1266 pub const fn quick() -> Self {
1267 Self::new(150)
1268 }
1269
1270 #[must_use]
1272 pub const fn normal() -> Self {
1273 Self::new(300)
1274 }
1275
1276 #[must_use]
1278 pub const fn slow() -> Self {
1279 Self::new(500)
1280 }
1281}
1282
1283#[derive(Debug, Clone)]
1287pub struct AnimatedProperty<T> {
1288 current: T,
1290 target: T,
1292 start: T,
1294 config: TransitionConfig,
1296 elapsed_ms: u32,
1298 animating: bool,
1300}
1301
1302impl<T: Clone + Default> Default for AnimatedProperty<T> {
1303 fn default() -> Self {
1304 Self::new(T::default())
1305 }
1306}
1307
1308impl<T: Clone> AnimatedProperty<T> {
1309 pub fn new(value: T) -> Self {
1311 Self {
1312 current: value.clone(),
1313 target: value.clone(),
1314 start: value,
1315 config: TransitionConfig::default(),
1316 elapsed_ms: 0,
1317 animating: false,
1318 }
1319 }
1320
1321 pub fn with_config(value: T, config: TransitionConfig) -> Self {
1323 Self {
1324 current: value.clone(),
1325 target: value.clone(),
1326 start: value,
1327 config,
1328 elapsed_ms: 0,
1329 animating: false,
1330 }
1331 }
1332
1333 pub const fn get(&self) -> &T {
1335 &self.current
1336 }
1337
1338 pub const fn target(&self) -> &T {
1340 &self.target
1341 }
1342
1343 #[must_use]
1345 pub const fn is_animating(&self) -> bool {
1346 self.animating
1347 }
1348
1349 pub fn set(&mut self, value: T) {
1351 self.start = self.current.clone();
1352 self.target = value;
1353 self.elapsed_ms = 0;
1354 self.animating = true;
1355 }
1356
1357 pub fn set_immediate(&mut self, value: T) {
1359 self.current = value.clone();
1360 self.target = value.clone();
1361 self.start = value;
1362 self.animating = false;
1363 self.elapsed_ms = 0;
1364 }
1365
1366 #[must_use]
1368 pub fn progress(&self) -> f32 {
1369 if !self.animating {
1370 return 1.0;
1371 }
1372
1373 let total = self.config.duration_ms + self.config.delay_ms;
1374 if total == 0 {
1375 return 1.0;
1376 }
1377
1378 if self.elapsed_ms < self.config.delay_ms {
1379 return 0.0;
1380 }
1381
1382 let elapsed_after_delay = self.elapsed_ms - self.config.delay_ms;
1383 (elapsed_after_delay as f32 / self.config.duration_ms as f32).min(1.0)
1384 }
1385
1386 #[must_use]
1388 pub fn eased_progress(&self) -> f32 {
1389 self.config.easing.apply(self.progress())
1390 }
1391}
1392
1393impl AnimatedProperty<f32> {
1394 pub fn advance(&mut self, delta_ms: u32) {
1396 if !self.animating {
1397 return;
1398 }
1399
1400 self.elapsed_ms += delta_ms;
1401
1402 let t = self.eased_progress();
1403 self.current = (self.target - self.start).mul_add(t, self.start);
1404
1405 if self.progress() >= 1.0 {
1406 self.current = self.target;
1407 self.animating = false;
1408 }
1409 }
1410}
1411
1412impl AnimatedProperty<f64> {
1413 pub fn advance(&mut self, delta_ms: u32) {
1415 if !self.animating {
1416 return;
1417 }
1418
1419 self.elapsed_ms += delta_ms;
1420
1421 let t = f64::from(self.eased_progress());
1422 self.current = (self.target - self.start).mul_add(t, self.start);
1423
1424 if self.progress() >= 1.0 {
1425 self.current = self.target;
1426 self.animating = false;
1427 }
1428 }
1429}
1430
1431impl AnimatedProperty<crate::Color> {
1432 pub fn advance(&mut self, delta_ms: u32) {
1434 if !self.animating {
1435 return;
1436 }
1437
1438 self.elapsed_ms += delta_ms;
1439
1440 let t = self.eased_progress();
1441 self.current = crate::Color {
1442 r: (self.target.r - self.start.r).mul_add(t, self.start.r),
1443 g: (self.target.g - self.start.g).mul_add(t, self.start.g),
1444 b: (self.target.b - self.start.b).mul_add(t, self.start.b),
1445 a: (self.target.a - self.start.a).mul_add(t, self.start.a),
1446 };
1447
1448 if self.progress() >= 1.0 {
1449 self.current = self.target;
1450 self.animating = false;
1451 }
1452 }
1453}
1454
1455impl AnimatedProperty<crate::Point> {
1456 pub fn advance(&mut self, delta_ms: u32) {
1458 if !self.animating {
1459 return;
1460 }
1461
1462 self.elapsed_ms += delta_ms;
1463
1464 let t = self.eased_progress();
1465 self.current = crate::Point {
1466 x: (self.target.x - self.start.x).mul_add(t, self.start.x),
1467 y: (self.target.y - self.start.y).mul_add(t, self.start.y),
1468 };
1469
1470 if self.progress() >= 1.0 {
1471 self.current = self.target;
1472 self.animating = false;
1473 }
1474 }
1475}
1476
1477impl AnimatedProperty<crate::Size> {
1478 pub fn advance(&mut self, delta_ms: u32) {
1480 if !self.animating {
1481 return;
1482 }
1483
1484 self.elapsed_ms += delta_ms;
1485
1486 let t = self.eased_progress();
1487 self.current = crate::Size {
1488 width: (self.target.width - self.start.width).mul_add(t, self.start.width),
1489 height: (self.target.height - self.start.height).mul_add(t, self.start.height),
1490 };
1491
1492 if self.progress() >= 1.0 {
1493 self.current = self.target;
1494 self.animating = false;
1495 }
1496 }
1497}
1498
1499#[derive(Debug, Clone, Copy)]
1501pub struct SpringConfig {
1502 pub stiffness: f32,
1504 pub damping: f32,
1506 pub mass: f32,
1508}
1509
1510impl Default for SpringConfig {
1511 fn default() -> Self {
1512 Self {
1513 stiffness: 100.0,
1514 damping: 10.0,
1515 mass: 1.0,
1516 }
1517 }
1518}
1519
1520impl SpringConfig {
1521 #[must_use]
1523 pub const fn new(stiffness: f32, damping: f32, mass: f32) -> Self {
1524 Self {
1525 stiffness,
1526 damping,
1527 mass,
1528 }
1529 }
1530
1531 #[must_use]
1533 pub const fn gentle() -> Self {
1534 Self::new(100.0, 15.0, 1.0)
1535 }
1536
1537 #[must_use]
1539 pub const fn bouncy() -> Self {
1540 Self::new(300.0, 10.0, 1.0)
1541 }
1542
1543 #[must_use]
1545 pub const fn stiff() -> Self {
1546 Self::new(500.0, 30.0, 1.0)
1547 }
1548}
1549
1550#[derive(Debug, Clone)]
1552pub struct SpringAnimation {
1553 position: f32,
1555 velocity: f32,
1557 target: f32,
1559 config: SpringConfig,
1561 velocity_threshold: f32,
1563 position_threshold: f32,
1565}
1566
1567impl SpringAnimation {
1568 #[must_use]
1570 pub fn new(initial: f32) -> Self {
1571 Self {
1572 position: initial,
1573 velocity: 0.0,
1574 target: initial,
1575 config: SpringConfig::default(),
1576 velocity_threshold: 0.01,
1577 position_threshold: 0.001,
1578 }
1579 }
1580
1581 #[must_use]
1583 pub const fn with_config(initial: f32, config: SpringConfig) -> Self {
1584 Self {
1585 position: initial,
1586 velocity: 0.0,
1587 target: initial,
1588 config,
1589 velocity_threshold: 0.01,
1590 position_threshold: 0.001,
1591 }
1592 }
1593
1594 #[must_use]
1596 pub const fn position(&self) -> f32 {
1597 self.position
1598 }
1599
1600 #[must_use]
1602 pub const fn velocity(&self) -> f32 {
1603 self.velocity
1604 }
1605
1606 #[must_use]
1608 pub const fn target(&self) -> f32 {
1609 self.target
1610 }
1611
1612 pub fn set_target(&mut self, target: f32) {
1614 self.target = target;
1615 }
1616
1617 pub fn set_immediate(&mut self, position: f32) {
1619 self.position = position;
1620 self.target = position;
1621 self.velocity = 0.0;
1622 }
1623
1624 #[must_use]
1626 pub fn is_at_rest(&self) -> bool {
1627 let position_diff = (self.position - self.target).abs();
1628 let velocity_abs = self.velocity.abs();
1629 position_diff < self.position_threshold && velocity_abs < self.velocity_threshold
1630 }
1631
1632 pub fn advance(&mut self, delta_s: f32) {
1634 if self.is_at_rest() {
1635 self.position = self.target;
1636 self.velocity = 0.0;
1637 return;
1638 }
1639
1640 let displacement = self.position - self.target;
1643 let spring_force = -self.config.stiffness * displacement;
1644 let damping_force = -self.config.damping * self.velocity;
1645 let acceleration = (spring_force + damping_force) / self.config.mass;
1646
1647 self.velocity += acceleration * delta_s;
1649 self.position += self.velocity * delta_s;
1650 }
1651
1652 pub fn advance_ms(&mut self, delta_ms: u32) {
1654 self.advance(delta_ms as f32 / 1000.0);
1655 }
1656}
1657
1658#[cfg(test)]
1659mod tests {
1660 use super::*;
1661
1662 #[test]
1667 fn test_memory_storage_new() {
1668 let storage = MemoryStorage::new();
1669 assert!(storage.is_empty());
1670 assert_eq!(storage.len(), 0);
1671 }
1672
1673 #[test]
1674 fn test_memory_storage_save_load() {
1675 let storage = MemoryStorage::new();
1676 storage.save("key1", b"value1");
1677
1678 assert!(!storage.is_empty());
1679 assert_eq!(storage.len(), 1);
1680 assert_eq!(storage.load("key1"), Some(b"value1".to_vec()));
1681 }
1682
1683 #[test]
1684 fn test_memory_storage_load_missing() {
1685 let storage = MemoryStorage::new();
1686 assert_eq!(storage.load("nonexistent"), None);
1687 }
1688
1689 #[test]
1690 fn test_memory_storage_contains() {
1691 let storage = MemoryStorage::new();
1692 storage.save("exists", b"data");
1693
1694 assert!(storage.contains("exists"));
1695 assert!(!storage.contains("missing"));
1696 }
1697
1698 #[test]
1699 fn test_memory_storage_remove() {
1700 let storage = MemoryStorage::new();
1701 storage.save("key", b"value");
1702 assert!(storage.contains("key"));
1703
1704 storage.remove("key");
1705 assert!(!storage.contains("key"));
1706 }
1707
1708 #[test]
1709 fn test_memory_storage_clear() {
1710 let storage = MemoryStorage::new();
1711 storage.save("a", b"1");
1712 storage.save("b", b"2");
1713 assert_eq!(storage.len(), 2);
1714
1715 storage.clear();
1716 assert!(storage.is_empty());
1717 }
1718
1719 #[test]
1720 fn test_memory_storage_overwrite() {
1721 let storage = MemoryStorage::new();
1722 storage.save("key", b"first");
1723 storage.save("key", b"second");
1724
1725 assert_eq!(storage.len(), 1);
1726 assert_eq!(storage.load("key"), Some(b"second".to_vec()));
1727 }
1728
1729 #[test]
1734 fn test_memory_router_new() {
1735 let router = MemoryRouter::new();
1736 assert_eq!(router.current_route(), "/");
1737 assert_eq!(router.history_len(), 1);
1738 }
1739
1740 #[test]
1741 fn test_memory_router_navigate() {
1742 let router = MemoryRouter::new();
1743 router.navigate("/home");
1744
1745 assert_eq!(router.current_route(), "/home");
1746 }
1747
1748 #[test]
1749 fn test_memory_router_history() {
1750 let router = MemoryRouter::new();
1751 router.navigate("/page1");
1752 router.navigate("/page2");
1753 router.navigate("/page3");
1754
1755 let history = router.history();
1756 assert_eq!(history, vec!["/", "/page1", "/page2", "/page3"]);
1757 }
1758
1759 #[test]
1760 fn test_memory_router_default() {
1761 let router = MemoryRouter::default();
1762 assert_eq!(router.current_route(), "/");
1763 }
1764
1765 #[test]
1770 fn test_execution_result_none() {
1771 let result: ExecutionResult<i32> = ExecutionResult::None;
1772 assert!(result.is_none());
1773 assert!(!result.has_messages());
1774 }
1775
1776 #[test]
1777 fn test_execution_result_message() {
1778 let result = ExecutionResult::Message(42);
1779 assert!(!result.is_none());
1780 assert!(result.has_messages());
1781 }
1782
1783 #[test]
1784 fn test_execution_result_messages() {
1785 let result = ExecutionResult::Messages(vec![1, 2, 3]);
1786 assert!(!result.is_none());
1787 assert!(result.has_messages());
1788 }
1789
1790 #[test]
1791 fn test_execution_result_pending() {
1792 let result: ExecutionResult<i32> = ExecutionResult::Pending;
1793 assert!(!result.is_none());
1794 assert!(!result.has_messages());
1795 }
1796
1797 #[test]
1798 fn test_execution_result_into_messages_none() {
1799 let result: ExecutionResult<i32> = ExecutionResult::None;
1800 assert!(result.into_messages().is_empty());
1801 }
1802
1803 #[test]
1804 fn test_execution_result_into_messages_single() {
1805 let result = ExecutionResult::Message(42);
1806 assert_eq!(result.into_messages(), vec![42]);
1807 }
1808
1809 #[test]
1810 fn test_execution_result_into_messages_multiple() {
1811 let result = ExecutionResult::Messages(vec![1, 2, 3]);
1812 assert_eq!(result.into_messages(), vec![1, 2, 3]);
1813 }
1814
1815 #[test]
1816 fn test_execution_result_into_messages_pending() {
1817 let result: ExecutionResult<i32> = ExecutionResult::Pending;
1818 assert!(result.into_messages().is_empty());
1819 }
1820
1821 #[test]
1826 fn test_executor_execute_none() {
1827 let executor = default_executor();
1828 let result = executor.execute::<()>(Command::None);
1829 assert!(result.is_none());
1830 }
1831
1832 #[test]
1833 fn test_executor_execute_navigate() {
1834 let executor = default_executor();
1835 let result = executor.execute::<()>(Command::Navigate {
1836 route: "/dashboard".to_string(),
1837 });
1838
1839 assert!(result.is_none());
1840 assert_eq!(executor.router().current_route(), "/dashboard");
1841 }
1842
1843 #[test]
1844 fn test_executor_execute_navigate_multiple() {
1845 let executor = default_executor();
1846
1847 executor.execute::<()>(Command::Navigate {
1848 route: "/page1".to_string(),
1849 });
1850 executor.execute::<()>(Command::Navigate {
1851 route: "/page2".to_string(),
1852 });
1853
1854 assert_eq!(executor.router().current_route(), "/page2");
1855 assert_eq!(executor.router().history_len(), 3); }
1857
1858 fn load_state_handler(data: Option<Vec<u8>>) -> String {
1859 data.map_or_else(
1860 || "not found".to_string(),
1861 |d| String::from_utf8(d).unwrap(),
1862 )
1863 }
1864
1865 #[test]
1866 fn test_executor_execute_load_state_found() {
1867 let executor = default_executor();
1868 executor.storage().save("my_key", b"stored_data");
1869
1870 let result = executor.execute(Command::LoadState {
1871 key: "my_key".to_string(),
1872 on_load: load_state_handler,
1873 });
1874
1875 match result {
1876 ExecutionResult::Message(msg) => assert_eq!(msg, "stored_data"),
1877 _ => panic!("Expected Message result"),
1878 }
1879 }
1880
1881 #[test]
1882 fn test_executor_execute_load_state_not_found() {
1883 let executor = default_executor();
1884
1885 let result = executor.execute(Command::LoadState {
1886 key: "missing_key".to_string(),
1887 on_load: load_state_handler,
1888 });
1889
1890 match result {
1891 ExecutionResult::Message(msg) => assert_eq!(msg, "not found"),
1892 _ => panic!("Expected Message result"),
1893 }
1894 }
1895
1896 #[test]
1897 fn test_executor_execute_batch_empty() {
1898 let executor = default_executor();
1899 let result = executor.execute::<()>(Command::Batch(vec![]));
1900 assert!(result.is_none());
1901 }
1902
1903 #[test]
1904 fn test_executor_execute_batch_navigations() {
1905 let executor = default_executor();
1906 let result = executor.execute::<()>(Command::Batch(vec![
1907 Command::Navigate {
1908 route: "/a".to_string(),
1909 },
1910 Command::Navigate {
1911 route: "/b".to_string(),
1912 },
1913 Command::Navigate {
1914 route: "/c".to_string(),
1915 },
1916 ]));
1917
1918 assert!(result.is_none());
1919 assert_eq!(executor.router().current_route(), "/c");
1920 assert_eq!(executor.router().history_len(), 4);
1921 }
1922
1923 fn batch_load_handler(data: Option<Vec<u8>>) -> i32 {
1924 data.map_or(0, |_| 42)
1925 }
1926
1927 #[test]
1928 fn test_executor_execute_batch_mixed() {
1929 let executor = default_executor();
1930 executor.storage().save("key", b"data");
1931
1932 let result = executor.execute(Command::Batch(vec![
1933 Command::Navigate {
1934 route: "/page".to_string(),
1935 },
1936 Command::LoadState {
1937 key: "key".to_string(),
1938 on_load: batch_load_handler,
1939 },
1940 ]));
1941
1942 match result {
1943 ExecutionResult::Messages(msgs) => {
1944 assert_eq!(msgs, vec![42]);
1945 }
1946 _ => panic!("Expected Messages result"),
1947 }
1948 assert_eq!(executor.router().current_route(), "/page");
1949 }
1950
1951 #[test]
1952 fn test_executor_execute_task_returns_pending() {
1953 let executor = default_executor();
1954 let result = executor.execute(Command::task(async { 42 }));
1955
1956 match result {
1957 ExecutionResult::Pending => {}
1958 _ => panic!("Expected Pending result for Task"),
1959 }
1960 }
1961
1962 #[test]
1963 fn test_executor_execute_save_state() {
1964 let executor = default_executor();
1965 let result = executor.execute::<()>(Command::SaveState {
1966 key: "test".to_string(),
1967 });
1968
1969 assert!(result.is_none());
1971 }
1972
1973 #[test]
1974 fn test_default_executor() {
1975 let executor = default_executor();
1976 assert_eq!(executor.router().current_route(), "/");
1977 assert!(executor.storage().is_empty());
1978 }
1979
1980 #[test]
1985 fn test_state_update_with_command_execution() {
1986 use crate::state::{CounterMessage, CounterState, State};
1987
1988 let executor = default_executor();
1989 let mut state = CounterState::default();
1990
1991 let cmd = state.update(CounterMessage::Increment);
1993 assert_eq!(state.count, 1);
1994
1995 let result = executor.execute(cmd);
1997 assert!(result.is_none());
1998 }
1999
2000 #[test]
2001 fn test_navigation_state_flow() {
2002 let executor = default_executor();
2003
2004 executor.execute::<()>(Command::Navigate {
2006 route: "/login".to_string(),
2007 });
2008 assert_eq!(executor.router().current_route(), "/login");
2009
2010 executor.execute::<()>(Command::Navigate {
2011 route: "/dashboard".to_string(),
2012 });
2013 assert_eq!(executor.router().current_route(), "/dashboard");
2014
2015 let history = executor.router().history();
2017 assert_eq!(history, vec!["/", "/login", "/dashboard"]);
2018 }
2019
2020 fn serialized_state_handler(data: Option<Vec<u8>>) -> Option<i32> {
2021 data.and_then(|d| {
2022 let json = String::from_utf8(d).ok()?;
2023 let count_str = json.split(':').nth(1)?;
2025 count_str.trim_end_matches('}').parse().ok()
2026 })
2027 }
2028
2029 #[test]
2030 fn test_load_state_with_serialized_data() {
2031 let executor = default_executor();
2032
2033 let saved_data = br#"{"count":42}"#;
2035 executor.storage().save("counter_state", saved_data);
2036
2037 let result = executor.execute(Command::LoadState {
2038 key: "counter_state".to_string(),
2039 on_load: serialized_state_handler,
2040 });
2041
2042 match result {
2043 ExecutionResult::Message(Some(count)) => assert_eq!(count, 42),
2044 _ => panic!("Expected Message with Some(42)"),
2045 }
2046 }
2047
2048 #[test]
2053 fn test_focus_manager_new() {
2054 let fm = FocusManager::new();
2055 assert!(fm.focused().is_none());
2056 assert!(!fm.is_trapped());
2057 }
2058
2059 #[test]
2060 fn test_focus_manager_set_ring() {
2061 let mut fm = FocusManager::new();
2062 fm.set_focus_ring(vec![1, 2, 3]);
2063 assert!(fm.is_focusable(1));
2064 assert!(fm.is_focusable(2));
2065 assert!(!fm.is_focusable(4));
2066 }
2067
2068 #[test]
2069 fn test_focus_manager_focus() {
2070 let mut fm = FocusManager::new();
2071 fm.set_focus_ring(vec![1, 2, 3]);
2072
2073 assert!(fm.focus(2));
2074 assert_eq!(fm.focused(), Some(2));
2075
2076 assert!(!fm.focus(99));
2078 assert_eq!(fm.focused(), Some(2));
2079 }
2080
2081 #[test]
2082 fn test_focus_manager_blur() {
2083 let mut fm = FocusManager::new();
2084 fm.set_focus_ring(vec![1, 2, 3]);
2085 fm.focus(1);
2086 assert!(fm.focused().is_some());
2087
2088 fm.blur();
2089 assert!(fm.focused().is_none());
2090 }
2091
2092 #[test]
2093 fn test_focus_manager_move_forward() {
2094 let mut fm = FocusManager::new();
2095 fm.set_focus_ring(vec![1, 2, 3]);
2096
2097 let next = fm.move_focus(FocusDirection::Forward);
2099 assert_eq!(next, Some(1));
2100
2101 let next = fm.move_focus(FocusDirection::Forward);
2103 assert_eq!(next, Some(2));
2104
2105 let next = fm.move_focus(FocusDirection::Forward);
2106 assert_eq!(next, Some(3));
2107
2108 let next = fm.move_focus(FocusDirection::Forward);
2110 assert_eq!(next, Some(1));
2111 }
2112
2113 #[test]
2114 fn test_focus_manager_move_backward() {
2115 let mut fm = FocusManager::new();
2116 fm.set_focus_ring(vec![1, 2, 3]);
2117
2118 let next = fm.move_focus(FocusDirection::Backward);
2120 assert_eq!(next, Some(3));
2121
2122 let next = fm.move_focus(FocusDirection::Backward);
2124 assert_eq!(next, Some(2));
2125
2126 let next = fm.move_focus(FocusDirection::Backward);
2127 assert_eq!(next, Some(1));
2128
2129 let next = fm.move_focus(FocusDirection::Backward);
2131 assert_eq!(next, Some(3));
2132 }
2133
2134 #[test]
2135 fn test_focus_manager_empty_ring() {
2136 let mut fm = FocusManager::new();
2137 let next = fm.move_focus(FocusDirection::Forward);
2138 assert!(next.is_none());
2139 }
2140
2141 #[test]
2142 fn test_focus_manager_trap() {
2143 let mut fm = FocusManager::new();
2144 fm.set_focus_ring(vec![1, 2, 3, 4, 5]);
2145 fm.focus(2);
2146
2147 fm.push_trap(vec![10, 11, 12]);
2149 assert!(fm.is_trapped());
2150 assert_eq!(fm.focused(), Some(10)); assert!(fm.is_focusable(10));
2154 assert!(!fm.is_focusable(1));
2155
2156 fm.move_focus(FocusDirection::Forward);
2158 assert_eq!(fm.focused(), Some(11));
2159 }
2160
2161 #[test]
2162 fn test_focus_manager_pop_trap() {
2163 let mut fm = FocusManager::new();
2164 fm.set_focus_ring(vec![1, 2, 3]);
2165 fm.focus(2);
2166
2167 fm.push_trap(vec![10, 11]);
2168 assert_eq!(fm.focused(), Some(10));
2169
2170 let trap = fm.pop_trap();
2172 assert!(trap.is_some());
2173 assert!(!fm.is_trapped());
2174 assert_eq!(fm.focused(), Some(2)); }
2176
2177 #[test]
2178 fn test_focus_manager_nested_traps() {
2179 let mut fm = FocusManager::new();
2180 fm.set_focus_ring(vec![1, 2, 3]);
2181 fm.focus(1);
2182
2183 fm.push_trap(vec![10, 11]);
2185 assert_eq!(fm.focused(), Some(10));
2186
2187 fm.push_trap(vec![20, 21]);
2189 assert_eq!(fm.focused(), Some(20));
2190
2191 fm.pop_trap();
2193 assert_eq!(fm.focused(), Some(10));
2194
2195 fm.pop_trap();
2197 assert_eq!(fm.focused(), Some(1));
2198 }
2199
2200 #[test]
2201 fn test_focus_direction_variants() {
2202 let mut fm = FocusManager::new();
2203 fm.set_focus_ring(vec![1, 2, 3]);
2204 fm.focus(2);
2205
2206 fm.move_focus(FocusDirection::Down);
2208 assert_eq!(fm.focused(), Some(3));
2209
2210 fm.focus(2);
2211 fm.move_focus(FocusDirection::Right);
2212 assert_eq!(fm.focused(), Some(3));
2213
2214 fm.focus(2);
2216 fm.move_focus(FocusDirection::Up);
2217 assert_eq!(fm.focused(), Some(1));
2218
2219 fm.focus(2);
2220 fm.move_focus(FocusDirection::Left);
2221 assert_eq!(fm.focused(), Some(1));
2222 }
2223
2224 #[test]
2229 fn test_easing_linear() {
2230 assert_eq!(EasingFunction::Linear.apply(0.0), 0.0);
2231 assert_eq!(EasingFunction::Linear.apply(0.5), 0.5);
2232 assert_eq!(EasingFunction::Linear.apply(1.0), 1.0);
2233 }
2234
2235 #[test]
2236 fn test_easing_clamps_input() {
2237 assert_eq!(EasingFunction::Linear.apply(-0.5), 0.0);
2238 assert_eq!(EasingFunction::Linear.apply(1.5), 1.0);
2239 }
2240
2241 #[test]
2242 fn test_easing_quad() {
2243 assert!(EasingFunction::EaseInQuad.apply(0.5) < 0.5);
2245 assert!(EasingFunction::EaseOutQuad.apply(0.5) > 0.5);
2247 assert_eq!(EasingFunction::EaseInQuad.apply(0.0), 0.0);
2249 assert_eq!(EasingFunction::EaseInQuad.apply(1.0), 1.0);
2250 }
2251
2252 #[test]
2253 fn test_easing_cubic() {
2254 assert!(EasingFunction::EaseInCubic.apply(0.5) < 0.5);
2255 assert!(EasingFunction::EaseOutCubic.apply(0.5) > 0.5);
2256 assert_eq!(EasingFunction::EaseInCubic.apply(0.0), 0.0);
2257 assert_eq!(EasingFunction::EaseOutCubic.apply(1.0), 1.0);
2258 }
2259
2260 #[test]
2261 fn test_easing_in_out_quad() {
2262 let first_quarter = EasingFunction::EaseInOutQuad.apply(0.25);
2264 assert!(first_quarter < 0.25);
2265 let third_quarter = EasingFunction::EaseInOutQuad.apply(0.75);
2267 assert!(third_quarter > 0.75);
2268 }
2269
2270 #[test]
2271 fn test_easing_elastic() {
2272 assert_eq!(EasingFunction::EaseOutElastic.apply(0.0), 0.0);
2273 assert_eq!(EasingFunction::EaseOutElastic.apply(1.0), 1.0);
2274 let mid = EasingFunction::EaseOutElastic.apply(0.5);
2276 assert!(mid > 0.9); }
2278
2279 #[test]
2280 fn test_easing_bounce() {
2281 assert_eq!(EasingFunction::EaseOutBounce.apply(0.0), 0.0);
2282 assert!((EasingFunction::EaseOutBounce.apply(1.0) - 1.0).abs() < 0.001);
2283 }
2284
2285 #[test]
2286 fn test_easing_default() {
2287 assert_eq!(EasingFunction::default(), EasingFunction::Linear);
2288 }
2289
2290 #[test]
2295 fn test_tween_new() {
2296 let tween = Tween::new(0.0_f32, 100.0, 1000);
2297 assert_eq!(tween.from, 0.0);
2298 assert_eq!(tween.to, 100.0);
2299 assert_eq!(tween.duration_ms, 1000);
2300 assert_eq!(tween.easing, EasingFunction::Linear);
2301 }
2302
2303 #[test]
2304 fn test_tween_progress() {
2305 let mut tween = Tween::new(0.0_f32, 100.0, 1000);
2306 assert_eq!(tween.progress(), 0.0);
2307
2308 tween.advance(500);
2309 assert_eq!(tween.progress(), 0.5);
2310
2311 tween.advance(500);
2312 assert_eq!(tween.progress(), 1.0);
2313 }
2314
2315 #[test]
2316 fn test_tween_value() {
2317 let mut tween = Tween::new(0.0_f32, 100.0, 1000);
2318 assert_eq!(tween.value(), 0.0);
2319
2320 tween.advance(500);
2321 assert_eq!(tween.value(), 50.0);
2322
2323 tween.advance(500);
2324 assert_eq!(tween.value(), 100.0);
2325 }
2326
2327 #[test]
2328 fn test_tween_f64_value() {
2329 let mut tween = Tween::new(0.0_f64, 100.0, 1000);
2330 tween.advance(250);
2331 assert!((tween.value() - 25.0).abs() < 0.001);
2332 }
2333
2334 #[test]
2335 fn test_tween_with_easing() {
2336 let mut tween = Tween::new(0.0_f32, 100.0, 1000).with_easing(EasingFunction::EaseInQuad);
2337 tween.advance(500);
2338 assert!(tween.value() < 50.0);
2340 }
2341
2342 #[test]
2343 fn test_tween_is_complete() {
2344 let mut tween = Tween::new(0.0_f32, 100.0, 1000);
2345 assert!(!tween.is_complete());
2346
2347 tween.advance(999);
2348 assert!(!tween.is_complete());
2349
2350 tween.advance(1);
2351 assert!(tween.is_complete());
2352 }
2353
2354 #[test]
2355 fn test_tween_reset() {
2356 let mut tween = Tween::new(0.0_f32, 100.0, 1000);
2357 tween.advance(500);
2358 assert_eq!(tween.progress(), 0.5);
2359
2360 tween.reset();
2361 assert_eq!(tween.progress(), 0.0);
2362 }
2363
2364 #[test]
2365 fn test_tween_zero_duration() {
2366 let tween = Tween::new(0.0_f32, 100.0, 0);
2367 assert_eq!(tween.progress(), 1.0);
2368 assert!(tween.is_complete());
2369 }
2370
2371 #[test]
2372 fn test_tween_advance_overflow() {
2373 let mut tween = Tween::new(0.0_f32, 100.0, 1000);
2374 tween.advance(2000); assert_eq!(tween.progress(), 1.0);
2376 assert!(tween.is_complete());
2377 }
2378
2379 #[test]
2384 fn test_animation_instance_new() {
2385 let anim = AnimationInstance::new(1, 0.0, 100.0, 1000);
2386 assert_eq!(anim.id, 1);
2387 assert_eq!(anim.state, AnimationState::Idle);
2388 assert_eq!(anim.loop_count, 1);
2389 }
2390
2391 #[test]
2392 fn test_animation_instance_start() {
2393 let mut anim = AnimationInstance::new(1, 0.0, 100.0, 1000);
2394 anim.start();
2395 assert_eq!(anim.state, AnimationState::Running);
2396 }
2397
2398 #[test]
2399 fn test_animation_instance_pause_resume() {
2400 let mut anim = AnimationInstance::new(1, 0.0, 100.0, 1000);
2401 anim.start();
2402 anim.advance(500);
2403
2404 anim.pause();
2405 assert_eq!(anim.state, AnimationState::Paused);
2406
2407 anim.advance(500);
2409 assert!(!anim.tween.is_complete());
2410
2411 anim.resume();
2412 assert_eq!(anim.state, AnimationState::Running);
2413 }
2414
2415 #[test]
2416 fn test_animation_instance_stop() {
2417 let mut anim = AnimationInstance::new(1, 0.0, 100.0, 1000);
2418 anim.start();
2419 anim.advance(500);
2420
2421 anim.stop();
2422 assert_eq!(anim.state, AnimationState::Idle);
2423 assert_eq!(anim.tween.progress(), 0.0);
2424 }
2425
2426 #[test]
2427 fn test_animation_instance_complete() {
2428 let mut anim = AnimationInstance::new(1, 0.0, 100.0, 1000);
2429 anim.start();
2430 anim.advance(1000);
2431
2432 assert_eq!(anim.state, AnimationState::Completed);
2433 }
2434
2435 #[test]
2436 fn test_animation_instance_loop() {
2437 let mut anim = AnimationInstance::new(1, 0.0, 100.0, 1000).with_loop_count(3);
2438 anim.start();
2439
2440 anim.advance(1000);
2442 assert_eq!(anim.state, AnimationState::Running);
2443 assert_eq!(anim.current_loop, 1);
2444
2445 anim.advance(1000);
2447 assert_eq!(anim.current_loop, 2);
2448
2449 anim.advance(1000);
2451 assert_eq!(anim.state, AnimationState::Completed);
2452 }
2453
2454 #[test]
2455 fn test_animation_instance_infinite_loop() {
2456 let mut anim = AnimationInstance::new(1, 0.0, 100.0, 1000).with_loop_count(0);
2457 anim.start();
2458
2459 for _ in 0..100 {
2460 anim.advance(1000);
2461 assert_eq!(anim.state, AnimationState::Running);
2462 }
2463 }
2464
2465 #[test]
2466 fn test_animation_instance_alternate() {
2467 let mut anim = AnimationInstance::new(1, 0.0, 100.0, 1000)
2468 .with_loop_count(2)
2469 .with_alternate(true);
2470 anim.start();
2471
2472 anim.advance(500);
2474 assert!((anim.value() - 50.0).abs() < 0.001);
2475
2476 anim.advance(500);
2478 assert_eq!(anim.current_loop, 1);
2479
2480 anim.advance(500);
2482 assert!((anim.value() - 50.0).abs() < 0.001);
2484 }
2485
2486 #[test]
2487 fn test_animation_instance_with_easing() {
2488 let anim =
2489 AnimationInstance::new(1, 0.0, 100.0, 1000).with_easing(EasingFunction::EaseInQuad);
2490 assert_eq!(anim.tween.easing, EasingFunction::EaseInQuad);
2491 }
2492
2493 #[test]
2498 fn test_animator_new() {
2499 let animator = Animator::new();
2500 assert!(animator.is_empty());
2501 assert_eq!(animator.len(), 0);
2502 }
2503
2504 #[test]
2505 fn test_animator_create() {
2506 let mut animator = Animator::new();
2507 let id = animator.create(0.0, 100.0, 1000);
2508
2509 assert_eq!(animator.len(), 1);
2510 assert!(animator.get(id).is_some());
2511 }
2512
2513 #[test]
2514 fn test_animator_unique_ids() {
2515 let mut animator = Animator::new();
2516 let id1 = animator.create(0.0, 100.0, 1000);
2517 let id2 = animator.create(0.0, 100.0, 1000);
2518 let id3 = animator.create(0.0, 100.0, 1000);
2519
2520 assert_ne!(id1, id2);
2521 assert_ne!(id2, id3);
2522 }
2523
2524 #[test]
2525 fn test_animator_start_and_value() {
2526 let mut animator = Animator::new();
2527 let id = animator.create(0.0, 100.0, 1000);
2528
2529 animator.start(id);
2530 assert_eq!(animator.value(id), Some(0.0));
2531
2532 animator.advance(500);
2533 assert_eq!(animator.value(id), Some(50.0));
2534 }
2535
2536 #[test]
2537 fn test_animator_pause_resume() {
2538 let mut animator = Animator::new();
2539 let id = animator.create(0.0, 100.0, 1000);
2540 animator.start(id);
2541 animator.advance(250);
2542
2543 animator.pause(id);
2544 animator.advance(500); animator.resume(id);
2547 animator.advance(250);
2548
2549 assert_eq!(animator.value(id), Some(50.0));
2551 }
2552
2553 #[test]
2554 fn test_animator_stop() {
2555 let mut animator = Animator::new();
2556 let id = animator.create(0.0, 100.0, 1000);
2557 animator.start(id);
2558 animator.advance(500);
2559
2560 animator.stop(id);
2561 assert_eq!(animator.value(id), Some(0.0));
2562 }
2563
2564 #[test]
2565 fn test_animator_remove() {
2566 let mut animator = Animator::new();
2567 let id = animator.create(0.0, 100.0, 1000);
2568 assert_eq!(animator.len(), 1);
2569
2570 animator.remove(id);
2571 assert!(animator.is_empty());
2572 assert!(animator.get(id).is_none());
2573 }
2574
2575 #[test]
2576 fn test_animator_has_running() {
2577 let mut animator = Animator::new();
2578 let id = animator.create(0.0, 100.0, 1000);
2579
2580 assert!(!animator.has_running());
2581
2582 animator.start(id);
2583 assert!(animator.has_running());
2584
2585 animator.advance(1000);
2586 assert!(!animator.has_running()); }
2588
2589 #[test]
2590 fn test_animator_cleanup_completed() {
2591 let mut animator = Animator::new();
2592 let id1 = animator.create(0.0, 100.0, 500);
2593 let id2 = animator.create(0.0, 100.0, 1000);
2594
2595 animator.start(id1);
2596 animator.start(id2);
2597 animator.advance(500);
2598
2599 assert_eq!(animator.len(), 2);
2600
2601 animator.cleanup_completed();
2602 assert_eq!(animator.len(), 1);
2603 assert!(animator.get(id1).is_none());
2604 assert!(animator.get(id2).is_some());
2605 }
2606
2607 #[test]
2608 fn test_animator_multiple_animations() {
2609 let mut animator = Animator::new();
2610 let id1 = animator.create(0.0, 100.0, 1000);
2611 let id2 = animator.create(100.0, 0.0, 1000);
2612
2613 animator.start(id1);
2614 animator.start(id2);
2615 animator.advance(500);
2616
2617 assert_eq!(animator.value(id1), Some(50.0));
2618 assert_eq!(animator.value(id2), Some(50.0)); }
2620
2621 #[test]
2626 fn test_timer_new() {
2627 let timer = Timer::new(1000);
2628 assert_eq!(timer.interval_ms, 1000);
2629 assert!(!timer.is_running());
2630 assert_eq!(timer.tick_count(), 0);
2631 }
2632
2633 #[test]
2634 fn test_timer_start_stop() {
2635 let mut timer = Timer::new(1000);
2636 timer.start();
2637 assert!(timer.is_running());
2638
2639 timer.stop();
2640 assert!(!timer.is_running());
2641 }
2642
2643 #[test]
2644 fn test_timer_advance() {
2645 let mut timer = Timer::new(1000);
2646 timer.start();
2647
2648 let ticks = timer.advance(500);
2650 assert_eq!(ticks, 0);
2651 assert_eq!(timer.tick_count(), 0);
2652
2653 let ticks = timer.advance(500);
2655 assert_eq!(ticks, 1);
2656 assert_eq!(timer.tick_count(), 1);
2657 }
2658
2659 #[test]
2660 fn test_timer_multiple_ticks() {
2661 let mut timer = Timer::new(100);
2662 timer.start();
2663
2664 let ticks = timer.advance(350);
2665 assert_eq!(ticks, 3);
2666 assert_eq!(timer.tick_count(), 3);
2667
2668 assert!((timer.progress() - 0.5).abs() < 0.01);
2670 }
2671
2672 #[test]
2673 fn test_timer_max_ticks() {
2674 let mut timer = Timer::new(100).with_max_ticks(3);
2675 timer.start();
2676
2677 timer.advance(200); assert!(timer.is_running());
2679
2680 timer.advance(200); assert!(!timer.is_running());
2682 assert_eq!(timer.tick_count(), 3);
2683 }
2684
2685 #[test]
2686 fn test_timer_reset() {
2687 let mut timer = Timer::new(100);
2688 timer.start();
2689 timer.advance(250);
2690 assert_eq!(timer.tick_count(), 2);
2691
2692 timer.reset();
2693 assert_eq!(timer.tick_count(), 0);
2694 assert_eq!(timer.progress(), 0.0);
2695 }
2696
2697 #[test]
2698 fn test_timer_progress() {
2699 let mut timer = Timer::new(100);
2700 timer.start();
2701 timer.advance(50);
2702 assert!((timer.progress() - 0.5).abs() < 0.01);
2703 }
2704
2705 #[test]
2706 fn test_timer_zero_interval() {
2707 let mut timer = Timer::new(0);
2708 timer.start();
2709 let ticks = timer.advance(1000);
2710 assert_eq!(ticks, 0); }
2712
2713 #[test]
2714 fn test_timer_not_running() {
2715 let mut timer = Timer::new(100);
2716 let ticks = timer.advance(1000);
2717 assert_eq!(ticks, 0); }
2719
2720 #[test]
2725 fn test_frame_timer_new() {
2726 let ft = FrameTimer::new(60);
2727 assert_eq!(ft.total_frames(), 0);
2728 assert!((ft.target_frame_ms() - 16.667).abs() < 0.01);
2729 }
2730
2731 #[test]
2732 fn test_frame_timer_default() {
2733 let ft = FrameTimer::default();
2734 assert_eq!(ft.total_frames(), 0);
2735 }
2736
2737 #[test]
2738 fn test_frame_timer_frame() {
2739 let mut ft = FrameTimer::new(60);
2740 ft.frame(0);
2741 assert_eq!(ft.total_frames(), 1);
2742
2743 ft.frame(16667); assert_eq!(ft.total_frames(), 2);
2745 }
2746
2747 #[test]
2748 fn test_frame_timer_fps() {
2749 let mut ft = FrameTimer::new(60);
2750
2751 for i in 0..60 {
2753 ft.frame(i * 16667);
2754 }
2755
2756 let fps = ft.fps();
2757 assert!(fps > 55.0 && fps < 65.0);
2758 }
2759
2760 #[test]
2761 fn test_frame_timer_is_on_target() {
2762 let mut ft = FrameTimer::new(60);
2763
2764 for i in 0..10 {
2766 ft.frame(i * 16667);
2767 }
2768 assert!(ft.is_on_target());
2769 }
2770
2771 #[test]
2772 fn test_frame_timer_slow_frames() {
2773 let mut ft = FrameTimer::new(60);
2774
2775 for i in 0..10 {
2777 ft.frame(i * 33333);
2778 }
2779
2780 let fps = ft.fps();
2781 assert!(fps < 35.0);
2782 assert!(!ft.is_on_target());
2783 }
2784
2785 #[test]
2786 fn test_frame_timer_zero_fps() {
2787 let ft = FrameTimer::new(0);
2788 assert!((ft.target_frame_ms() - 16.667).abs() < 0.01); }
2790
2791 #[test]
2796 fn test_transition_config_default() {
2797 let config = TransitionConfig::default();
2798 assert_eq!(config.duration_ms, 300);
2799 assert_eq!(config.delay_ms, 0);
2800 assert_eq!(config.easing, EasingFunction::EaseInOutCubic);
2801 }
2802
2803 #[test]
2804 fn test_transition_config_new() {
2805 let config = TransitionConfig::new(500);
2806 assert_eq!(config.duration_ms, 500);
2807 }
2808
2809 #[test]
2810 fn test_transition_config_presets() {
2811 assert_eq!(TransitionConfig::quick().duration_ms, 150);
2812 assert_eq!(TransitionConfig::normal().duration_ms, 300);
2813 assert_eq!(TransitionConfig::slow().duration_ms, 500);
2814 }
2815
2816 #[test]
2817 fn test_transition_config_builder() {
2818 let config = TransitionConfig::new(200)
2819 .with_easing(EasingFunction::EaseOutBounce)
2820 .with_delay(50);
2821
2822 assert_eq!(config.duration_ms, 200);
2823 assert_eq!(config.easing, EasingFunction::EaseOutBounce);
2824 assert_eq!(config.delay_ms, 50);
2825 }
2826
2827 #[test]
2832 fn test_animated_property_new() {
2833 let prop = AnimatedProperty::new(0.0_f32);
2834 assert_eq!(*prop.get(), 0.0);
2835 assert_eq!(*prop.target(), 0.0);
2836 assert!(!prop.is_animating());
2837 }
2838
2839 #[test]
2840 fn test_animated_property_default() {
2841 let prop: AnimatedProperty<f32> = AnimatedProperty::default();
2842 assert_eq!(*prop.get(), 0.0);
2843 }
2844
2845 #[test]
2846 fn test_animated_property_set() {
2847 let mut prop = AnimatedProperty::new(0.0_f32);
2848 prop.set(100.0);
2849
2850 assert!(prop.is_animating());
2851 assert_eq!(*prop.target(), 100.0);
2852 assert_eq!(*prop.get(), 0.0); }
2854
2855 #[test]
2856 fn test_animated_property_advance() {
2857 let mut prop = AnimatedProperty::with_config(0.0_f32, TransitionConfig::new(1000));
2858 prop.set(100.0);
2859
2860 prop.advance(500);
2861 let value = *prop.get();
2862 assert!(value > 0.0 && value < 100.0);
2863 assert!(prop.is_animating());
2864
2865 prop.advance(500);
2866 assert_eq!(*prop.get(), 100.0);
2867 assert!(!prop.is_animating());
2868 }
2869
2870 #[test]
2871 fn test_animated_property_set_immediate() {
2872 let mut prop = AnimatedProperty::new(0.0_f32);
2873 prop.set_immediate(50.0);
2874
2875 assert_eq!(*prop.get(), 50.0);
2876 assert_eq!(*prop.target(), 50.0);
2877 assert!(!prop.is_animating());
2878 }
2879
2880 #[test]
2881 fn test_animated_property_with_delay() {
2882 let mut prop =
2883 AnimatedProperty::with_config(0.0_f32, TransitionConfig::new(1000).with_delay(500));
2884 prop.set(100.0);
2885
2886 prop.advance(250);
2888 assert_eq!(prop.progress(), 0.0);
2889 assert_eq!(*prop.get(), 0.0);
2890
2891 prop.advance(500); assert!(prop.progress() > 0.0);
2894 assert!(*prop.get() > 0.0);
2895 }
2896
2897 #[test]
2898 fn test_animated_property_f64() {
2899 let mut prop = AnimatedProperty::with_config(0.0_f64, TransitionConfig::new(1000));
2900 prop.set(100.0);
2901
2902 prop.advance(500);
2903 let value = *prop.get();
2904 assert!(value > 0.0 && value < 100.0);
2905 }
2906
2907 #[test]
2908 fn test_animated_property_color() {
2909 let mut prop =
2910 AnimatedProperty::with_config(crate::Color::BLACK, TransitionConfig::new(1000));
2911 prop.set(crate::Color::WHITE);
2912
2913 prop.advance(500);
2914 let color = *prop.get();
2915 assert!(color.r > 0.0 && color.r < 1.0);
2916 }
2917
2918 #[test]
2919 fn test_animated_property_point() {
2920 let mut prop =
2921 AnimatedProperty::with_config(crate::Point::new(0.0, 0.0), TransitionConfig::new(1000));
2922 prop.set(crate::Point::new(100.0, 200.0));
2923
2924 prop.advance(500);
2925 let point = *prop.get();
2926 assert!(point.x > 0.0 && point.x < 100.0);
2927 assert!(point.y > 0.0 && point.y < 200.0);
2928 }
2929
2930 #[test]
2931 fn test_animated_property_size() {
2932 let mut prop =
2933 AnimatedProperty::with_config(crate::Size::new(0.0, 0.0), TransitionConfig::new(1000));
2934 prop.set(crate::Size::new(100.0, 100.0));
2935
2936 prop.advance(500);
2937 let size = *prop.get();
2938 assert!(size.width > 0.0 && size.width < 100.0);
2939 }
2940
2941 #[test]
2942 fn test_animated_property_progress() {
2943 let mut prop = AnimatedProperty::with_config(0.0_f32, TransitionConfig::new(1000));
2944 prop.set(100.0);
2945
2946 assert_eq!(prop.progress(), 0.0);
2947
2948 prop.advance(250);
2949 assert!((prop.progress() - 0.25).abs() < 0.001);
2950
2951 prop.advance(750);
2952 assert!((prop.progress() - 1.0).abs() < 0.001);
2953 }
2954
2955 #[test]
2956 fn test_animated_property_interrupt() {
2957 let mut prop = AnimatedProperty::with_config(0.0_f32, TransitionConfig::new(1000));
2958 prop.set(100.0);
2959
2960 prop.advance(500);
2961 let mid_value = *prop.get();
2962 assert!(mid_value > 0.0);
2963
2964 prop.set(0.0);
2966 assert!(prop.is_animating());
2967 assert_eq!(*prop.target(), 0.0);
2968 }
2970
2971 #[test]
2976 fn test_spring_config_default() {
2977 let config = SpringConfig::default();
2978 assert_eq!(config.stiffness, 100.0);
2979 assert_eq!(config.damping, 10.0);
2980 assert_eq!(config.mass, 1.0);
2981 }
2982
2983 #[test]
2984 fn test_spring_config_presets() {
2985 let gentle = SpringConfig::gentle();
2986 assert_eq!(gentle.damping, 15.0);
2987
2988 let bouncy = SpringConfig::bouncy();
2989 assert_eq!(bouncy.stiffness, 300.0);
2990
2991 let stiff = SpringConfig::stiff();
2992 assert_eq!(stiff.stiffness, 500.0);
2993 assert_eq!(stiff.damping, 30.0);
2994 }
2995
2996 #[test]
3001 fn test_spring_animation_new() {
3002 let spring = SpringAnimation::new(0.0);
3003 assert_eq!(spring.position(), 0.0);
3004 assert_eq!(spring.velocity(), 0.0);
3005 assert_eq!(spring.target(), 0.0);
3006 }
3007
3008 #[test]
3009 fn test_spring_animation_set_target() {
3010 let mut spring = SpringAnimation::new(0.0);
3011 spring.set_target(100.0);
3012
3013 assert_eq!(spring.target(), 100.0);
3014 assert_eq!(spring.position(), 0.0);
3015 }
3016
3017 #[test]
3018 fn test_spring_animation_advance() {
3019 let mut spring = SpringAnimation::new(0.0);
3020 spring.set_target(100.0);
3021
3022 for _ in 0..100 {
3024 spring.advance_ms(16);
3025 }
3026
3027 assert!(spring.position() > 50.0);
3029 }
3030
3031 #[test]
3032 fn test_spring_animation_at_rest() {
3033 let mut spring = SpringAnimation::new(0.0);
3034 assert!(spring.is_at_rest()); spring.set_target(100.0);
3037 assert!(!spring.is_at_rest());
3038
3039 for _ in 0..500 {
3041 spring.advance_ms(16);
3042 if spring.is_at_rest() {
3043 break;
3044 }
3045 }
3046
3047 assert!(spring.is_at_rest());
3048 assert!((spring.position() - 100.0).abs() < 0.01);
3049 }
3050
3051 #[test]
3052 fn test_spring_animation_set_immediate() {
3053 let mut spring = SpringAnimation::new(0.0);
3054 spring.set_target(100.0);
3055 spring.advance_ms(100);
3056
3057 spring.set_immediate(50.0);
3058
3059 assert_eq!(spring.position(), 50.0);
3060 assert_eq!(spring.target(), 50.0);
3061 assert_eq!(spring.velocity(), 0.0);
3062 assert!(spring.is_at_rest());
3063 }
3064
3065 #[test]
3066 fn test_spring_animation_bouncy() {
3067 let mut spring = SpringAnimation::with_config(0.0, SpringConfig::bouncy());
3068 spring.set_target(100.0);
3069
3070 let mut max_position = 0.0_f32;
3071
3072 for _ in 0..200 {
3074 spring.advance_ms(16);
3075 max_position = max_position.max(spring.position());
3076 }
3077
3078 assert!(max_position > 100.0);
3080 }
3081
3082 #[test]
3083 fn test_spring_animation_overdamped() {
3084 let config = SpringConfig::new(100.0, 50.0, 1.0);
3086 let mut spring = SpringAnimation::with_config(0.0, config);
3087 spring.set_target(100.0);
3088
3089 let mut max_position = 0.0_f32;
3090
3091 for _ in 0..500 {
3092 spring.advance_ms(16);
3093 max_position = max_position.max(spring.position());
3094 }
3095
3096 assert!(max_position <= 100.1); }
3099
3100 #[test]
3105 fn test_data_refresh_manager_new() {
3106 let manager = DataRefreshManager::new();
3107 assert!(manager.tasks().is_empty());
3108 }
3109
3110 #[test]
3111 fn test_data_refresh_manager_default() {
3112 let manager = DataRefreshManager::default();
3113 assert!(manager.tasks().is_empty());
3114 }
3115
3116 #[test]
3117 fn test_data_refresh_manager_register() {
3118 let mut manager = DataRefreshManager::new();
3119 manager.register("source1", 1000);
3120
3121 assert_eq!(manager.tasks().len(), 1);
3122 assert_eq!(manager.tasks()[0].key, "source1");
3123 assert_eq!(manager.tasks()[0].interval_ms, 1000);
3124 assert!(manager.tasks()[0].active);
3125 }
3126
3127 #[test]
3128 fn test_data_refresh_manager_register_multiple() {
3129 let mut manager = DataRefreshManager::new();
3130 manager.register("source1", 1000);
3131 manager.register("source2", 2000);
3132 manager.register("source3", 500);
3133
3134 assert_eq!(manager.tasks().len(), 3);
3135 }
3136
3137 #[test]
3138 fn test_data_refresh_manager_register_duplicate_updates() {
3139 let mut manager = DataRefreshManager::new();
3140 manager.register("source1", 1000);
3141 manager.register("source1", 2000);
3142
3143 assert_eq!(manager.tasks().len(), 1);
3144 assert_eq!(manager.tasks()[0].interval_ms, 2000);
3145 }
3146
3147 #[test]
3148 fn test_data_refresh_manager_unregister() {
3149 let mut manager = DataRefreshManager::new();
3150 manager.register("source1", 1000);
3151 manager.register("source2", 2000);
3152
3153 manager.unregister("source1");
3154
3155 assert_eq!(manager.tasks().len(), 1);
3156 assert_eq!(manager.tasks()[0].key, "source2");
3157 }
3158
3159 #[test]
3160 fn test_data_refresh_manager_unregister_nonexistent() {
3161 let mut manager = DataRefreshManager::new();
3162 manager.register("source1", 1000);
3163
3164 manager.unregister("nonexistent");
3165
3166 assert_eq!(manager.tasks().len(), 1);
3167 }
3168
3169 #[test]
3170 fn test_data_refresh_manager_pause() {
3171 let mut manager = DataRefreshManager::new();
3172 manager.register("source1", 1000);
3173
3174 manager.pause("source1");
3175
3176 assert!(!manager.tasks()[0].active);
3177 }
3178
3179 #[test]
3180 fn test_data_refresh_manager_pause_nonexistent() {
3181 let mut manager = DataRefreshManager::new();
3182 manager.register("source1", 1000);
3183
3184 manager.pause("nonexistent");
3186
3187 assert!(manager.tasks()[0].active);
3188 }
3189
3190 #[test]
3191 fn test_data_refresh_manager_resume() {
3192 let mut manager = DataRefreshManager::new();
3193 manager.register("source1", 1000);
3194 manager.pause("source1");
3195
3196 manager.resume("source1");
3197
3198 assert!(manager.tasks()[0].active);
3199 }
3200
3201 #[test]
3202 fn test_data_refresh_manager_resume_nonexistent() {
3203 let mut manager = DataRefreshManager::new();
3204 manager.register("source1", 1000);
3205 manager.pause("source1");
3206
3207 manager.resume("nonexistent");
3209
3210 assert!(!manager.tasks()[0].active);
3211 }
3212
3213 #[test]
3214 fn test_data_refresh_manager_update_initial() {
3215 let mut manager = DataRefreshManager::new();
3216 manager.register("source1", 1000);
3217
3218 let to_refresh = manager.update(0);
3220 assert!(to_refresh.is_empty());
3221
3222 let to_refresh = manager.update(1000);
3224 assert_eq!(to_refresh, vec!["source1"]);
3225 }
3226
3227 #[test]
3228 fn test_data_refresh_manager_update_before_interval() {
3229 let mut manager = DataRefreshManager::new();
3230 manager.register("source1", 1000);
3231
3232 manager.update(1000);
3234
3235 let to_refresh = manager.update(1500);
3237
3238 assert!(to_refresh.is_empty());
3239 }
3240
3241 #[test]
3242 fn test_data_refresh_manager_update_after_interval() {
3243 let mut manager = DataRefreshManager::new();
3244 manager.register("source1", 1000);
3245
3246 manager.update(1000);
3248
3249 let to_refresh = manager.update(2000);
3251
3252 assert_eq!(to_refresh, vec!["source1"]);
3253 }
3254
3255 #[test]
3256 fn test_data_refresh_manager_update_multiple_sources() {
3257 let mut manager = DataRefreshManager::new();
3258 manager.register("fast", 100);
3259 manager.register("slow", 1000);
3260
3261 let to_refresh = manager.update(100);
3263 assert!(to_refresh.contains(&"fast".to_string()));
3264
3265 let to_refresh = manager.update(1000);
3266 assert!(to_refresh.contains(&"slow".to_string()));
3267
3268 let to_refresh = manager.update(1200);
3270 assert_eq!(to_refresh.len(), 1);
3271 assert!(to_refresh.contains(&"fast".to_string()));
3272 }
3273
3274 #[test]
3275 fn test_data_refresh_manager_update_paused_skipped() {
3276 let mut manager = DataRefreshManager::new();
3277 manager.register("source1", 1000);
3278 manager.pause("source1");
3279
3280 let to_refresh = manager.update(2000);
3281
3282 assert!(to_refresh.is_empty());
3283 }
3284
3285 #[test]
3286 fn test_data_refresh_manager_force_refresh() {
3287 let mut manager = DataRefreshManager::new();
3288 manager.register("source1", 1000);
3289
3290 manager.update(1000);
3292
3293 let to_refresh = manager.update(1500);
3295 assert!(to_refresh.is_empty());
3296
3297 let result = manager.force_refresh("source1");
3299 assert!(result);
3300
3301 let to_refresh = manager.update(1500);
3303 assert_eq!(to_refresh, vec!["source1"]);
3304 }
3305
3306 #[test]
3307 fn test_data_refresh_manager_force_refresh_nonexistent() {
3308 let mut manager = DataRefreshManager::new();
3309 manager.register("source1", 1000);
3310
3311 let result = manager.force_refresh("nonexistent");
3312
3313 assert!(!result);
3314 }
3315
3316 #[test]
3317 fn test_data_refresh_manager_get_task() {
3318 let mut manager = DataRefreshManager::new();
3319 manager.register("source1", 1000);
3320
3321 let task = manager.get_task("source1");
3322 assert!(task.is_some());
3323 assert_eq!(task.unwrap().key, "source1");
3324
3325 let task = manager.get_task("nonexistent");
3326 assert!(task.is_none());
3327 }
3328
3329 #[test]
3330 fn test_data_refresh_manager_is_due() {
3331 let mut manager = DataRefreshManager::new();
3332 manager.register("source1", 1000);
3333
3334 manager.update(0);
3336 assert!(!manager.is_due("source1"));
3337
3338 manager.update(500);
3340 assert!(!manager.is_due("source1"));
3341
3342 manager.update(999);
3344 assert!(!manager.is_due("source1"));
3345
3346 manager.update(1000); assert!(!manager.is_due("source1")); manager.update(1500);
3354 assert!(!manager.is_due("source1")); }
3360
3361 #[test]
3362 fn test_data_refresh_manager_is_due_paused() {
3363 let mut manager = DataRefreshManager::new();
3364 manager.register("source1", 1000);
3365 manager.pause("source1");
3366
3367 manager.update(2000);
3368
3369 assert!(!manager.is_due("source1"));
3370 }
3371
3372 #[test]
3373 fn test_data_refresh_manager_is_due_nonexistent() {
3374 let manager = DataRefreshManager::new();
3375 assert!(!manager.is_due("nonexistent"));
3376 }
3377
3378 #[test]
3379 fn test_data_refresh_manager_time_until_refresh() {
3380 let mut manager = DataRefreshManager::new();
3381 manager.register("source1", 1000);
3382
3383 manager.update(1000);
3385
3386 assert_eq!(manager.time_until_refresh("source1"), Some(1000));
3388
3389 manager.update(1500);
3391 assert_eq!(manager.time_until_refresh("source1"), Some(500));
3394 }
3395
3396 #[test]
3397 fn test_data_refresh_manager_time_until_refresh_paused() {
3398 let mut manager = DataRefreshManager::new();
3399 manager.register("source1", 1000);
3400 manager.pause("source1");
3401
3402 manager.update(500);
3403
3404 assert_eq!(manager.time_until_refresh("source1"), Some(u64::MAX));
3405 }
3406
3407 #[test]
3408 fn test_data_refresh_manager_time_until_refresh_nonexistent() {
3409 let manager = DataRefreshManager::new();
3410 assert_eq!(manager.time_until_refresh("nonexistent"), None);
3411 }
3412
3413 #[test]
3414 fn test_data_refresh_manager_saturating_arithmetic() {
3415 let mut manager = DataRefreshManager::new();
3416 manager.register("source1", 1000);
3417
3418 manager.update(u64::MAX - 1);
3420 let to_refresh = manager.update(u64::MAX);
3421
3422 assert!(to_refresh.is_empty() || to_refresh.len() == 1);
3424 }
3425
3426 #[test]
3427 fn test_data_refresh_manager_reactivate_updates_interval() {
3428 let mut manager = DataRefreshManager::new();
3429 manager.register("source1", 1000);
3430 manager.pause("source1");
3431
3432 manager.register("source1", 500);
3434
3435 assert!(manager.tasks()[0].active);
3436 assert_eq!(manager.tasks()[0].interval_ms, 500);
3437 }
3438
3439 #[test]
3440 fn test_data_refresh_manager_multiple_refresh_cycles() {
3441 let mut manager = DataRefreshManager::new();
3442 manager.register("source1", 100);
3443
3444 let mut refresh_count = 0;
3445
3446 for time in (0..1000).step_by(50) {
3447 let to_refresh = manager.update(time as u64);
3448 refresh_count += to_refresh.len();
3449 }
3450
3451 assert!(refresh_count >= 9 && refresh_count <= 11);
3454 }
3455}