summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorClementTsang <cjhtsang@uwaterloo.ca>2021-09-22 01:16:33 -0400
committerClementTsang <cjhtsang@uwaterloo.ca>2021-09-22 01:16:33 -0400
commit7ee85a82f794c44b3a0bb6a50e8075eff96bb4c8 (patch)
tree35f2817649638873113b8b51c220adfee2454de1 /src
parente7b9c729123eda386f12ce90faaa413bf516fed3 (diff)
refactor: finish help menu
Diffstat (limited to 'src')
-rw-r--r--src/app.rs345
-rw-r--r--src/app/data_farmer.rs3
-rw-r--r--src/app/data_harvester/processes.rs1
-rw-r--r--src/app/data_harvester/temperature.rs1
-rw-r--r--src/app/event.rs109
-rw-r--r--src/app/event/multi_key.rs101
-rw-r--r--src/app/layout_manager.rs1
-rw-r--r--src/app/widgets.rs77
-rw-r--r--src/app/widgets/base/scrollable.rs47
-rw-r--r--src/app/widgets/base/sort_menu.rs6
-rw-r--r--src/app/widgets/base/sort_text_table.rs12
-rw-r--r--src/app/widgets/base/text_input.rs76
-rw-r--r--src/app/widgets/base/text_table.rs12
-rw-r--r--src/app/widgets/base/time_graph.rs42
-rw-r--r--src/app/widgets/bottom_widgets/basic_mem.rs11
-rw-r--r--src/app/widgets/bottom_widgets/battery.rs24
-rw-r--r--src/app/widgets/bottom_widgets/carousel.rs12
-rw-r--r--src/app/widgets/bottom_widgets/cpu.rs16
-rw-r--r--src/app/widgets/bottom_widgets/disk.rs8
-rw-r--r--src/app/widgets/bottom_widgets/mem.rs8
-rw-r--r--src/app/widgets/bottom_widgets/net.rs25
-rw-r--r--src/app/widgets/bottom_widgets/process.rs65
-rw-r--r--src/app/widgets/bottom_widgets/temp.rs11
-rw-r--r--src/app/widgets/dialogs.rs2
-rw-r--r--src/app/widgets/dialogs/help.rs304
-rw-r--r--src/app/widgets/tui_stuff/block_builder.rs38
-rw-r--r--src/bin/main.rs2
-rw-r--r--src/canvas.rs48
-rw-r--r--src/canvas/dialogs.rs3
-rw-r--r--src/canvas/dialogs/help_dialog.rs121
-rw-r--r--src/constants.rs296
-rw-r--r--src/lib.rs2
32 files changed, 980 insertions, 849 deletions
diff --git a/src/app.rs b/src/app.rs
index b3409d3f..3b95a536 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -13,7 +13,7 @@ use std::{
time::Instant,
};
-use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEventKind};
+use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent};
use fxhash::FxHashMap;
use indextree::{Arena, NodeId};
use unicode_segmentation::GraphemeCursor;
@@ -33,7 +33,7 @@ use crate::{
BottomEvent, Pid,
};
-use self::event::{EventResult, ReturnSignal, WidgetEventResult};
+use self::event::{ComponentEventResult, EventResult, ReturnSignal};
const MAX_SEARCH_LENGTH: usize = 200;
@@ -140,8 +140,6 @@ pub struct AppState {
// --- Eventually delete/rewrite ---
pub delete_dialog_state: AppDeleteDialogState,
- pub help_dialog_state: AppHelpDialogState,
-
// --- TO DELETE ---
pub cpu_state: CpuState,
pub mem_state: MemState,
@@ -172,6 +170,8 @@ pub struct AppState {
pub layout_tree: Arena<LayoutNode>,
pub layout_tree_root: NodeId,
frozen_state: FrozenState,
+
+ pub help_dialog: DialogState<HelpDialog>,
}
impl AppState {
@@ -204,7 +204,6 @@ impl AppState {
data_collection: Default::default(),
is_expanded: Default::default(),
delete_dialog_state: Default::default(),
- help_dialog_state: Default::default(),
cpu_state: Default::default(),
mem_state: Default::default(),
net_state: Default::default(),
@@ -222,6 +221,7 @@ impl AppState {
is_force_redraw: Default::default(),
is_determining_widget_boundary: Default::default(),
frozen_state: Default::default(),
+ help_dialog: Default::default(),
}
}
@@ -277,7 +277,7 @@ impl AppState {
let c = c.to_ascii_lowercase();
match c {
'q' => EventResult::Quit,
- 'e' => {
+ 'e' if !self.help_dialog.is_showing() => {
if self.app_config_fields.use_basic_mode {
EventResult::NoRedraw
} else {
@@ -285,11 +285,11 @@ impl AppState {
EventResult::Redraw
}
}
- '?' => {
- self.help_dialog_state.is_showing_help = true;
+ '?' if !self.help_dialog.is_showing() => {
+ self.help_dialog.show();
EventResult::Redraw
}
- 'f' => {
+ 'f' if !self.help_dialog.is_showing() => {
self.toggle_freeze();
if !self.is_frozen() {
let data_collection = &self.data_collection;
@@ -331,17 +331,59 @@ impl AppState {
}
}
+ /// Quick and dirty handler to convert [`ComponentEventResult`]s to [`EventResult`]s, and handle [`ReturnSignal`]s.
+ fn convert_widget_event_result(&mut self, w: ComponentEventResult) -> EventResult {
+ match w {
+ ComponentEventResult::Unhandled => EventResult::NoRedraw,
+ ComponentEventResult::Redraw => EventResult::Redraw,
+ ComponentEventResult::NoRedraw => EventResult::NoRedraw,
+ ComponentEventResult::Signal(signal) => match signal {
+ ReturnSignal::KillProcess => {
+ todo!()
+ }
+ ReturnSignal::Update => {
+ if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
+ match &self.frozen_state {
+ FrozenState::NotFrozen => {
+ widget.update_data(&self.data_collection);
+ }
+ FrozenState::Frozen(frozen_data) => {
+ widget.update_data(frozen_data);
+ }
+ }
+ }
+ EventResult::Redraw
+ }
+ },
+ }
+ }
+
+ /// Handles a [`KeyEvent`], and returns an [`EventResult`].
+ fn handle_key_event(&mut self, event: KeyEvent) -> EventResult {
+ let result = if let DialogState::Shown(help_dialog) = &mut self.help_dialog {
+ help_dialog.handle_key_event(event)
+ } else if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
+ widget.handle_key_event(event)
+ } else {
+ ComponentEventResult::Unhandled
+ };
+
+ match result {
+ ComponentEventResult::Unhandled => self.handle_global_key_event(event),
+ _ => self.convert_widget_event_result(result),
+ }
+ }
+
/// Handles a global [`KeyEvent`], and returns an [`EventResult`].
- fn handle_global_shortcut(&mut self, event: KeyEvent) -> EventResult {
+ fn handle_global_key_event(&mut self, event: KeyEvent) -> EventResult {
if event.modifiers.is_empty() {
match event.code {
KeyCode::Esc => {
if self.is_expanded {
self.is_expanded = false;
EventResult::Redraw
- } else if self.help_dialog_state.is_showing_help {
- self.help_dialog_state.is_showing_help = false;
- self.help_dialog_state.scroll_state.current_scroll_index = 0;
+ } else if self.help_dialog.is_showing() {
+ self.help_dialog.hide();
EventResult::Redraw
} else if self.delete_dialog_state.is_showing_dd {
self.close_dd();
@@ -350,8 +392,13 @@ impl AppState {
EventResult::NoRedraw
}
}
- KeyCode::Char(c) => self.handle_global_char(c),
- _ => EventResult::NoRedraw,
+ _ => {
+ if let KeyCode::Char(c) = event.code {
+ self.handle_global_char(c)
+ } else {
+ EventResult::NoRedraw
+ }
+ }
}
} else if let KeyModifiers::CONTROL = event.modifiers {
match event.code {
@@ -384,30 +431,55 @@ impl AppState {
}
}
- /// Quick and dirty handler to convert [`WidgetEventResult`]s to [`EventResult`]s, and handle [`ReturnSignal`]s.
- fn convert_widget_event_result(&mut self, w: WidgetEventResult) -> EventResult {
- match w {
- WidgetEventResult::Quit => EventResult::Quit,
- WidgetEventResult::Redraw => EventResult::Redraw,
- WidgetEventResult::NoRedraw => EventResult::NoRedraw,
- WidgetEventResult::Signal(signal) => match signal {
- ReturnSignal::KillProcess => {
- todo!()
- }
- ReturnSignal::Update => {
- if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
- match &self.frozen_state {
- FrozenState::NotFrozen => {
- widget.update_data(&self.data_collection);
- }
- FrozenState::Frozen(frozen_data) => {
- widget.update_data(frozen_data);
- }
+ /// Handles a [`MouseEvent`].
+ fn handle_mouse_event(&mut self, event: MouseEvent) -> EventResult {
+ if let DialogState::Shown(help_dialog) = &mut self.help_dialog {
+ let result = help_dialog.handle_mouse_event(event);
+ self.convert_widget_event_result(result)
+ } else if self.is_expanded {
+ if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
+ let result = widget.handle_mouse_event(event);
+ self.convert_widget_event_result(result)
+ } else {
+ EventResult::NoRedraw
+ }
+ } else {
+ let mut returned_result = EventResult::NoRedraw;
+ for (id, widget) in self.widget_lookup_map.iter_mut() {
+ if widget.does_border_intersect_mouse(&event) {
+ let result = widget.handle_mouse_event(event);
+
+ let new_id;
+ match widget.selectable_type() {
+ SelectableType::Selectable => {
+ new_id = *id;
+ }
+ SelectableType::Unselectable => {
+ let result = widget.handle_mouse_event(event);
+ return self.convert_widget_event_result(result);
+ }
+ SelectableType::Redirect(redirected_id) => {
+ new_id = redirected_id;
}
}
- EventResult::Redraw
+
+ let was_id_already_selected = self.selected_widget == new_id;
+ self.selected_widget = new_id;
+
+ if was_id_already_selected {
+ returned_result = self.convert_widget_event_result(result);
+ break;
+ } else {
+ // If the weren't equal, *force* a redraw, and correct the layout tree.
+ correct_layout_last_selections(&mut self.layout_tree, self.selected_widget);
+ let _ = self.convert_widget_event_result(result);
+ returned_result = EventResult::Redraw;
+ break;
+ }
}
- },
+ }
+
+ returned_result
}
}
@@ -415,83 +487,16 @@ impl AppState {
/// whether the app now requires a redraw.
pub fn handle_event(&mut self, event: BottomEvent) -> EventResult {
match event {
- BottomEvent::KeyInput(event) => {
- if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
- let result = widget.handle_key_event(event);
- match self.convert_widget_event_result(result) {
- EventResult::Quit => EventResult::Quit,
- EventResult::Redraw => EventResult::Redraw,
- EventResult::NoRedraw => self.handle_global_shortcut(event),
- }
- } else {
- self.handle_global_shortcut(event)
- }
- }
+ BottomEvent::KeyInput(event) => self.handle_key_event(event),
BottomEvent::MouseInput(event) => {
// Not great, but basically a blind lookup through the table for anything that clips the click location.
- // TODO: Would be cool to use a kd-tree or something like that in the future.
-
- match &event.kind {
- MouseEventKind::Down(MouseButton::Left) => {
- if self.is_expanded {
- if let Some(widget) =
- self.widget_lookup_map.get_mut(&self.selected_widget)
- {
- let result = widget.handle_mouse_event(event);
- return self.convert_widget_event_result(result);
- }
- } else {
- for (id, widget) in self.widget_lookup_map.iter_mut() {
- if widget.does_border_intersect_mouse(&event) {
- let result = widget.handle_mouse_event(event);
-
- let new_id;
- match widget.selectable_type() {
- SelectableType::Selectable => {
- new_id = *id;
- }
- SelectableType::Unselectable => {
- let result = widget.handle_mouse_event(event);
- return self.convert_widget_event_result(result);
- }
- SelectableType::Redirect(redirected_id) => {
- new_id = redirected_id;
- }
- }
-
- let was_id_already_selected = self.selected_widget == new_id;
- self.selected_widget = new_id;
-
- if was_id_already_selected {
- return self.convert_widget_event_result(result);
- } else {
- // If the weren't equal, *force* a redraw, and correct the layout tree.
- correct_layout_last_selections(
- &mut self.layout_tree,
- self.selected_widget,
- );
- let _ = self.convert_widget_event_result(result);
- return EventResult::Redraw;
- }
- }
- }
- }
- }
- MouseEventKind::ScrollDown | MouseEventKind::ScrollUp => {
- if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget)
- {
- let result = widget.handle_mouse_event(event);
- return self.convert_widget_event_result(result);
- }
- }
- _ => {}
- }
-
- EventResult::NoRedraw
+ self.handle_mouse_event(event)
}
BottomEvent::Update(new_data) => {
self.data_collection.eat_data(new_data);
+ // TODO: Optimization for dialogs; don't redraw here.
+
if !self.is_frozen() {
let data_collection = &self.data_collection;
self.widget_lookup_map
@@ -515,78 +520,6 @@ impl AppState {
}
}
- pub fn on_esc(&mut self) {
- self.reset_multi_tap_keys();
- if self.is_in_dialog() {
- if self.help_dialog_state.is_showing_help {
- self.help_dialog_state.is_showing_help = false;
- self.help_dialog_state.scroll_state.current_scroll_index = 0;
- } else {
- self.close_dd();
- }
-
- self.is_force_redraw = true;
- } else {
- match self.current_widget.widget_type {
- BottomWidgetType::Proc => {
- if let Some(current_proc_state) = self
- .proc_state
- .get_mut_widget_state(self.current_widget.widget_id)
- {
- if current_proc_state.is_search_enabled() || current_proc_state.is_sort_open
- {
- current_proc_state
- .process_search_state
- .search_state
- .is_enabled = false;
-
- current_proc_state.is_sort_open = false;
- self.is_force_redraw = true;
- return;
- }
- }
- }
- BottomWidgetType::ProcSearch => {
- if let Some(current_proc_state) = self
- .proc_state
- .get_mut_widget_state(self.current_widget.widget_id - 1)
- {
- if current_proc_state.is_search_enabled() {
- current_proc_state
- .process_search_state
- .search_state
- .is_enabled = false;
- self.move_widget_selection(&WidgetDirection::Up);
- self.is_force_redraw = true;
- return;
- }
- }
- }
- BottomWidgetType::ProcSort => {
- if let Some(current_proc_state) = self
- .proc_state
- .get_mut_widget_state(self.current_widget.widget_id - 2)
- {
- if current_proc_state.is_sort_open {
- current_proc_state.columns.current_scroll_position =
- current_proc_state.columns.backup_prev_scroll_position;
- current_proc_state.is_sort_open = false;
- self.move_widget_selection(&WidgetDirection::Right);
- self.is_force_redraw = true;
- return;
- }
- }
- }
- _ => {}
- }
-
- if self.is_expanded {
- self.is_expanded = false;
- self.is_force_redraw = true;
- }
- }
- }
-
pub fn is_in_search_widget(&self) -> bool {
matches!(
self.current_widget.widget_type,
@@ -600,7 +533,7 @@ impl AppState {
}
fn is_in_dialog(&self) -> bool {
- self.help_dialog_state.is_showing_help || self.delete_dialog_state.is_showing_dd
+ self.delete_dialog_state.is_showing_dd
}
fn ignore_normal_keybinds(&self) -> bool {
@@ -1097,7 +1030,7 @@ impl AppState {
pub fn on_up_key(&mut self) {
if !self.is_in_dialog() {
self.decrement_position_count();
- } else if self.help_dialog_state.is_showing_help {
+ } else if self.help_dialog.is_showing() {
self.help_scroll_up();
} else if self.delete_dialog_state.is_showing_dd {
#[cfg(target_os = "windows")]
@@ -1118,7 +1051,7 @@ impl AppState {
pub fn on_down_key(&mut self) {
if !self.is_in_dialog() {
self.increment_position_count();
- } else if self.help_dialog_state.is_showing_help {
+ } else if self.help_dialog.is_showing() {
self.help_scroll_down();
} else if self.delete_dialog_state.is_showing_dd {
#[cfg(target_os = "windows")]
@@ -1596,19 +1529,9 @@ impl AppState {
}
}
self.handle_char(caught_char);
- } else if self.help_dialog_state.is_showing_help {
+ } else if self.help_dialog.is_showing() {
match caught_char {
- '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
- let potential_index = caught_char.to_digit(10);
- if let Some(potential_index) = potential_index {
- if (potential_index as usize) < self.help_dialog_state.index_shortcuts.len()
- {
- self.help_scroll_to_or_max(
- self.help_dialog_state.index_shortcuts[potential_index as usize],
- );
- }
- }
- }
+ '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {}
'j' | 'k' | 'g' | 'G' => self.handle_char(caught_char),
_ => {}
}
@@ -1792,7 +1715,7 @@ impl AppState {
}
}
'?' => {
- self.help_dialog_state.is_showing_help = true;
+ self.help_dialog.show();
self.is_force_redraw = true;
}
'H' | 'A' => self.move_widget_selection(&WidgetDirection::Left),
@@ -1863,7 +1786,6 @@ impl AppState {
}
fn expand_widget(&mut self) {
- // TODO: [BASIC] Expansion in basic mode.
if !self.ignore_normal_keybinds() && !self.app_config_fields.use_basic_mode {
// Pop-out mode. We ignore if in process search.
@@ -2256,7 +2178,7 @@ impl AppState {
{
if proc_widget_state.is_sort_open {
if let Some(proc_sort_widget) = self.widget_map.get(&new_widget_id) {
- self.current_widget = proc_sort_widget.clone(); // TODO: Could I remove this clone w/ static references?
+ self.current_widget = proc_sort_widget.clone();
}
}
}
@@ -2378,8 +2300,8 @@ impl AppState {
_ => {}
}
self.reset_multi_tap_keys();
- } else if self.help_dialog_state.is_showing_help {
- self.help_dialog_state.scroll_state.current_scroll_index = 0;
+ } else if self.help_dialog.is_showing() {
+ // self.help_dialog_state.scroll_state.current_scroll_index = 0;
} else if self.delete_dialog_state.is_showing_dd {
self.delete_dialog_state.selected_signal = KillSignal::Cancel;
}
@@ -2517,26 +2439,17 @@ impl AppState {
}
fn help_scroll_up(&mut self) {
- if self.help_dialog_state.scroll_state.current_scroll_index > 0 {
- self.help_dialog_state.scroll_state.current_scroll_index -= 1;
- }
+ // if self.help_dialog_state.scroll_state.current_scroll_index > 0 {
+ // self.help_dialog_state.scroll_state.current_scroll_index -= 1;
+ // }
}
fn help_scroll_down(&mut self) {
- if self.help_dialog_state.scroll_state.current_scroll_index + 1
- < self.help_dialog_state.scroll_state.max_scroll_index
- {
- self.help_dialog_state.scroll_state.current_scroll_index += 1;
- }
- }
-
- fn help_scroll_to_or_max(&mut self, new_position: u16) {
- if new_position < self.help_dialog_state.scroll_state.max_scroll_index {
- self.help_dialog_state.scroll_state.current_scroll_index = new_position;
- } else {
- self.help_dialog_state.scroll_state.current_scroll_index =
- self.help_dialog_state.scroll_state.max_scroll_index - 1;
- }
+ // if self.help_dialog_state.scroll_state.current_scroll_index + 1
+ // < self.help_dialog_state.scroll_state.max_scroll_index
+ // {
+ // self.help_dialog_state.scroll_state.current_scroll_index += 1;
+ // }
}
fn on_plus(&mut self) {
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
- }