diff options
Diffstat (limited to 'src/app')
25 files changed, 680 insertions, 332 deletions
diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs index c2747289..dc6ee1ab 100644 --- a/src/app/data_farmer.rs +++ b/src/app/data_farmer.rs @@ -224,15 +224,12 @@ impl DataCollection { } fn eat_temp(&mut self, temperature_sensors: Vec<temperature::TempHarvest>) { - // TODO: [PO] To implement self.temp_harvest = temperature_sensors.to_vec(); } fn eat_disks( &mut self, disks: Vec<disks::DiskHarvest>, io: disks::IoHarvest, harvested_time: Instant, ) { - // TODO: [PO] To implement - let time_since_last_harvest = harvested_time .duration_since(self.current_instant) .as_secs_f64(); diff --git a/src/app/data_harvester/processes.rs b/src/app/data_harvester/processes.rs index 3d8d7d5c..151d3a55 100644 --- a/src/app/data_harvester/processes.rs +++ b/src/app/data_harvester/processes.rs @@ -27,7 +27,6 @@ use std::borrow::Cow; use crate::Pid; -// TODO: Add value so we know if it's sorted ascending or descending by default? #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum ProcessSorting { CpuPercent, diff --git a/src/app/data_harvester/temperature.rs b/src/app/data_harvester/temperature.rs index 8f3b776e..3426a053 100644 --- a/src/app/data_harvester/temperature.rs +++ b/src/app/data_harvester/temperature.rs @@ -57,7 +57,6 @@ fn is_temp_filtered(filter: &Option<Filter>, text: &str) -> bool { fn temp_vec_sort(temperature_vec: &mut Vec<TempHarvest>) { // By default, sort temperature, then by alphabetically! - // TODO: [TEMPS] Allow users to control this. // Note we sort in reverse here; we want greater temps to be higher priority. temperature_vec.sort_by(|a, b| match a.temperature.partial_cmp(&b.temperature) { diff --git a/src/app/event.rs b/src/app/event.rs index 9201e2c0..f727050d 100644 --- a/src/app/event.rs +++ b/src/app/event.rs @@ -1,6 +1,5 @@ -use std::time::{Duration, Instant}; - -const MAX_TIMEOUT: Duration = Duration::from_millis(400); +pub mod multi_key; +pub use multi_key::*; /// These are "signals" that are sent along with an [`WidgetEventResult`] to signify a potential additional action /// that the caller must do, along with the "core" result of either drawing or redrawing. @@ -32,9 +31,9 @@ pub enum EventResult { /// The results of a widget handling some event, like a mouse or key event, /// signifying what the program should then do next. #[derive(Debug)] -pub enum WidgetEventResult { - /// Kill the program. - Quit, +pub enum ComponentEventResult { + /// The event isn't handled by the widget, and should be propagated to the parent. + Unhandled, /// Trigger a redraw. Redraw, /// Don't trigger a redraw. @@ -50,101 +49,3 @@ pub enum SelectionAction { /// This event occurs if the widget did not handle the selection action; the caller must handle it. NotHandled, } - -/// The states a [`MultiKey`] can be in. -enum MultiKeyState { - /// Currently not waiting on any next input. - Idle, - /// Waiting for the next input, with a given trigger [`Instant`]. - Waiting { - /// When it was triggered. - trigger_instant: Instant, - - /// What part of the pattern it is at. - checked_index: usize, - }, -} - -/// The possible outcomes of calling [`MultiKey::input`]. -pub enum MultiKeyResult { - /// Returned when a character was *accepted*, but has not completed the sequence required. - Accepted, - /// Returned when a character is accepted and completes the sequence. - Completed, - /// Returned if a character breaks the sequence or if it has already timed out. - Rejected, -} - -/// A struct useful for managing multi-key keybinds. -pub struct MultiKey { - state: MultiKeyState, - pattern: Vec<char>, -} - -impl MultiKey { - /// Creates a new [`MultiKey`] with a given pattern and timeout. - pub fn register(pattern: Vec<char>) -> Self { - Self { - state: MultiKeyState::Idle, - pattern, - } - } - - /// Resets to an idle state. - fn reset(&mut self) { - self.state = MultiKeyState::Idle; - } - - /// Handles a char input and returns the current status of the [`MultiKey`] after, which is one of: - /// - Accepting the char and moving to the next state - /// - Completing the multi-key pattern - /// - Rejecting it - /// - /// Note that if a [`MultiKey`] only "times out" upon calling this - if it has timed out, it will first reset - /// before trying to check the char. - pub fn input(&mut self, c: char) -> MultiKeyResult { - match &mut self.state { - MultiKeyState::Idle => { - if let Some(first) = self.pattern.first() { - if *first == c { - self.state = MultiKeyState::Waiting { - trigger_instant: Instant::now(), - checked_index: 0, - }; - - return MultiKeyResult::Accepted; - } - } - - MultiKeyResult::Rejected - } - MultiKeyState::Waiting { - trigger_instant, - checked_index, - } => { - if trigger_instant.elapsed() > MAX_TIMEOUT { - // Just reset and recursively call (putting it into Idle). - self.reset(); - self.input(c) - } else if let Some(next) = self.pattern.get(*checked_index + 1) { - if *next == c { - *checked_index += 1; - - if *checked_index == self.pattern.len() - 1 { - self.reset(); - MultiKeyResult::Completed - } else { - MultiKeyResult::Accepted - } - } else { - self.reset(); - MultiKeyResult::Rejected - } - } else { - self.reset(); - MultiKeyResult::Rejected - } - } - } - } -} diff --git a/src/app/event/multi_key.rs b/src/app/event/multi_key.rs new file mode 100644 index 00000000..1ab2c1b7 --- /dev/null +++ b/src/app/event/multi_key.rs @@ -0,0 +1,101 @@ +use std::time::{Duration, Instant}; + +const MAX_TIMEOUT: Duration = Duration::from_millis(400); + +/// The states a [`MultiKey`] can be in. +enum MultiKeyState { + /// Currently not waiting on any next input. + Idle, + /// Waiting for the next input, with a given trigger [`Instant`]. + Waiting { + /// When it was triggered. + trigger_instant: Instant, + + /// What part of the pattern it is at. + checked_index: usize, + }, +} + +/// The possible outcomes of calling [`MultiKey::input`]. +pub enum MultiKeyResult { + /// Returned when a character was *accepted*, but has not completed the sequence required. + Accepted, + /// Returned when a character is accepted and completes the sequence. + Completed, + /// Returned if a character breaks the sequence or if it has already timed out. + Rejected, +} + +/// A struct useful for managing multi-key keybinds. +pub struct MultiKey { + state: MultiKeyState, + pattern: Vec<char>, +} + +impl MultiKey { + /// Creates a new [`MultiKey`] with a given pattern and timeout. + pub fn register(pattern: Vec<char>) -> Self { + Self { + state: MultiKeyState::Idle, + pattern, + } + } + + /// Resets to an idle state. + fn reset(&mut self) { + self.state = MultiKeyState::Idle; + } + + /// Handles a char input and returns the current status of the [`MultiKey`] after, which is one of: + /// - Accepting the char and moving to the next state + /// - Completing the multi-key pattern + /// - Rejecting it + /// + /// Note that if a [`MultiKey`] only "times out" upon calling this - if it has timed out, it will first reset + /// before trying to check the char. + pub fn input(&mut self, c: char) -> MultiKeyResult { + match &mut self.state { + MultiKeyState::Idle => { + if let Some(first) = self.pattern.first() { + if *first == c { + self.state = MultiKeyState::Waiting { + trigger_instant: Instant::now(), + checked_index: 0, + }; + + return MultiKeyResult::Accepted; + } + } + + MultiKeyResult::Rejected + } + MultiKeyState::Waiting { + trigger_instant, + checked_index, + } => { + if trigger_instant.elapsed() > MAX_TIMEOUT { + // Just reset and recursively call (putting it into Idle). + self.reset(); + self.input(c) + } else if let Some(next) = self.pattern.get(*checked_index + 1) { + if *next == c { + *checked_index += 1; + + if *checked_index == self.pattern.len() - 1 { + self.reset(); + MultiKeyResult::Completed + } else { + MultiKeyResult::Accepted + } + } else { + self.reset(); + MultiKeyResult::Rejected + } + } else { + self.reset(); + MultiKeyResult::Rejected + } + } + } + } +} diff --git a/src/app/layout_manager.rs b/src/app/layout_manager.rs index 61d17924..9f2c287b 100644 --- a/src/app/layout_manager.rs +++ b/src/app/layout_manager.rs @@ -1511,7 +1511,6 @@ pub fn move_widget_selection( } LayoutNode::Widget(_) => { // Halt! - // TODO: How does this handle carousel? current_id } } diff --git a/src/app/widgets.rs b/src/app/widgets.rs index ffc08cd6..f49a3890 100644 --- a/src/app/widgets.rs +++ b/src/app/widgets.rs @@ -7,11 +7,10 @@ use tui::{backend::Backend, layout::Rect, widgets::TableState, Frame}; use crate::{ app::{ - event::{SelectionAction, WidgetEventResult}, + event::{ComponentEventResult, SelectionAction}, layout_manager::BottomWidgetType, }, canvas::Painter, - constants, options::layout_options::LayoutRule, }; @@ -20,12 +19,15 @@ mod tui_stuff; pub mod base; pub use base::*; +pub mod dialogs; +pub use dialogs::*; + pub mod bottom_widgets; pub use bottom_widgets::*; use self::tui_stuff::BlockBuilder; -use super::data_farmer::DataCollection; +use super::{data_farmer::DataCollection, event::EventResult}; /// A trait for things that are drawn with state. #[enum_dispatch] @@ -33,16 +35,16 @@ use super::data_farmer::DataCollection; pub trait Component { /// Handles a [`KeyEvent`]. /// - /// Defaults to returning [`EventResult::NoRedraw`], indicating nothing should be done. - fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult { - WidgetEventResult::NoRedraw + /// Defaults to returning [`ComponentEventResult::Unhandled`], indicating the component does not handle this event. + fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult { + ComponentEventResult::Unhandled } /// Handles a [`MouseEvent`]. /// - /// Defaults to returning [`EventResult::Continue`], indicating nothing should be done. - fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult { - WidgetEventResult::NoRedraw + /// Defaults to returning [`ComponentEventResult::Unhandled`], indicating the component does not handle this event. + fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult { + ComponentEventResult::Unhandled } /// Returns a [`Component`]'s bounding box. Note that these are defined in *global*, *absolute* @@ -180,6 +182,41 @@ pub enum TmpBottomWidget { Empty, } +/// The states a dialog can be in. Consists of either: +/// - [`DialogState::Hidden`] - the dialog is currently not showing. +/// - [`DialogState::Shown`] - the dialog is showing. +#[derive(Debug)] +pub enum DialogState<D: Default + Component> { + Hidden, + Shown(D), +} + +impl<D> Default for DialogState<D> +where + D: Default + Component, +{ + fn default() -> Self { + DialogState::Hidden + } +} + +impl<D> DialogState<D> +where + D: Default + Component, +{ + pub fn is_showing(&self) -> bool { + matches!(self, DialogState::Shown(_)) + } + + pub fn hide(&mut self) { + *self = DialogState::Hidden; + } + + pub fn show(&mut self) { + *self = DialogState::Shown(D::default()); + } +} + // ----- Old stuff below ----- #[derive(Debug)] @@ -250,7 +287,27 @@ impl Default for AppHelpDialogState { AppHelpDialogState { is_showing_help: false, scroll_state: ParagraphScrollState::default(), - index_shortcuts: vec![0; constants::HELP_TEXT.len()], + index_shortcuts: vec![], + } + } +} + +impl AppHelpDialogState { + pub fn increment(&mut self) -> EventResult { + if self.scroll_state.current_scroll_index < self.scroll_state.max_scroll_index { + self.scroll_state.current_scroll_index += 1; + EventResult::Redraw + } else { + EventResult::NoRedraw + } + } + + pub fn decrement(&mut self) -> EventResult { + if self.scroll_state.current_scroll_index > 0 { + self.scroll_state.current_scroll_index -= 1; + EventResult::Redraw + } else { + EventResult::NoRedraw } } } diff --git a/src/app/widgets/base/scrollable.rs b/src/app/widgets/base/scrollable.rs index ba4faa5f..b8652a9f 100644 --- a/src/app/widgets/base/scrollable.rs +++ b/src/app/widgets/base/scrollable.rs @@ -4,7 +4,7 @@ use crossterm::event::{KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEve use tui::{layout::Rect, widgets::TableState}; use crate::app::{ - event::{MultiKey, MultiKeyResult, WidgetEventResult}, + event::{ComponentEventResult, MultiKey, MultiKeyResult}, Component, }; @@ -138,54 +138,54 @@ impl Scrollable { } } - fn skip_to_first(&mut self) -> WidgetEventResult { + fn skip_to_first(&mut self) -> ComponentEventResult { if self.current_index != 0 { self.set_index(0); - WidgetEventResult::Redraw + ComponentEventResult::Redraw } else { - WidgetEventResult::NoRedraw + ComponentEventResult::NoRedraw } } - fn skip_to_last(&mut self) -> WidgetEventResult { + fn skip_to_last(&mut self) -> ComponentEventResult { let last_index = self.num_items - 1; if self.current_index != last_index { self.set_index(last_index); - WidgetEventResult::Redraw + ComponentEventResult::Redraw } else { - WidgetEventResult::NoRedraw + ComponentEventResult::NoRedraw } } /// Moves *downward* by *incrementing* the current index. - fn move_down(&mut self, change_by: usize) -> WidgetEventResult { + fn move_down(&mut self, change_by: usize) -> ComponentEventResult { if self.num_items == 0 { - return WidgetEventResult::NoRedraw; + return ComponentEventResult::NoRedraw; } let new_index = self.current_index + change_by; if new_index >= self.num_items || self.current_index == new_index { - WidgetEventResult::NoRedraw + ComponentEventResult::NoRedraw } else { self.set_index(new_index); - WidgetEventResult::Redraw + ComponentEventResult::Redraw } } /// Moves *upward* by *decrementing* the current index. - fn move_up(&mut self, change_by: usize) -> WidgetEventResult { + fn move_up(&mut self, change_by: usize) -> ComponentEventResult { if self.num_items == 0 { - return WidgetEventResult::NoRedraw; + return ComponentEventResult::NoRedraw; } let new_index = self.current_index.saturating_sub(change_by); if self.current_index == new_index { - WidgetEventResult::NoRedraw + ComponentEventResult::NoRedraw } else { self.set_index(new_index); - WidgetEventResult::Redraw + ComponentEventResult::Redraw } } @@ -207,7 +207,7 @@ impl Scrollable { } impl Component for Scrollable { - fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult { + fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult { use crossterm::event::KeyCode::{Char, Down, Up}; if event.modifiers == KeyModifiers::NONE || event.modifiers == KeyModifiers::SHIFT { @@ -218,18 +218,19 @@ impl Component for Scrollable { Char('k') => self.move_up(1), Char('g') => match self.gg_manager.input('g') { MultiKeyResult::Completed => self.skip_to_first(), - MultiKeyResult::Accepted => WidgetEventResult::NoRedraw, - MultiKeyResult::Rejected => WidgetEventResult::NoRedraw, + MultiKeyResult::Accepted | MultiKeyResult::Rejected => { + ComponentEventResult::NoRedraw + } }, Char('G') => self.skip_to_last(), - _ => WidgetEventResult::NoRedraw, + _ => ComponentEventResult::Unhandled, } } else { - WidgetEventResult::NoRedraw + ComponentEventResult::Unhandled } } - fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult { + fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult { match event.kind { MouseEventKind::Down(MouseButton::Left) => { if self.does_bounds_intersect_mouse(&event) { @@ -256,11 +257,11 @@ impl Component for Scrollable { } } - WidgetEventResult::NoRedraw + ComponentEventResult::NoRedraw } MouseEventKind::ScrollDown => self.move_down(1), MouseEventKind::ScrollUp => self.move_up(1), - _ => WidgetEventResult::NoRedraw, + _ => ComponentEventResult::Unhandled, } } diff --git a/src/app/widgets/base/sort_menu.rs b/src/app/widgets/base/sort_menu.rs index b893a418..548bbf77 100644 --- a/src/app/widgets/base/sort_menu.rs +++ b/src/app/widgets/base/sort_menu.rs @@ -3,7 +3,7 @@ use tui::{backend::Backend, layout::Rect, Frame}; use crate::{ app::{ - event::WidgetEventResult, text_table::SimpleColumn, widgets::tui_stuff::BlockBuilder, + event::ComponentEventResult, text_table::SimpleColumn, widgets::tui_stuff::BlockBuilder, Component, TextTable, }, canvas::Painter, @@ -69,11 +69,11 @@ impl Component for SortMenu { self.bounds = new_bounds; } - fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult { + fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult { self.table.handle_key_event(event) } - fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult { + fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult { self.table.handle_mouse_event(event) } } diff --git a/src/app/widgets/base/sort_text_table.rs b/src/app/widgets/base/sort_text_table.rs index 64766041..f1f3b0e4 100644 --- a/src/app/widgets/base/sort_text_table.rs +++ b/src/app/widgets/base/sort_text_table.rs @@ -5,7 +5,7 @@ use tui::{backend::Backend, layout::Rect, Frame}; use crate::{ app::{ - event::{ReturnSignal, WidgetEventResult}, + event::{ReturnSignal, ComponentEventResult}, widgets::tui_stuff::BlockBuilder, Component, TextTable, }, @@ -391,12 +391,12 @@ impl<S> Component for SortableTextTable<S> where S: SortableColumn, { - fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult { + fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult { for (index, column) in self.table.columns.iter().enumerate() { if let Some((shortcut, _)) = *column.shortcut() { if shortcut == event { self.set_sort_index(index); - return WidgetEventResult::Signal(ReturnSignal::Update); + return ComponentEventResult::Signal(ReturnSignal::Update); } } } @@ -404,10 +404,10 @@ where self.table.scrollable.handle_key_event(event) } - fn handle_mouse_event(&mut self, event: MouseEvent) -> WidgetEventResult { + fn handle_mouse_event(&mut self, event: MouseEvent) -> ComponentEventResult { if let MouseEventKind::Down(MouseButton::Left) = event.kind { if !self.does_bounds_intersect_mouse(&event) { - return WidgetEventResult::NoRedraw; + return ComponentEventResult::NoRedraw; } // Note these are representing RELATIVE coordinates! They *need* the above intersection check for validity! @@ -419,7 +419,7 @@ where if let Some((start, end)) = column.get_x_bounds() { if x >= start && x <= end { self.set_sort_index(index); - return WidgetEventResult::Signal(ReturnSignal::Update); + return ComponentEventResult::Signal(ReturnSignal::Update); } } } diff --git a/src/app/widgets/base/text_input.rs b/src/app/widgets/base/text_input.rs index f3480c0e..47a5cb7b 100644 --- a/src/app/widgets/base/text_input.rs +++ b/src/app/widgets/base/text_input.rs @@ -13,8 +13,8 @@ use unicode_width::UnicodeWidthStr; use crate::{ app::{ event::{ + ComponentEventResult::{self}, ReturnSignal, - WidgetEventResult::{self}, }, Component, }, @@ -85,19 +85,19 @@ impl TextInput { } } - fn clear_text(&mut self) -> WidgetEventResult { + fn clear_text(&mut self) -> ComponentEventResult { if self.text.is_empty() { - WidgetEventResult::NoRedraw + ComponentEventResult::NoRedraw } else { self.text = String::default(); self.cursor = GraphemeCursor::new(0, 0, true); self.window_index = Default::default(); self.cursor_direction = CursorDirection::Left; - WidgetEventResult::Signal(ReturnSignal::Update) + ComponentEventResult::Signal(ReturnSignal::Update) } } - fn move_word_forward(&mut self) -> WidgetEventResult { + fn move_word_forward(&mut self) -> ComponentEventResult { let current_index = self.cursor.cur_cursor(); if current_index < self.text.len() { @@ -105,30 +105,30 @@ impl TextInput { if index > 0 { self.cursor.set_cursor(index + current_index); self.cursor_direction = CursorDirection::Right; - return WidgetEventResult::Redraw; + return ComponentEventResult::Redraw; } } self.cursor.set_cursor(self.text.len()); } - WidgetEventResult::Redraw + ComponentEventResult::Redraw } - fn move_word_back(&mut self) -> WidgetEventResult { + fn move_word_back(&mut self) -> ComponentEventResult { let current_index = self.cursor.cur_cursor(); for (index, _word) in self.text[..current_index].unicode_word_indices().rev() { if index < current_index { self.cursor.set_cursor(index); self.cursor_direction = CursorDirection::Left; - return WidgetEventResult::Redraw; + return ComponentEventResult::Redraw; } } - WidgetEventResult::NoRedraw + ComponentEventResult::NoRedraw } - fn clear_word_from_cursor(&mut self) -> WidgetEventResult { + fn clear_word_from_cursor(&mut self) -> ComponentEventResult { // Fairly simple logic - create the word index iterator, skip the word that intersects with the current // cursor location, draw the rest, update the string. let current_index = self.cursor.cur_cursor(); @@ -147,16 +147,16 @@ impl TextInput { } if start_delete_index == current_index { - WidgetEventResult::NoRedraw + ComponentEventResult::NoRedraw } else { self.text.drain(start_delete_index..current_index); self.cursor = GraphemeCursor::new(start_delete_index, self.text.len(), true); self.cursor_direction = CursorDirection::Left; - WidgetEventResult::Signal(ReturnSignal::Update) + ComponentEventResult::Signal(ReturnSignal::Update) } } - fn clear_previous_grapheme(&mut self) -> WidgetEventResult { + fn clear_previous_grapheme(&mut self) -> ComponentEventResult { let current_index = self.cursor.cur_cursor(); if current_index > 0 { @@ -166,13 +166,13 @@ impl TextInput { self.cursor = GraphemeCursor::new(new_index, self.text.len(), true); self.cursor_direction = CursorDirection::Left; - WidgetEventResult::Signal(ReturnSignal::Update) + ComponentEventResult::Signal(ReturnSignal::Update) } else { - WidgetEventResult::NoRedraw + ComponentEventResult::NoRedraw } } - fn clear_current_grapheme(&mut self) -> WidgetEventResult { + fn clear_current_grapheme(&mut self) -> ComponentEventResult { let current_index = self.cursor.cur_cursor(); if current_index < self.text.len() { @@ -182,19 +182,19 @@ impl TextInput { self.cursor = GraphemeCursor::new(current_index, self.text.len(), true); self.cursor_direction = CursorDirection::Left; - WidgetEventResult::Signal(ReturnSignal::Update) + ComponentEventResult::Signal(ReturnSignal::Update) } else { - WidgetEventResult::NoRedraw + ComponentEventResult::NoRedraw } } - fn insert_character(&mut self, c: char) -> WidgetEventResult { + fn insert_character(&mut self, c: char) -> ComponentEventResult { let current_index = self.cursor.cur_cursor(); self.text.insert(current_index, c); self.cursor = GraphemeCursor::new(current_index, self.text.len(), true); self.move_forward(); - WidgetEventResult::Signal(ReturnSignal::Update) + ComponentEventResult::Signal(ReturnSignal::Update) } /// Updates the window indexes and returns the start index. @@ -296,29 +296,29 @@ impl Component for TextInput { self.bounds = new_bounds; } - fn handle_key_event(&mut self, event: KeyEvent) -> WidgetEventResult { + fn handle_key_event(&mut self, event: KeyEvent) -> ComponentEventResult { if event.modifiers.is_empty() { match event.code { KeyCode::Left => { let original_cursor = self.cursor.cur_cursor(); if self.move_back() == original_cursor { - WidgetEventResult::NoRedraw + ComponentEventResult::NoRedraw } else { - WidgetEventResult::Redraw + ComponentEventResult::Redraw } } KeyCode::Right => { let original_cursor = self.cursor.cur_cursor(); if self.move_forward() == original_cursor { - WidgetEventResult::NoRedraw + ComponentEventResult::NoRedraw } else { |