Oregami
Repositories/oxedyne/fe2o3

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

28.0 KiB, 99 runs

created by r1870400018:1147, 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 draw::{
3 outline::BorderManagementMode,
4 window::{
5 TextBoxesState,
6 WindowId,
7 },
8 },
9 event::AppFlow,
10 text::{
11 edit::EditorMode,
12 typ::{
13 ContentType,
14 HighlightType,
15 TextViewType,
16 },
17 },
18 window::{
19 WindowManager,
20 WindowMode,
21 },
22};
23
24use oxedyne_fe2o3_core::prelude::*;
25use oxedyne_fe2o3_geom::{
26 dim::{
27 Coord,
28 Dim,
29 },
30 rect::{
31 AbsRect,
32 RectSide,
33 RectView,
34 },
35};
36
37
38#[derive(Clone, Copy, Debug, Default)]
39pub enum Action {
40 // All Modes
41 MoveCurrentMenuFocusUp,
42 MoveCurrentMenuFocusDown,
43 ExecuteCurrentMenuAction,
44 ExitApp,
45 // Navigation
46 #[default]
47 MoveToNextWindow,
48 EnterWindow,
49 EnterWindowManagementMode,
50 CreateNewWindow,
51 // Window Management
52 EnterBorderManagementMode,
53 SlideWindowUp,
54 SlideWindowDown,
55 SlideWindowRight,
56 SlideWindowLeft,
57 CreateWindow,
58 DeleteWindow,
59 ReturnToWindowNavigationMode,
60 // Border Management
61 MoveToNextBorder,
62 DragBorderUp,
63 DragBorderDown,
64 DragBorderRight,
65 DragBorderLeft,
66 ReturnToWindowManagementMode,
67 // Interaction
68 MoveToNextTab,
69 MoveCursorUp,
70 MoveCursorDown,
71 MoveCursorRight,
72 MoveCursorLeft,
73 PanTextViewUp,
74 PanTextViewDown,
75 PanTextViewRight,
76 PanTextViewLeft,
77 EnterEditorInsertMode,
78 EnterEditorReplaceMode,
79 EnterEditorModifyMode,
80 ReturnToEditorNavigationMode,
81 //ReturnToWindowNavigationMode,
82 // Supplementary interaction
83 CreateNewTextTab,
84}
85
86#[derive(Clone, Debug, Default)]
87pub struct ActionData {
88 pub term: Option<AbsRect>,
89 pub c: Option<char>,
90}
91
92impl ActionData {
93
94 pub fn must_get_term(self) -> Outcome<AbsRect> {
95 if let Some(term) = self.term {
96 Ok(term)
97 } else {
98 Err(err!(
99 "Terminal view is missing from ActionData.";
100 Bug, Data, Missing))
101 }
102 }
103
104 pub fn must_get_char(self) -> Outcome<char> {
105 if let Some(c) = self.c {
106 Ok(c)
107 } else {
108 Err(err!(
109 "Character is missing from ActionData.";
110 Bug, Data, Missing))
111 }
112 }
113}
114
115impl WindowManager<'_> {
116
117 pub fn must_get_term(data: Option<ActionData>) -> Outcome<AbsRect> {
118 if let Some(data) = data {
119 data.must_get_term()
120 } else {
121 Err(err!(
122 "ActionData is missing.";
123 Bug, Data, Missing))
124 }
125 }
126
127 pub fn must_get_char(data: Option<ActionData>) -> Outcome<char> {
128 if let Some(data) = data {
129 data.must_get_char()
130 } else {
131 Err(err!(
132 "ActionData is missing.";
133 Bug, Data, Missing))
134 }
135 }
136
137 pub fn act(
138 &mut self,
139 action: &Action,
140 data: Option<ActionData>,
141 )
142 -> Outcome<AppFlow>
143 {
144 match action {
145 // ┌─────────────────────────────┐
146 // │ ALL MODES │
147 // └─────────────────────────────┘
148 Action::MoveCurrentMenuFocusUp => {
149 let result = self.get_window_by_id_mut(&WindowId::Menu);
150 let win = res!(result);
151 if let Some(tbox) = win.state.text_boxes.get_text_box_mut() {
152 {
153 let result = tbox.tview.atext.get_text_lines_mut();
154 let mut text_lines = res!(result);
155 if let Some(highlighter) = text_lines.get_highlighter_mut() {
156 highlighter.dec_focus();
157 }
158 }
159 let _ = tbox.cursor_up();
160 }
161 return Ok(AppFlow::FullScreenRender);
162 }
163 Action::MoveCurrentMenuFocusDown => {
164 let result = self.get_window_by_id_mut(&WindowId::Menu);
165 let win = res!(result);
166 if let Some(tbox) = win.state.text_boxes.get_text_box_mut() {
167 {
168 let result = tbox.tview.atext.get_text_lines_mut();
169 let mut text_lines = res!(result);
170 if let Some(highlighter) = text_lines.get_highlighter_mut() {
171 highlighter.inc_focus();
172 }
173 }
174 tbox.cursor_down();
175 }
176 return Ok(AppFlow::FullScreenRender);
177 }
178 Action::ExecuteCurrentMenuAction => {
179 let result = self.get_window_by_id_mut(&WindowId::Menu);
180 let win = res!(result);
181 let mut execute_action = None;
182 if let Some(tbox) = win.state.text_boxes.get_text_box() {
183 let text_lines = res!(tbox.tview.atext.get_text_lines());
184 if let Some(highlighter) = text_lines.get_highlighter() {
185 if let Some(highlight) = highlighter.get_highlighted() {
186 if let Some(HighlightType::Menu(action)) = highlight.get_data() {
187 execute_action = Some(action.clone());
188 }
189 }
190 }
191 }
192 if let Some(action) = execute_action {
193 return self.act(&action, data);
194 }
195 return Ok(AppFlow::FullScreenRender);
196 }
197 Action::ExitApp => {
198 return Ok(AppFlow::Quit);
199 }
200 // ┌─────────────────────────────┐
201 // │ NAVIGATION MODE │
202 // └─────────────────────────────┘
203 Action::MoveToNextWindow => {
204 self.next_focus();
205 return Ok(AppFlow::FullScreenRender);
206 }
207 Action::EnterWindow => {
208 if self.key_state.is_empty() {
209 res!(self.set_mode_update_menu(WindowMode::Interaction, None));
210 } else {
211 let buf = self.key_state.buffer().clone();
212 match self.set_focus_by_label(&buf) {
213 Ok(()) => {
214 res!(self.set_mode_update_menu(WindowMode::Interaction, None));
215 }
216 Err(_) => {}
217 }
218 self.key_state.clear();
219 }
220 return Ok(AppFlow::FullScreenRender);
221 }
222 Action::EnterWindowManagementMode => {
223 res!(self.set_mode_update_menu(WindowMode::WindowManagement, None));
224 let result = self.get_focal_window_mut();
225 let win = res!(result);
226 // If the window has a relative specification switch it to an
227 // absolute one.
228 let term = ok!(Self::must_get_term(data));
229 if let RectView::InitiallyRelative(_rel_rect) = &mut win.view {
230 match win.view.relative_to(term) {
231 Some(view) => win.view = RectView::Float(view),
232 _ => {}
233 }
234 }
235 return Ok(AppFlow::FocalWindowRender);
236 }
237 Action::CreateNewWindow => {
238 let id = res!(self.next_id());
239 let top_left = Coord::from((
240 self.state.new_win_pos.0.next(),
241 self.state.new_win_pos.1.next(),
242 ));
243 let (cfg, view, state) = res!(self.style_lib.default_new_window(
244 &self.cfg,
245 &id,
246 top_left,
247 ));
248 res!(self.add_window(Some(id.clone()), cfg, view));
249 res!(self.set_state(&id, state));
250 res!(self.set_focus_by_id(&id));
251 return Ok(AppFlow::FullScreenRender);
252 }
253 // ┌─────────────────────────────┐
254 // │ WINDOW MANAGEMENT MODE │
255 // └─────────────────────────────┘
256 Action::EnterBorderManagementMode => {
257 let result = self.get_focal_window_mut();
258 let win = res!(result);
259 // Deforming the window requires that it be specified absolutely in the
260 // terminal frame.
261 if let RectView::Float(_abs_rect) = win.view {
262 win.state.lines.mode = Some(BorderManagementMode::Adjust);
263 res!(self.set_mode_update_menu(WindowMode::BorderManagement, None));
264 }
265 return Ok(AppFlow::FocalWindowRender);
266 }
267 Action::SlideWindowUp => {
268 let result = self.get_focal_window_mut();
269 let win = res!(result);
270 match &mut win.view {
271 // Translate entire window upward, if possible.
272 RectView::Float(abs_rect) => {
273 if abs_rect.top() > Dim(0) {
274 abs_rect.dec_y(Dim(1));
275 }
276 }
277 _ => {}
278 }
279 return Ok(AppFlow::FullScreenRender);
280 }
281 Action::SlideWindowDown => {
282 let result = self.get_focal_window_mut();
283 let win = res!(result);
284 match &mut win.view {
285 // Translate entire window downward, if possible.
286 RectView::Float(abs_rect) => {
287 let term = ok!(Self::must_get_term(data));
288 if abs_rect.bottom() < term.bottom() {
289 abs_rect.inc_y(Dim(1));
290 }
291 }
292 _ => {}
293 }
294 return Ok(AppFlow::FullScreenRender);
295 }
296 Action::SlideWindowRight => {
297 let result = self.get_focal_window_mut();
298 let win = res!(result);
299 match &mut win.view {
300 // Translate entire window rightward, if possible.
301 RectView::Float(abs_rect) => {
302 let term = ok!(Self::must_get_term(data));
303 if abs_rect.right() < term.right() {
304 abs_rect.inc_x(Dim(1));
305 }
306 }
307 _ => {}
308 }
309 return Ok(AppFlow::FullScreenRender);
310 }
311 Action::SlideWindowLeft => {
312 let result = self.get_focal_window_mut();
313 let win = res!(result);
314 match &mut win.view {
315 // Translate entire window leftward, if possible.
316 RectView::Float(abs_rect) => {
317 if abs_rect.left() > Dim(0) {
318 abs_rect.dec_x(Dim(1));
319 }
320 }
321 _ => {}
322 }
323 return Ok(AppFlow::FullScreenRender);
324 }
325 Action::CreateWindow => {
326 return Ok(AppFlow::FullScreenRender);
327 }
328 Action::DeleteWindow => {
329 return Ok(AppFlow::FullScreenRender);
330 }
331 Action::ReturnToWindowNavigationMode => {
332 res!(self.set_mode_update_menu(WindowMode::Navigation, None));
333 self.key_state.clear();
334 return Ok(AppFlow::FocalWindowRender);
335 }
336 // ┌─────────────────────────────┐
337 // │ BORDER MANAGEMENT MODE │
338 // └─────────────────────────────┘
339 Action::MoveToNextBorder => {
340 let result = self.get_focal_window_mut();
341 let win = res!(result);
342 match &mut win.state.lines.mode {
343 Some(BorderManagementMode::Adjust) => {
344 win.state.lines.line = win.state.lines.iter.next();
345 }
346 _ => {}
347 }
348 return Ok(AppFlow::FocalWindowRender);
349 }
350 Action::DragBorderUp => {
351 let result = self.get_focal_window_mut();
352 let win = res!(result);
353 match win.state.lines.mode {
354 Some(BorderManagementMode::Adjust) => {
355 match win.state.lines.line {
356 // Move bottom line upward, if possible.
357 RectSide::Bottom => {
358 match &mut win.view {
359 RectView::Float(abs_rect) => {
360 if abs_rect.height() > win.cfg.min_size.y {
361 abs_rect.dec_h(Dim(1));
362 }
363 }
364 _ => {}
365 }
366 }
367 // Move top line upward, if possible.
368 RectSide::Top => {
369 match &mut win.view {
370 RectView::Float(abs_rect) => {
371 if abs_rect.top() > Dim(0) {
372 abs_rect.dec_y(Dim(1));
373 abs_rect.inc_h(Dim(1));
374 }
375 }
376 _ => {}
377 }
378 }
379 _ => {}
380 }
381 }
382 _ => {}
383 }
384 return Ok(AppFlow::FullScreenRender);
385 }
386 Action::DragBorderDown => {
387 let result = self.get_focal_window_mut();
388 let win = res!(result);
389 match win.state.lines.mode {
390 Some(BorderManagementMode::Adjust) => {
391 match win.state.lines.line {
392 // Move bottom line downward, if possible.
393 RectSide::Bottom => {
394 match &mut win.view {
395 RectView::Float(abs_rect) => {
396 let term = ok!(Self::must_get_term(data));
397 if abs_rect.bottom() < term.bottom() {
398 abs_rect.inc_h(Dim(1));
399 }
400 }
401 _ => {}
402 }
403 }
404 // Move top line downward, if possible.
405 RectSide::Top => {
406 match &mut win.view {
407 RectView::Float(abs_rect) => {
408 if abs_rect.height() > win.cfg.min_size.y {
409 abs_rect.inc_y(Dim(1));
410 abs_rect.dec_h(Dim(1));
411 }
412 }
413 _ => {}
414 }
415 }
416 _ => {}
417 }
418 }
419 _ => {}
420 }
421 return Ok(AppFlow::FullScreenRender);
422 }
423 Action::DragBorderRight => {
424 let result = self.get_focal_window_mut();
425 let win = res!(result);
426 match win.state.lines.mode {
427 Some(BorderManagementMode::Adjust) => {
428 match win.state.lines.line {
429 // Move right line rightward, if possible.
430 RectSide::Right => {
431 match &mut win.view {
432 RectView::Float(abs_rect) => {
433 let term = ok!(Self::must_get_term(data));
434 if abs_rect.right() < term.right() {
435 abs_rect.inc_w(Dim(1));
436 }
437 }
438 _ => {}
439 }
440 }
441 // Move left line rightward, if possible.
442 RectSide::Left => {
443 match &mut win.view {
444 RectView::Float(abs_rect) => {
445 if abs_rect.width() > win.cfg.min_size.x {
446 abs_rect.inc_x(Dim(1));
447 abs_rect.dec_w(Dim(1));
448 }
449 }
450 _ => {}
451 }
452 }
453 _ => {}
454 }
455 }
456 _ => {}
457 }
458 return Ok(AppFlow::FullScreenRender);
459 }
460 Action::DragBorderLeft => {
461 let result = self.get_focal_window_mut();
462 let win = res!(result);
463 match win.state.lines.mode {
464 Some(BorderManagementMode::Adjust) => {
465 match win.state.lines.line {
466 // Move right line leftward, if possible.
467 RectSide::Right => {
468 match &mut win.view {
469 RectView::Float(abs_rect) => {
470 if abs_rect.width() > win.cfg.min_size.x {
471 abs_rect.dec_w(Dim(1));
472 }
473 }
474 _ => {}
475 }
476 }
477 // Move left line leftward, if possible.
478 RectSide::Left => {
479 match &mut win.view {
480 RectView::Float(abs_rect) => {
481 if abs_rect.left() > Dim(0) {
482 abs_rect.dec_x(Dim(1));
483 abs_rect.inc_w(Dim(1));
484 }
485 }
486 _ => {}
487 }
488 }
489 _ => {}
490 }
491 }
492 _ => {}
493 }
494 return Ok(AppFlow::FullScreenRender);
495 }
496 Action::ReturnToWindowManagementMode => {
497 let result = self.get_focal_window_mut();
498 let win = res!(result);
499 win.state.lines.mode = None;
500 res!(self.set_mode_update_menu(WindowMode::WindowManagement, None));
501 self.key_state.clear();
502 return Ok(AppFlow::FocalWindowRender);
503 }
504 // ┌─────────────────────────────┐
505 // │ INTERACTION MODE │
506 // └─────────────────────────────┘
507 Action::MoveToNextTab => {
508 let tab_string = self.cfg.tab_string.clone();
509 let result = self.get_focal_window_mut();
510 let win = res!(result);
511 if let Some(tbox) = win.get_focal_text_box_mut() {
512 match &mut tbox.tview.vtyp {
513 TextViewType::Editable(editor) => match editor.mode {
514 EditorMode::Navigation => {
515 if let TextBoxesState::Tabbed(ttmgr) = &mut win.state.text_boxes {
516 res!(ttmgr.next_focus());
517 }
518 }
519 EditorMode::Insert | EditorMode::Replace => {
520 let result = tbox.tview.atext.get_text_lines_mut();
521 let mut text_lines = res!(result);
522 text_lines.add_str(
523 &mut editor.nav.cursor,
524 tab_string.as_str(),
525 editor.mode == EditorMode::Replace,
526 );
527 }
528 _ => {}
529 }
530 _ => {
531 if let TextBoxesState::Tabbed(ttmgr) = &mut win.state.text_boxes {
532 res!(ttmgr.next_focus());
533 }
534 }
535 }
536 }
537 return Ok(AppFlow::FocalWindowRender);
538 }
539 Action::MoveCursorUp => {
540 let result = self.get_focal_window_text_box_mut();
541 if let Some(tbox) = res!(result) {
542 {
543 let result = tbox.tview.atext.get_text_lines_mut();
544 let mut text_lines = res!(result);
545 if let Some(highlighter) = text_lines.get_highlighter_mut() {
546 highlighter.dec_focus();
547 }
548 }
549 if tbox.cursor_up() {
550 return Ok(AppFlow::FocalWindowRender);
551 } else {
552 return Ok(AppFlow::CursorOnly);
553 }
554 } else {
555 return Ok(AppFlow::CursorOnly);
556 }
557 }
558 Action::MoveCursorDown => {
559 let result = self.get_focal_window_text_box_mut();
560 if let Some(tbox) = res!(result) {
561 {
562 let result = tbox.tview.atext.get_text_lines_mut();
563 let mut text_lines = res!(result);
564 if let Some(highlighter) = text_lines.get_highlighter_mut() {
565 highlighter.inc_focus();
566 }
567 }
568 if tbox.cursor_down() {
569 return Ok(AppFlow::FocalWindowRender);
570 } else {
571 return Ok(AppFlow::CursorOnly);
572 }
573 } else {
574 return Ok(AppFlow::CursorOnly);
575 }
576 }
577 Action::MoveCursorRight => {
578 let result = self.get_focal_window_text_box_mut();
579 if let Some(tbox) = res!(result) {
580 if tbox.cursor_right() {
581 return Ok(AppFlow::FocalWindowRender);
582 } else {
583 return Ok(AppFlow::CursorOnly);
584 }
585 } else {
586 return Ok(AppFlow::CursorOnly);
587 }
588 }
589 Action::MoveCursorLeft => {
590 let result = self.get_focal_window_text_box_mut();
591 if let Some(tbox) = res!(result) {
592 if tbox.cursor_left() {
593 return Ok(AppFlow::FocalWindowRender);
594 } else {
595 return Ok(AppFlow::CursorOnly);
596 }
597 } else {
598 return Ok(AppFlow::CursorOnly);
599 }
600 }
601 Action::PanTextViewUp => {
602 let result = self.get_focal_window_text_box_mut();
603 if let Some(tbox) = res!(result) {
604 tbox.pan_up(Dim(2));
605 }
606 return Ok(AppFlow::FocalWindowRender);
607 }
608 Action::PanTextViewDown => {
609 let result = self.get_focal_window_text_box_mut();
610 if let Some(tbox) = res!(result) {
611 tbox.pan_down(Dim(2));
612 }
613 return Ok(AppFlow::FocalWindowRender);
614 }
615 Action::PanTextViewRight => {
616 let result = self.get_focal_window_text_box_mut();
617 if let Some(tbox) = res!(result) {
618 tbox.pan_right(Dim(3));
619 }
620 return Ok(AppFlow::FocalWindowRender);
621 }
622 Action::PanTextViewLeft => {
623 let result = self.get_focal_window_text_box_mut();
624 if let Some(tbox) = res!(result) {
625 tbox.pan_left(Dim(3));
626 }
627 return Ok(AppFlow::FocalWindowRender);
628 }
629 Action::EnterEditorInsertMode => {
630 let result = self.get_focal_window_mut();
631 let win = res!(result);
632 if let Some(tbox) = win.get_focal_text_box_mut() {
633 if let Some(editor) = tbox.tview.vtyp.get_editor_mut() {
634 match editor.mode {
635 EditorMode::Navigation => editor.mode = EditorMode::Insert,
636 _ => {}
637 }
638 }
639 }
640 return Ok(AppFlow::FocalWindowRender);
641 }
642 Action::EnterEditorReplaceMode => {
643 let result = self.get_focal_window_mut();
644 let win = res!(result);
645 if let Some(tbox) = win.get_focal_text_box_mut() {
646 if let Some(editor) = tbox.tview.vtyp.get_editor_mut() {
647 match editor.mode {
648 EditorMode::Navigation => editor.mode = EditorMode::Replace,
649 _ => {}
650 }
651 }
652 }
653 return Ok(AppFlow::FocalWindowRender);
654 }
655 Action::EnterEditorModifyMode => {
656 let result = self.get_focal_window_mut();
657 let win = res!(result);
658 if let Some(tbox) = win.get_focal_text_box_mut() {
659 if let Some(editor) = tbox.tview.vtyp.get_editor_mut() {
660 match editor.mode {
661 EditorMode::Navigation => editor.mode = EditorMode::Modify,
662 _ => {}
663 }
664 }
665 }
666 return Ok(AppFlow::FocalWindowRender);
667 }
668 Action::ReturnToEditorNavigationMode => {
669 let result = self.get_focal_window_text_box_mut();
670 if let Some(tbox) = res!(result) {
671 match &mut tbox.tview.vtyp {
672 TextViewType::Editable(editor) => {
673 editor.mode = EditorMode::Navigation;
674 }
675 _ => {}
676 }
677 }
678 self.key_state.clear();
679 return Ok(AppFlow::FocalWindowRender);
680 }
681 Action::CreateNewTextTab => {
682 let id = self.get_focus_id().clone();
683 match self.add_text_box(
684 &id,
685 res!(self.style_lib.tabbed_text_box(ContentType::Text)),
686 ) {
687 Ok(()) => {}
688 Err(e) => error!(e),
689 }
690 return Ok(AppFlow::FocalWindowRender);
691 }
692 //_ => {}
693 }
694 //Ok(AppFlow::FocalWindowRender)
695 }
696}