process_terminal/
keyboard_actions.rs1use {
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}