process_terminal/
keyboard_actions.rs

1use {
2    crate::{shared::Shared, ExitCallback, SharedMessages},
3    anyhow::{anyhow, Result},
4    crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers},
5};
6
7pub struct KeyBoardActions {
8    actions: Vec<Action>,
9    focus: Shared<Option<usize>>,
10}
11
12impl KeyBoardActions {
13    pub fn new(main_messages: SharedMessages) -> (Self, BaseStatus, Shared<ExitCallback>) {
14        let base_status: BaseStatus = Default::default();
15        let exit_callback: Shared<ExitCallback> = Default::default();
16
17        let main_action_scroll = ActionScroll {
18            status: base_status.main_scroll.clone(),
19            messages: main_messages.clone(),
20        };
21
22        let actions = vec![
23            Action {
24                event: KeyCode::Char('c').into_event(KeyModifiers::CONTROL),
25                data: ActionType::Close(exit_callback.clone()),
26            },
27            Action {
28                event: KeyCode::Up.into_event_no_modifier(),
29                data: ActionType::ScrollUp(main_action_scroll.clone()),
30            },
31            Action {
32                event: KeyCode::Down.into_event_no_modifier(),
33                data: ActionType::ScrollDown(main_action_scroll.clone()),
34            },
35            Action {
36                event: KeyCode::Left.into_event_no_modifier(),
37                data: ActionType::ScrollLeft(main_action_scroll.clone()),
38            },
39            Action {
40                event: KeyCode::Right.into_event_no_modifier(),
41                data: ActionType::ScrollRight(main_action_scroll.clone()),
42            },
43            Action {
44                event: KeyCode::Char('0').into_event_no_modifier(),
45                data: ActionType::Focus((0, base_status.focus.clone())),
46            },
47            Action {
48                event: KeyCode::Esc.into_event_no_modifier(),
49                data: ActionType::RemoveFocus(base_status.focus.clone()),
50            },
51        ];
52
53        (
54            Self {
55                actions,
56                focus: base_status.focus.clone(),
57            },
58            base_status,
59            exit_callback,
60        )
61    }
62
63    pub fn apply_event(&self, event: Event) {
64        let events = self
65            .actions
66            .iter()
67            .filter(|action| action.event == event)
68            .collect::<Vec<_>>();
69
70        for action in events {
71            action.data.apply();
72        }
73    }
74
75    pub fn push(&mut self, action: Action) {
76        self.actions.push(action);
77    }
78
79    pub fn push_focus(&mut self, indexes: &[usize]) -> Result<()> {
80        for index in indexes {
81            let char = to_char(*index)?;
82
83            self.push(Action::new(
84                KeyCode::Char(char).into_event_no_modifier(),
85                ActionType::Focus((*index, self.focus.clone())),
86            ));
87        }
88
89        Ok(())
90    }
91}
92
93pub struct Action {
94    pub event: Event,
95    pub data: ActionType,
96}
97
98impl Action {
99    pub fn new(event: Event, data: ActionType) -> Self {
100        Self { event, data }
101    }
102}
103
104pub enum ActionType {
105    Close(Shared<ExitCallback>),
106    ScrollUp(ActionScroll),
107    ScrollDown(ActionScroll),
108    ScrollLeft(ActionScroll),
109    ScrollRight(ActionScroll),
110    StopScrolling(Shared<ScrollStatus>),
111    Focus((usize, Shared<Option<usize>>)),
112    RemoveFocus(Shared<Option<usize>>),
113}
114
115impl ActionType {
116    pub fn apply(&self) {
117        match self {
118            ActionType::Close(exit_callback) => {
119                ratatui::restore();
120
121                if let Some(callback) = exit_callback.read_access().as_ref() {
122                    callback();
123                }
124
125                std::process::exit(0);
126            }
127            ActionType::ScrollUp(shared) => {
128                shared.status.write_with(|mut status| {
129                    if let Some(y) = &mut status.y {
130                        *y = y.saturating_sub(1);
131                    } else {
132                        status.y = Some(shared.messages.read_access().len() as u16);
133                    }
134                });
135            }
136            ActionType::ScrollDown(shared) => {
137                shared.status.write_with(|mut status| {
138                    if let Some(y) = &mut status.y {
139                        *y += 1
140                    }
141                });
142            }
143            ActionType::ScrollLeft(shared) => {
144                shared.status.write_with(|mut status| {
145                    status.x = status.x.saturating_sub(1);
146                });
147            }
148            ActionType::ScrollRight(shared) => {
149                shared.status.write_with(|mut status| {
150                    status.x = status.x + 1;
151                });
152            }
153            ActionType::StopScrolling(shared) => {
154                shared.write_with(|mut status| {
155                    status.y = None;
156                });
157            }
158            ActionType::Focus((index, shared)) => {
159                shared.write_with(|mut focus| {
160                    *focus = Some(*index);
161                });
162            }
163            ActionType::RemoveFocus(shared) => {
164                shared.write_with(|mut focus| {
165                    *focus = None;
166                });
167            }
168        }
169    }
170}
171
172#[derive(Default, Clone, PartialEq)]
173pub(crate) struct ScrollStatus {
174    pub x: u16,
175    pub y: Option<u16>,
176}
177
178#[derive(Clone)]
179pub(crate) struct ActionScroll {
180    pub status: Shared<ScrollStatus>,
181    pub messages: SharedMessages,
182}
183
184pub trait KeyCodeExt: Sized {
185    fn into_event(self, modifier: KeyModifiers) -> Event;
186
187    fn into_event_no_modifier(self) -> Event {
188        self.into_event(KeyModifiers::empty())
189    }
190}
191
192impl KeyCodeExt for KeyCode {
193    fn into_event(self, modifier: KeyModifiers) -> Event {
194        Event::Key(KeyEvent::new(self, modifier))
195    }
196}
197
198pub type DetachBaseStatus = BaseStatus<ScrollStatus, Option<usize>>;
199
200#[derive(Default, Clone, PartialEq)]
201pub struct BaseStatus<MS = Shared<ScrollStatus>, F = Shared<Option<usize>>> {
202    pub main_scroll: MS,
203    pub focus: F,
204}
205
206impl BaseStatus {
207    pub fn detach(&self) -> BaseStatus<ScrollStatus, Option<usize>> {
208        BaseStatus {
209            main_scroll: self.main_scroll.read_access().clone(),
210            focus: self.focus.read_access().clone(),
211        }
212    }
213}
214
215fn to_char(index: usize) -> Result<char> {
216    let str_index = index.to_string();
217    let mut chars = str_index.chars();
218
219    if let (Some(char), None) = (chars.next(), chars.next()) {
220        Ok(char)
221    } else {
222        return Err(anyhow!("Can't add more then 9 processes."));
223    }
224}