Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_tui/src/lib_tui/event.rs

18.1 KiB, 9 runs

created by r1870400018:1191, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1use crate::lib_tui::{
2 action::{
3 Action,
4 ActionData,
5 },
6 text::{
7 edit::EditorMode,
8 typ::TextViewType,
9 },
10 window::{
11 WindowManager,
12 WindowMode,
13 },
14};
15
16use oxedyne_fe2o3_core::prelude::*;
17
18use crossterm::{
19 event::{
20 KeyCode,
21 KeyEvent,
22 KeyModifiers,
23 },
24};
25
26
27#[derive(Clone, Debug, Eq, PartialEq)]
28pub enum AppFlow {
29 Quit,
30 Continue,
31 CursorOnly,
32 FocalWindowRender,
33 FullScreenRender,
34}
35
36#[derive(Clone, Debug, Default)]
37pub struct KeyState {
38 buf: String,
39}
40
41impl KeyState {
42
43 pub fn buffer(&self) -> &String {
44 &self.buf
45 }
46
47 pub fn clear(&mut self) {
48 self.buf = String::new();
49 }
50
51 pub fn is_empty(&self) -> bool {
52 self.buf.len() == 0
53 }
54
55 pub fn push(&mut self, c: char) {
56 self.buf.push(c);
57 }
58}
59
60impl WindowManager<'_> {
61
62 pub fn native_key_handler(
63 &mut self,
64 key: KeyEvent,
65 data: Option<ActionData>,
66 )
67 -> Outcome<AppFlow>
68 {
69 match key {
70 // ┌─────────────────────────────┐
71 // │ ALL MODES │
72 // └─────────────────────────────┘
73 // Ctrl-C: Exit app.
74 KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, .. } => {
75 return self.act(&Action::ExitApp, data);
76 }
77 // Esc: Exiting modes.
78 KeyEvent { code: KeyCode::Esc, modifiers: KeyModifiers::NONE, .. } => {
79 match self.mode {
80 WindowMode::Interaction => {
81 // If the user is in editor navigation mode, Esc drops them out of editing
82 // and back to window navigation mode. If the user is in another editor
83 // mode, they are dropped back to editor navigation mode.
84 let result = self.get_focal_window_text_box_mut();
85 if let Some(tbox) = res!(result) {
86 match &mut tbox.tview.vtyp {
87 TextViewType::Editable(editor) => {
88 match editor.mode {
89 EditorMode::Navigation => {
90 res!(self.set_mode_update_menu(WindowMode::Navigation, None));
91 }
92 _ => {
93 editor.mode = EditorMode::Navigation;
94 }
95 }
96 }
97 _ => {
98 res!(self.set_mode_update_menu(WindowMode::Navigation, None));
99 }
100 }
101 }
102 self.key_state.clear();
103 }
104 WindowMode::WindowManagement => {
105 return self.act(&Action::ReturnToWindowNavigationMode, data);
106 }
107 WindowMode::BorderManagement => {
108 return self.act(&Action::ReturnToWindowManagementMode, data);
109 }
110 _ => {}
111 }
112 }
113 // Shift + PageUp: Move menu selection up one line in current tab of menu window.
114 KeyEvent { code: KeyCode::PageUp, modifiers: KeyModifiers::SHIFT, .. } => {
115 return self.act(&Action::MoveCurrentMenuFocusUp, data);
116 }
117 // Shift + PageDown: Move menu selection down one line in current tab of menu window.
118 KeyEvent { code: KeyCode::PageDown, modifiers: KeyModifiers::SHIFT, .. } => {
119 return self.act(&Action::MoveCurrentMenuFocusDown, data);
120 }
121 // Home: Perform currently highlighted menu action.
122 KeyEvent { code: KeyCode::Home, modifiers: KeyModifiers::NONE, .. } => {
123 return self.act(&Action::ExecuteCurrentMenuAction, data);
124 }
125 // ┌─────────────────────────────┐
126 // │ NAVIGATION MODE │
127 // └─────────────────────────────┘
128 // Tab: Rotate the focus amongst windows.
129 KeyEvent { code: KeyCode::Tab, modifiers: KeyModifiers::NONE, .. }
130 if self.mode == WindowMode::Navigation =>
131 {
132 return self.act(&Action::MoveToNextWindow, data);
133 }
134 // Enter:
135 // - Transition from window navigation to interaction mode (enter the window).
136 // - If the key buffer is not empty, try and jump straight into the appropriate window.
137 KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::NONE, .. }
138 if self.mode == WindowMode::Navigation =>
139 {
140 return self.act(&Action::EnterWindow, data);
141 }
142 // Collect keys into the key buffer, to form a label which we can navigate to.
143 KeyEvent { code: KeyCode::Char(c), modifiers: KeyModifiers::NONE, .. }
144 if self.mode == WindowMode::Navigation
145 && c != ' ' =>
146 {
147 self.key_state.push(c);
148 }
149 // Space: Transition from window navigation to management mode.
150 KeyEvent { code: KeyCode::Char(c), modifiers: KeyModifiers::NONE, .. }
151 if self.mode == WindowMode::Navigation =>
152 {
153 match c {
154 ' ' => {
155 return self.act(&Action::EnterWindowManagementMode, data);
156 }
157 _ => {}
158 }
159 }
160 // ┌─────────────────────────────┐
161 // │ WINDOW MANAGEMENT MODE │
162 // └─────────────────────────────┘
163 // Space: Transition to border management mode.
164 KeyEvent { code: KeyCode::Char(c), modifiers: KeyModifiers::NONE, .. }
165 if self.mode == WindowMode::WindowManagement =>
166 {
167 match c {
168 ' ' => {
169 return self.act(&Action::EnterBorderManagementMode, data);
170 }
171 _ => {}
172 }
173 }
174 // Up: Translate window in window management mode.
175 KeyEvent { code: KeyCode::Up, modifiers: KeyModifiers::NONE, .. }
176 if self.mode == WindowMode::WindowManagement =>
177 {
178 return self.act(&Action::SlideWindowUp, data);
179 }
180 // Down: Translate window in window management mode.
181 KeyEvent { code: KeyCode::Down, modifiers: KeyModifiers::NONE, .. }
182 if self.mode == WindowMode::WindowManagement =>
183 {
184 return self.act(&Action::SlideWindowDown, data);
185 }
186 // Right: Translate window in window management mode.
187 KeyEvent { code: KeyCode::Right, modifiers: KeyModifiers::NONE, .. }
188 if self.mode == WindowMode::WindowManagement =>
189 {
190 return self.act(&Action::SlideWindowRight, data);
191 }
192 // Left: Translate window in window management mode.
193 KeyEvent { code: KeyCode::Left, modifiers: KeyModifiers::NONE, .. }
194 if self.mode == WindowMode::WindowManagement =>
195 {
196 return self.act(&Action::SlideWindowLeft, data);
197 }
198 // ┌─────────────────────────────┐
199 // │ BORDER MANAGEMENT MODE │
200 // └─────────────────────────────┘
201 // Space: Select next border.
202 KeyEvent { code: KeyCode::Char(c), modifiers: KeyModifiers::NONE, .. }
203 if self.mode == WindowMode::BorderManagement =>
204 {
205 match c {
206 ' ' => {
207 return self.act(&Action::MoveToNextBorder, data);
208 }
209 _ => {}
210 }
211 }
212 // Up: Resize in vertical direction in border management mode.
213 KeyEvent { code: KeyCode::Up, modifiers: KeyModifiers::NONE, .. }
214 if self.mode == WindowMode::BorderManagement =>
215 {
216 return self.act(&Action::DragBorderUp, data);
217 }
218 // Down: Resize in vertical direction in border management mode.
219 KeyEvent { code: KeyCode::Down, modifiers: KeyModifiers::NONE, .. }
220 if self.mode == WindowMode::BorderManagement =>
221 {
222 return self.act(&Action::DragBorderDown, data);
223 }
224 // Right: Resize in horizontal direction in border management mode.
225 KeyEvent { code: KeyCode::Right, modifiers: KeyModifiers::NONE, .. }
226 if self.mode == WindowMode::BorderManagement =>
227 {
228 return self.act(&Action::DragBorderRight, data);
229 }
230 // Left: Resize in horizontal direction in border management mode.
231 KeyEvent { code: KeyCode::Left, modifiers: KeyModifiers::NONE, .. }
232 if self.mode == WindowMode::BorderManagement =>
233 {
234 return self.act(&Action::DragBorderLeft, data);
235 }
236 // ┌─────────────────────────────┐
237 // │ INTERACTION MODE │
238 // └─────────────────────────────┘
239 // Up: Move cursor up one line, if possible.
240 KeyEvent { code: KeyCode::Up, modifiers: KeyModifiers::NONE, .. }
241 if self.mode == WindowMode::Interaction =>
242 {
243 return self.act(&Action::MoveCursorUp, data);
244 }
245 // Down: Move cursor down one line, if possible.
246 KeyEvent { code: KeyCode::Down, modifiers: KeyModifiers::NONE, .. }
247 if self.mode == WindowMode::Interaction =>
248 {
249 return self.act(&Action::MoveCursorDown, data);
250 }
251 // Right: Move one character to the right, if possible.
252 KeyEvent { code: KeyCode::Right, modifiers: KeyModifiers::NONE, .. }
253 if self.mode == WindowMode::Interaction =>
254 {
255 return self.act(&Action::MoveCursorRight, data);
256 }
257 // Left: Move one character to the left, if possible.
258 KeyEvent { code: KeyCode::Left, modifiers: KeyModifiers::NONE, .. }
259 if self.mode == WindowMode::Interaction =>
260 {
261 return self.act(&Action::MoveCursorLeft, data);
262 }
263 // Shift Up: Pan text view up, if possible.
264 KeyEvent { code: KeyCode::Up, modifiers: KeyModifiers::SHIFT, .. }
265 if self.mode == WindowMode::Interaction =>
266 {
267 return self.act(&Action::PanTextViewUp, data);
268 }
269 // Shift Down: Pan text view down, if possible.
270 KeyEvent { code: KeyCode::Down, modifiers: KeyModifiers::SHIFT, .. }
271 if self.mode == WindowMode::Interaction =>
272 {
273 return self.act(&Action::PanTextViewDown, data);
274 }
275 // Shift Right: Pan text view right, if possible.
276 KeyEvent { code: KeyCode::Right, modifiers: KeyModifiers::SHIFT, .. }
277 if self.mode == WindowMode::Interaction =>
278 {
279 return self.act(&Action::PanTextViewRight, data);
280 }
281 // Shift Left: Pan text view left, if possible.
282 KeyEvent { code: KeyCode::Left, modifiers: KeyModifiers::SHIFT, .. }
283 if self.mode == WindowMode::Interaction =>
284 {
285 return self.act(&Action::PanTextViewLeft, data);
286 }
287 // Char:
288 // - Transition from intra-window navigation to other editor modes.
289 // - Character insertion in insert or replace editor modes.
290 KeyEvent { code: KeyCode::Char(c), modifiers: KeyModifiers::NONE | KeyModifiers::SHIFT, .. }
291 if self.mode == WindowMode::Interaction =>
292 {
293 let result = self.get_focal_window_mut();
294 let win = res!(result);
295 if let Some(tbox) = win.get_focal_text_box_mut() {
296 if let Some(editor) = tbox.tview.vtyp.get_editor_mut() {
297 match editor.mode {
298 EditorMode::Navigation => {
299 match c {
300 'i' => editor.mode = EditorMode::Insert,
301 'r' => editor.mode = EditorMode::Replace,
302 'm' => editor.mode = EditorMode::Modify,
303 _ => {}
304 }
305 }
306 EditorMode::Insert | EditorMode::Replace => {
307 let result = tbox.tview.atext.get_text_lines_mut();
308 let mut text_lines = res!(result);
309 text_lines.add_char(
310 &mut editor.nav.cursor,
311 c,
312 editor.mode == EditorMode::Replace,
313 );
314 }
315 _ => {}
316 }
317 }
318 }
319 }
320 // Shift + Tab doesn't work reliably.
321 // - Rotate the focus amongst tabs.
322 //KeyEvent { code: KeyCode::BackTab, modifiers: KeyModifiers::NONE, .. }
323 // if self.mode == WindowMode::Interaction =>
324 //{
325 // debug!("Shift + Tab");
326 // let win = res!(self.get_focal_window_mut());
327 // if let Some(tbox) = win.get_focal_text_box() {
328 // match &tbox.tview.vtyp {
329 // TextViewType::Editable(editor) => match editor.mode {
330 // EditorMode::Navigation => {
331 // win.state.text_boxes.next_focus();
332 // }
333 // _ => {}
334 // }
335 // _ => {
336 // win.state.text_boxes.next_focus();
337 // }
338 // }
339 // }
340 //}
341 // Tab:
342 // - If navigating: rotate highlighting of line focus ranges in text.
343 // - If editing: insert tab characters (defined via configuration) into current text.
344 KeyEvent { code: KeyCode::Tab, modifiers: KeyModifiers::NONE, .. }
345 if self.mode == WindowMode::Interaction =>
346 {
347 return self.act(&Action::MoveToNextTab, data);
348 }
349 // Backspace: Delete character to the left of the cursors, dragging the remainder of
350 // the line from the right.
351 KeyEvent { code: KeyCode::Backspace, modifiers: KeyModifiers::NONE, .. }
352 if self.mode == WindowMode::Interaction =>
353 {
354 let result = self.get_focal_window_text_box_mut();
355 if let Some(tbox) = res!(result) {
356 if let Some(editor) = tbox.tview.vtyp.get_editor_mut() {
357 match editor.mode {
358 EditorMode::Insert | EditorMode::Replace | EditorMode::Modify => {
359 let result = tbox.tview.atext.get_text_lines_mut();
360 let mut text_lines = res!(result);
361 text_lines.backspace(&mut editor.nav.cursor);
362 }
363 _ => {}
364 }
365 }
366 }
367 }
368 // Delete: Delete character under the cursor, dragging the remainder of the line from
369 // the right.
370 KeyEvent { code: KeyCode::Delete, modifiers: KeyModifiers::NONE, .. }
371 if self.mode == WindowMode::Interaction =>
372 {
373 let result = self.get_focal_window_text_box_mut();
374 if let Some(tbox) = res!(result) {
375 if let Some(editor) = tbox.tview.vtyp.get_editor_mut() {
376 match editor.mode {
377 EditorMode::Insert | EditorMode::Replace | EditorMode::Modify => {
378 let result = tbox.tview.atext.get_text_lines_mut();
379 let mut text_lines = res!(result);
380 text_lines.delete_char(&mut editor.nav.cursor);
381 }
382 _ => {}
383 }
384 }
385 }
386 }
387 // Enter: Insert a new line.
388 KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::NONE, .. }
389 if self.mode == WindowMode::Interaction =>
390 {
391 let result = self.get_focal_window_text_box_mut();
392 if let Some(tbox) = res!(result) {
393 if let Some(editor) = tbox.tview.vtyp.get_editor_mut() {
394 match editor.mode {
395 EditorMode::Insert | EditorMode::Replace | EditorMode::Modify => {
396 let result = tbox.tview.atext.get_text_lines_mut();
397 let mut text_lines = res!(result);
398 text_lines.enter_new_line(&mut editor.nav.cursor);
399 }
400 _ => {}
401 }
402 }
403 }
404 }
405 _ => {}
406 }
407 Ok(AppFlow::FullScreenRender)
408 }
409}