Skip to main content

ftui_runtime/tick_strategy/
active_only.rs

1//! [`ActiveOnly`] strategy: only the active screen ticks.
2
3use super::{TickDecision, TickStrategy};
4
5/// Simplest strategy — all inactive screens are skipped every frame.
6///
7/// Use when screen tick methods are expensive (heavy DB queries, network
8/// calls) and force-tick-on-activation provides sufficient freshness.
9#[derive(Debug, Clone, Copy, Default)]
10pub struct ActiveOnly;
11
12impl TickStrategy for ActiveOnly {
13    fn should_tick(
14        &mut self,
15        _screen_id: &str,
16        _tick_count: u64,
17        _active_screen: &str,
18    ) -> TickDecision {
19        TickDecision::Skip
20    }
21
22    fn name(&self) -> &str {
23        "ActiveOnly"
24    }
25
26    fn debug_stats(&self) -> Vec<(String, String)> {
27        vec![
28            ("strategy".into(), "ActiveOnly".into()),
29            ("background_ticks".into(), "0".into()),
30        ]
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn always_skips() {
40        let mut s = ActiveOnly;
41        for tick in 0..100 {
42            assert_eq!(
43                s.should_tick("screen_a", tick, "screen_b"),
44                TickDecision::Skip
45            );
46        }
47    }
48
49    #[test]
50    fn name_is_stable() {
51        assert_eq!(ActiveOnly.name(), "ActiveOnly");
52    }
53
54    #[test]
55    fn default_hooks_are_noops() {
56        let mut s = ActiveOnly;
57        s.on_screen_transition("a", "b");
58        s.maintenance_tick(42);
59        s.shutdown();
60        let stats = s.debug_stats();
61        assert!(
62            stats
63                .iter()
64                .any(|(k, v)| k == "strategy" && v == "ActiveOnly")
65        );
66        assert!(
67            stats
68                .iter()
69                .any(|(k, v)| k == "background_ticks" && v == "0")
70        );
71    }
72}