Skip to main content

tix/ui/
dialog.rs

1use crossterm::event::KeyCode;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct RolloverDialog {
5    pub plans: Vec<(String, String, usize)>,
6    pub choice: DialogChoice,
7    pub dismissed: bool,
8}
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum DialogChoice {
12    DoRollover,
13    Ignore,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum DialogEvent {
18    None,
19    ConfirmDo,
20    ConfirmIgnore,
21    Dismiss,
22}
23
24impl RolloverDialog {
25    pub fn new(plans: Vec<(String, String, usize)>) -> Self {
26        Self {
27            plans,
28            choice: DialogChoice::DoRollover,
29            dismissed: false,
30        }
31    }
32
33    pub fn is_active(&self) -> bool {
34        !self.plans.is_empty() && !self.dismissed
35    }
36
37    pub fn handle_key(&mut self, key: KeyCode) -> DialogEvent {
38        if !self.is_active() {
39            return DialogEvent::Dismiss;
40        }
41        match key {
42            KeyCode::Left | KeyCode::Right | KeyCode::Tab => {
43                self.choice = match self.choice {
44                    DialogChoice::DoRollover => DialogChoice::Ignore,
45                    DialogChoice::Ignore => DialogChoice::DoRollover,
46                };
47                DialogEvent::None
48            }
49            KeyCode::Esc => {
50                self.dismissed = true;
51                DialogEvent::Dismiss
52            }
53            KeyCode::Enter => {
54                self.dismissed = true;
55                match self.choice {
56                    DialogChoice::DoRollover => DialogEvent::ConfirmDo,
57                    DialogChoice::Ignore => DialogEvent::ConfirmIgnore,
58                }
59            }
60            _ => DialogEvent::None,
61        }
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    #[allow(clippy::wildcard_imports)]
68    use super::*;
69
70    #[test]
71    fn dialog_choice_toggles() {
72        let mut dialog = RolloverDialog::new(vec![("Cycle 1".into(), "Cycle 2".into(), 2)]);
73        assert_eq!(dialog.choice, DialogChoice::DoRollover);
74        dialog.handle_key(KeyCode::Tab);
75        assert_eq!(dialog.choice, DialogChoice::Ignore);
76    }
77
78    #[test]
79    fn enter_confirms_active_choice() {
80        let mut dialog = RolloverDialog::new(vec![("Cycle 1".into(), "Cycle 2".into(), 2)]);
81        assert_eq!(dialog.handle_key(KeyCode::Enter), DialogEvent::ConfirmDo);
82
83        let mut dialog = RolloverDialog::new(vec![("Cycle 1".into(), "Cycle 2".into(), 2)]);
84        dialog.handle_key(KeyCode::Right);
85        assert_eq!(
86            dialog.handle_key(KeyCode::Enter),
87            DialogEvent::ConfirmIgnore
88        );
89    }
90
91    #[test]
92    fn esc_dismisses_dialog() {
93        let mut dialog = RolloverDialog::new(vec![("Cycle 1".into(), "Cycle 2".into(), 2)]);
94        assert_eq!(dialog.handle_key(KeyCode::Esc), DialogEvent::Dismiss);
95        assert!(!dialog.is_active());
96    }
97}