summaryrefslogtreecommitdiffstats
path: root/src/app.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/app.rs')
-rw-r--r--src/app.rs3495
1 files changed, 527 insertions, 2968 deletions
diff --git a/src/app.rs b/src/app.rs
index b6bca61d..d81b88fa 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,39 +1,27 @@
-use std::{
- cmp::{max, min},
- collections::HashMap,
- // io::Write,
- path::PathBuf,
- time::Instant,
-};
-
-use unicode_segmentation::GraphemeCursor;
-use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
-
-use typed_builder::*;
-
-use data_farmer::*;
-use data_harvester::{processes, temperature};
-use layout_manager::*;
-pub use states::*;
-
-use crate::{
- canvas, constants,
- options::Config,
- options::ConfigFlags,
- options::WidgetIdEnabled,
- units::data_units::DataUnit,
- utils::error::{BottomError, Result},
- Pid,
-};
-
pub mod data_farmer;
pub mod data_harvester;
+pub mod event;
+pub mod filter;
pub mod layout_manager;
mod process_killer;
pub mod query;
-pub mod states;
+pub mod widgets;
-const MAX_SEARCH_LENGTH: usize = 200;
+use std::time::Instant;
+
+use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent};
+use fxhash::FxHashMap;
+use indextree::{Arena, NodeId};
+
+pub use data_farmer::*;
+use data_harvester::temperature;
+pub use filter::*;
+use layout_manager::*;
+pub use widgets::*;
+
+use crate::{constants, units::data_units::DataUnit, utils::error::Result, BottomEvent, Pid};
+
+use self::event::{ComponentEventResult, EventResult, ReturnSignal};
#[derive(Debug, Clone)]
pub enum AxisScaling {
@@ -41,6 +29,46 @@ pub enum AxisScaling {
Linear,
}
+#[derive(Clone, Default, Debug)]
+pub struct UsedWidgets {
+ pub use_cpu: bool,
+ pub use_mem: bool,
+ pub use_net: bool,
+ pub use_proc: bool,
+ pub use_disk: bool,
+ pub use_temp: bool,
+ pub use_battery: bool,
+}
+
+impl UsedWidgets {
+ pub fn add(&mut self, widget_type: &BottomWidgetType) {
+ match widget_type {
+ BottomWidgetType::Cpu | BottomWidgetType::BasicCpu => {
+ self.use_cpu = true;
+ }
+ BottomWidgetType::Mem | BottomWidgetType::BasicMem => {
+ self.use_mem = true;
+ }
+ BottomWidgetType::Net | BottomWidgetType::BasicNet => {
+ self.use_net = true;
+ }
+ BottomWidgetType::Proc => {
+ self.use_proc = true;
+ }
+ BottomWidgetType::Temp => {
+ self.use_temp = true;
+ }
+ BottomWidgetType::Disk => {
+ self.use_disk = true;
+ }
+ BottomWidgetType::Battery => {
+ self.use_battery = true;
+ }
+ _ => {}
+ }
+ }
+}
+
/// AppConfigFields is meant to cover basic fields that would normally be set
/// by config files or launch options.
#[derive(Debug)]
@@ -57,139 +85,114 @@ pub struct AppConfigFields {
pub hide_time: bool,
pub autohide_time: bool,
pub use_old_network_legend: bool,
- pub table_gap: u16,
+ pub table_gap: bool,
pub disable_click: bool,
pub no_write: bool,
pub show_table_scroll_position: bool,
pub is_advanced_kill: bool,
- // TODO: Remove these, move network details state-side.
pub network_unit_type: DataUnit,
pub network_scale_type: AxisScaling,
pub network_use_binary_prefix: bool,
}
-/// For filtering out information
-#[derive(Debug, Clone)]
-pub struct DataFilters {
- pub disk_filter: Option<Filter>,
- pub mount_filter: Option<Filter>,
- pub temp_filter: Option<Filter>,
- pub net_filter: Option<Filter>,
+/// The [`FrozenState`] indicates whether the application state should be frozen; if it is, save a snapshot of
+/// the data collected at that instant.
+pub enum FrozenState {
+ NotFrozen,
+ Frozen(Box<DataCollection>),
}
-#[derive(Debug, Clone)]
-pub struct Filter {
- pub is_list_ignored: bool,
- pub list: Vec<regex::Regex>,
+impl Default for FrozenState {
+ fn default() -> Self {
+ Self::NotFrozen
+ }
}
-#[derive(TypedBuilder)]
-pub struct App {
- #[builder(default = false, setter(skip))]
- awaiting_second_char: bool,
-
- #[builder(default, setter(skip))]
- second_char: Option<char>,
-
- #[builder(default, setter(skip))]
+pub struct AppState {
pub dd_err: Option<String>,
- #[builder(default, setter(skip))]
to_delete_process_list: Option<(String, Vec<Pid>)>,
- #[builder(default = false, setter(skip))]
- pub is_frozen: bool,
-
- #[builder(default = Instant::now(), setter(skip))]
- last_key_press: Instant,
-
- #[builder(default, setter(skip))]
- pub canvas_data: canvas::DisplayableData,
-
- #[builder(default, setter(skip))]
pub data_collection: DataCollection,
- #[builder(default, setter(skip))]
- pub delete_dialog_state: AppDeleteDialogState,
-
- #[builder(default, setter(skip))]
- pub help_dialog_state: AppHelpDialogState,
-
- #[builder(default = false, setter(skip))]
pub is_expanded: bool,
- #[builder(default = false, setter(skip))]
+ pub used_widgets: UsedWidgets,
+ pub filters: DataFilters,
+ pub app_config_fields: AppConfigFields,
+
+ // --- FIXME: TO DELETE/REWRITE ---
+ pub delete_dialog_state: AppDeleteDialogState,
pub is_force_redraw: bool,
- #[builder(default = false, setter(skip))]
pub is_determining_widget_boundary: bool,
- #[builder(default = false, setter(skip))]
- pub basic_mode_use_percent: bool,
-
- #[builder(default = false, setter(skip))]
- pub is_config_open: bool,
-
- #[builder(default = false, setter(skip))]
- pub did_config_fail_to_save: bool,
-
- #[cfg(target_family = "unix")]
- #[builder(default, setter(skip))]
- pub user_table: processes::UserTable,
+ // --- NEW STUFF ---
+ pub selected_widget: NodeId,
+ pub widget_lookup_map: FxHashMap<NodeId, TmpBottomWidget>,
+ pub layout_tree: Arena<LayoutNode>,
+ pub layout_tree_root: NodeId,
+ frozen_state: FrozenState,
- pub cpu_state: CpuState,
- pub mem_state: MemState,
- pub net_state: NetState,
- pub proc_state: ProcState,
- pub temp_state: TempState,
- pub disk_state: DiskState,
- pub battery_state: BatteryState,
- pub basic_table_widget_state: Option<BasicTableWidgetState>,
- pub app_config_fields: AppConfigFields,
- pub widget_map: HashMap<u64, BottomWidget>,
- pub current_widget: BottomWidget,
- pub used_widgets: UsedWidgets,
- pub filters: DataFilters,
- pub config: Config,
- pub config_path: Option<PathBuf>,
+ pub help_dialog: DialogState<HelpDialog>,
}
-#[cfg(target_os = "windows")]
-const MAX_SIGNAL: usize = 1;
-#[cfg(target_os = "linux")]
-const MAX_SIGNAL: usize = 64;
-#[cfg(target_os = "macos")]
-const MAX_SIGNAL: usize = 31;
+impl AppState {
+ /// Creates a new [`AppState`].
+ pub fn new(
+ app_config_fields: AppConfigFields, filters: DataFilters,
+ layout_tree_output: LayoutCreationOutput,
+ ) -> Self {
+ let LayoutCreationOutput {
+ layout_tree,
+ root: layout_tree_root,
+ widget_lookup_map,
+ selected: selected_widget,
+ used_widgets,
+ } = layout_tree_output;
+
+ Self {
+ app_config_fields,
+ filters,
+ used_widgets,
+ selected_widget,
+ widget_lookup_map,
+ layout_tree,
+ layout_tree_root,
+
+ // Use defaults.
+ dd_err: Default::default(),
+ to_delete_process_list: Default::default(),
+ data_collection: Default::default(),
+ is_expanded: Default::default(),
+ delete_dialog_state: Default::default(),
+ is_force_redraw: Default::default(),
+ is_determining_widget_boundary: Default::default(),
+ frozen_state: Default::default(),
+ help_dialog: Default::default(),
+ }
+ }
+
+ pub fn is_frozen(&self) -> bool {
+ matches!(self.frozen_state, FrozenState::Frozen(_))
+ }
+
+ pub fn toggle_freeze(&mut self) {
+ if matches!(self.frozen_state, FrozenState::Frozen(_)) {
+ self.frozen_state = FrozenState::NotFrozen;
+ } else {
+ self.frozen_state = FrozenState::Frozen(Box::new(self.data_collection.clone()));
+ }
+ }
-impl App {
pub fn reset(&mut self) {
- // Reset multi
- self.reset_multi_tap_keys();
-
- // Reset dialog state
- self.help_dialog_state.is_showing_help = false;
- self.delete_dialog_state.is_showing_dd = false;
-
- // Close all searches and reset it
- self.proc_state
- .widget_states
+ // Call reset on all widgets.
+ self.widget_lookup_map
.values_mut()
- .for_each(|state| {
- state.process_search_state.search_state.reset();
- });
- self.proc_state.force_update_all = true;
-
- // Clear current delete list
- self.to_delete_process_list = None;
- self.dd_err = None;
+ .for_each(|widget| widget.reset());
// Unfreeze.
- self.is_frozen = false;
-
- // Reset zoom
- self.reset_cpu_zoom();
- self.reset_mem_zoom();
- self.reset_net_zoom();
+ self.frozen_state = FrozenState::NotFrozen;
// Reset data
self.data_collection.reset();
@@ -207,674 +210,269 @@ impl App {
self.dd_err = None;
}
- 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 if self.is_config_open {
- self.close_config_screen();
+ /// Handles a global event involving a char.
+ fn handle_global_char(&mut self, c: char) -> EventResult {
+ if c.is_ascii_control() {
+ EventResult::NoRedraw
} 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;
+ // Check for case-sensitive bindings first.
+ match c {
+ 'H' | 'A' => self.move_to_widget(MovementDirection::Left),
+ 'L' | 'D' => self.move_to_widget(MovementDirection::Right),
+ 'K' | 'W' => self.move_to_widget(MovementDirection::Up),
+ 'J' | 'S' => self.move_to_widget(MovementDirection::Down),
+ _ => {
+ let c = c.to_ascii_lowercase();
+ match c {
+ 'q' => EventResult::Quit,
+ 'e' if !self.help_dialog.is_showing() => {
+ if self.app_config_fields.use_basic_mode {
+ EventResult::NoRedraw
+ } else {
+ self.is_expanded = !self.is_expanded;
+ EventResult::Redraw
+ }
}
- }
- }
- _ => {}
- }
-
- 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,
- BottomWidgetType::ProcSearch
- )
- }
-
- fn reset_multi_tap_keys(&mut self) {
- self.awaiting_second_char = false;
- self.second_char = None;
- }
-
- fn is_in_dialog(&self) -> bool {
- self.help_dialog_state.is_showing_help || self.delete_dialog_state.is_showing_dd
- }
-
- fn ignore_normal_keybinds(&self) -> bool {
- self.is_config_open || self.is_in_dialog()
- }
-
- pub fn on_tab(&mut self) {
- // Allow usage whilst only in processes
-
- if !self.ignore_normal_keybinds() {
- match self.current_widget.widget_type {
- BottomWidgetType::Cpu => {
- if let Some(cpu_widget_state) = self
- .cpu_state
- .get_mut_widget_state(self.current_widget.widget_id)
- {
- cpu_widget_state.is_multi_graph_mode =
- !cpu_widget_state.is_multi_graph_mode;
- }
- }
- BottomWidgetType::Proc => {
- if let Some(proc_widget_state) = self
- .proc_state
- .get_mut_widget_state(self.current_widget.widget_id)
- {
- // Do NOT allow when in tree mode!
- if !proc_widget_state.is_tree_mode {
- // Toggles process widget grouping state
- proc_widget_state.is_grouped = !(proc_widget_state.is_grouped);
-
- // Forcefully switch off column if we were on it...
- if (proc_widget_state.is_grouped
- && (proc_widget_state.process_sorting_type
- == processes::ProcessSorting::Pid
- || proc_widget_state.process_sorting_type
- == processes::ProcessSorting::User
- || proc_widget_state.process_sorting_type
- == processes::ProcessSorting::State))
- || (!proc_widget_state.is_grouped
- && proc_widget_state.process_sorting_type
- == processes::ProcessSorting::Count)
- {
- proc_widget_state.process_sorting_type =
- processes::ProcessSorting::CpuPercent; // Go back to default, negate PID for group
- proc_widget_state.is_process_sort_descending = true;
+ '?' if !self.help_dialog.is_showing() => {
+ self.help_dialog.show();
+ EventResult::Redraw
+ }
+ 'f' if !self.help_dialog.is_showing() => {
+ self.toggle_freeze();
+ if !self.is_frozen() {
+ let data_collection = &self.data_collection;
+ self.widget_lookup_map
+ .iter_mut()
+ .for_each(|(_id, widget)| widget.update_data(data_collection));
}
-
- proc_widget_state.columns.set_to_sorted_index_from_type(
- &proc_widget_state.process_sorting_type,
- );
-
- proc_widget_state.columns.try_set(
- &processes::ProcessSorting::State,
- !(proc_widget_state.is_grouped),
- );
-
- #[cfg(target_family = "unix")]
- proc_widget_state.columns.try_set(
- &processes::ProcessSorting::User,
- !(proc_widget_state.is_grouped),
- );
-
- proc_widget_state
- .columns
- .toggle(&processes::ProcessSorting::Count);
- proc_widget_state
- .columns
- .toggle(&processes::ProcessSorting::Pid);
-
- proc_widget_state.requires_redraw = true;
- self.proc_state.force_update = Some(self.current_widget.widget_id);
+ EventResult::Redraw
}
+ _ => EventResult::NoRedraw,
}
}
- _ => {}
}
}
}
- /// I don't like this, but removing it causes a bunch of breakage.
- /// Use ``proc_widget_state.is_grouped`` if possible!
- pub fn is_grouped(&self, widget_id: u64) -> bool {
- if let Some(proc_widget_state) = self.proc_state.widget_states.get(&widget_id) {
- proc_widget_state.is_grouped
+ /// Moves to a widget.
+ fn move_to_widget(&mut self, direction: MovementDirection) -> EventResult {
+ match if self.is_expanded {
+ move_expanded_widget_selection(
+ &mut self.widget_lookup_map,
+ self.selected_widget,
+ direction,
+ )
} else {
- false
- }
- }
-
- pub fn on_slash(&mut self) {
- if !self.ignore_normal_keybinds() {
- match &self.current_widget.widget_type {
- BottomWidgetType::Proc | BottomWidgetType::ProcSort => {
- // Toggle on
- if let Some(proc_widget_state) = self.proc_state.get_mut_widget_state(
- self.current_widget.widget_id
- - match &self.current_widget.widget_type {
- BottomWidgetType::ProcSort => 2,
- _ => 0,
- },
- ) {
- proc_widget_state
- .process_search_state
- .search_state
- .is_enabled = true;
- self.move_widget_selection(&WidgetDirection::Down);
- self.is_force_redraw = true;
- }
+ let layout_tree = &mut self.layout_tree;
+
+ move_widget_selection(
+ layout_tree,
+ &mut self.widget_lookup_map,
+ self.selected_widget,
+ direction,
+ )
+ } {
+ MoveWidgetResult::ForceRedraw(new_widget_id) => {
+ self.selected_widget = new_widget_id;
+ EventResult::Redraw
+ }
+ MoveWidgetResult::NodeId(new_widget_id) => {
+ let previous_selected = self.selected_widget;
+ self.selected_widget = new_widget_id;
+
+ if previous_selected != self.selected_widget {
+ EventResult::Redraw
+ } else {
+ EventResult::NoRedraw
}
- _ => {}
}
}
}
- pub fn toggle_sort(&mut self) {
- match &self.current_widget.widget_type {
- BottomWidgetType::Proc | BottomWidgetType::ProcSort => {
- let widget_id = self.current_widget.widget_id
- - match &self.current_widget.widget_type {
- BottomWidgetType::Proc => 0,
- BottomWidgetType::ProcSort => 2,
- _ => 0,
- };
-
- if let Some(proc_widget_state) = self.proc_state.get_mut_widget_state(widget_id) {
- // Open up sorting dialog for that specific proc widget.
- // TODO: It might be a decent idea to allow sorting ALL? I dunno.
-
- proc_widget_state.is_sort_open = !proc_widget_state.is_sort_open;
- if proc_widget_state.is_sort_open {
- // If it just opened, move left
- proc_widget_state
- .columns
- .set_to_sorted_index_from_type(&proc_widget_state.process_sorting_type);
- self.move_widget_selection(&WidgetDirection::Left);
- } else {
- // Otherwise, move right if currently on the sort widget
- if let BottomWidgetType::ProcSort = self.current_widget.widget_type {
- self.move_widget_selection(&WidgetDirection::Right);
+ /// 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
}
-
- self.is_force_redraw = true;
- }
- _ => {}
+ },
}
}
- pub fn invert_sort(&mut self) {
- match &self.current_widget.widget_type {
- BottomWidgetType::Proc | BottomWidgetType::ProcSort => {
- let widget_id = self.current_widget.widget_id
- - match &self.current_widget.widget_type {
- BottomWidgetType::Proc => 0,
- BottomWidgetType::ProcSort => 2,
- _ => 0,
- };
-
- if let Some(proc_widget_state) = self.proc_state.get_mut_widget_state(widget_id) {
- proc_widget_state.is_process_sort_descending =
- !proc_widget_state.is_process_sort_descending;
-
- self.proc_state.force_update = Some(widget_id);
+ /// 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_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.is_showing() {
+ self.help_dialog.hide();
+ EventResult::Redraw
+ } else if self.delete_dialog_state.is_showing_dd {
+ self.close_dd();
+ EventResult::Redraw
+ } else {
+ 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 {
+ KeyCode::Char('c') | KeyCode::Char('C') => EventResult::Quit,
+ KeyCode::Char('r') | KeyCode::Char('R') => {
+ self.reset();
+ let data_collection = &self.data_collection;
+ self.widget_lookup_map
+ .iter_mut()
+ .for_each(|(_id, widget)| widget.update_data(data_collection));
+ EventResult::Redraw
+ }
+ KeyCode::Left => self.move_to_widget(MovementDirection::Left),
+ KeyCode::Right => self.move_to_widget(MovementDirection::Right),
+ KeyCode::Up => self.move_to_widget(MovementDirection::Up),
+ KeyCode::Down => self.move_to_widget(MovementDirection::Down),
+ _ => EventResult::NoRedraw,
+ }
+ } else if let KeyModifiers::SHIFT = event.modifiers {
+ match event.code {
+ KeyCode::Left => self.move_to_widget(MovementDirection::Left),
+ KeyCode::Right => self.move_to_widget(MovementDirection::Right),
+ KeyCode::Up => self.move_to_widget(MovementDirection::Up),
+ KeyCode::Down => self.move_to_widget(MovementDirection::Down),
+ KeyCode::Char(c) => self.handle_global_char(c),
+ _ => EventResult::NoRedraw,
}
- _ => {}
+ } else {
+ EventResult::NoRedraw
}
}
- pub fn toggle_percentages(&mut self) {
- match &self.current_widget.widget_type {
- BottomWidgetType::BasicMem => {
- self.basic_mode_use_percent = !self.basic_mode_use_percent; // Oh god this is so lazy.
+ /// 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
}
- BottomWidgetType::Proc => {
- if let Some(proc_widget_state) = self
- .proc_state
- .widget_states
- .get_mut(&self.current_widget.widget_id)
- {
- proc_widget_state
- .columns
- .toggle(&processes::ProcessSorting::Mem);
- if let Some(mem_percent_state) = proc_widget_state
- .columns
- .toggle(&processes::ProcessSorting::MemPercent)
- {
- if proc_widget_state.process_sorting_type
- == processes::ProcessSorting::MemPercent
- || proc_widget_state.process_sorting_type
- == processes::ProcessSorting::Mem
- {
- if mem_percent_state {
- proc_widget_state.process_sorting_type =
- processes::ProcessSorting::MemPercent;
+ } 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);
+ match widget.selectable_type() {
+ SelectableType::Selectable => {
+ let was_id_already_selected = self.selected_widget == *id;
+ self.selected_widget = *id;
+
+ if was_id_already_selected {
+ returned_result = self.convert_widget_event_result(result);
} else {
- proc_widget_state.process_sorting_type =
- processes::ProcessSorting::Mem;
+ // 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;
+ }
+ SelectableType::Unselectable => {
+ let result = widget.handle_mouse_event(event);
+ return self.convert_widget_event_result(result);
}
}
-
- proc_widget_state.requires_redraw = true;
- self.proc_state.force_update = Some(self.current_widget.widget_id);
}
}
- _ => {}
- }
- }
-
- pub fn toggle_ignore_case(&mut self) {
- let is_in_search_widget = self.is_in_search_widget();
- let mut is_case_sensitive: Option<bool> = None;
- if let Some(proc_widget_state) = self
- .proc_state
- .widget_states
- .get_mut(&(self.current_widget.widget_id - 1))
- {
- if is_in_search_widget && proc_widget_state.is_search_enabled() {
- proc_widget_state
- .process_search_state
- .search_toggle_ignore_case();
- proc_widget_state.update_query();
- self.proc_state.force_update = Some(self.current_widget.widget_id - 1);
- // Remember, it's the opposite (ignoring case is case "in"sensitive)
- is_case_sensitive = Some(!proc_widget_state.process_search_state.is_ignoring_case);
- }
- }
-
- // Also toggle it in the config file if we actually changed it.
- if let Some(is_ignoring_case) = is_case_sensitive {
- if let Some(flags) = &mut self.config.flags {
- if let Some(map) = &mut flags.search_case_enabled_widgets_map {
- // Just update the map.
- let mapping = map.entry(self.current_widget.widget_id - 1).or_default();
- *mapping = is_ignoring_case;
-
- flags.search_case_enabled_widgets =
- Some(WidgetIdEnabled::create_from_hashmap(map));
- } else {
- // Map doesn't exist yet... initialize ourselves.
- let mut map = HashMap::default();
- map.insert(self.current_widget.widget_id - 1, is_ignoring_case);
- flags.search_case_enabled_widgets =
- Some(WidgetIdEnabled::create_from_hashmap(&map));
- flags.search_case_enabled_widgets_map = Some(map);
- }
- } else {
- // Must initialize it ourselves...
- let mut map = HashMap::default();
- map.insert(self.current_widget.widget_id - 1, is_ignoring_case);
-
- self.config.flags = Some(
- ConfigFlags::builder()
- .search_case_enabled_widgets(WidgetIdEnabled::create_from_hashmap(&map))
- .search_case_enabled_widgets_map(map)
- .build(),
- );
- }
-
- // self.did_config_fail_to_save = self.update_config_file().is_err();
- }
- }
-
- pub fn toggle_search_whole_word(&mut self) {
- let is_in_search_widget = self.is_in_search_widget();
- let mut is_searching_whole_word: Option<bool> = None;
- if let Some(proc_widget_state) = self
- .proc_state
- .widget_states
- .get_mut(&(self.current_widget.widget_id - 1))
- {
- if is_in_search_widget && proc_widget_state.is_search_enabled() {
- proc_widget_state
- .process_search_state
- .search_toggle_whole_word();
- proc_widget_state.update_query();
- self.proc_state.force_update = Some(self.current_widget.widget_id - 1);
-
- is_searching_whole_word = Some(
- proc_widget_state
- .process_search_state
- .is_searching_whole_word,
- );
- }
- }
-
- // Also toggle it in the config file if we actually changed it.
- if let Some(is_searching_whole_word) = is_searching_whole_word {
- if let Some(flags) = &mut self.config.flags {
- if let Some(map) = &mut flags.search_whole_word_enabled_widgets_map {
- // Just update the map.
- let mapping = map.entry(self.current_widget.widget_id - 1).or_default();
- *mapping = is_searching_whole_word;
-
- flags.search_whole_word_enabled_widgets =
- Some(WidgetIdEnabled::create_from_hashmap(map));
- } else {
- // Map doesn't exist yet... initialize ourselves.
- let mut map = HashMap::default();
- map.insert(self.current_widget.widget_id - 1, is_searching_whole_word);
- flags.search_whole_word_enabled_widgets =
- Some(WidgetIdEnabled::create_from_hashmap(&map));
- flags.search_whole_word_enabled_widgets_map = Some(map);
- }
- } else {
- // Must initialize it ourselves...
- let mut map = HashMap::default();
- map.insert(self.current_widget.widget_id - 1, is_searching_whole_word);
-
- self.config.flags = Some(
- ConfigFlags::builder()
- .search_whole_word_enabled_widgets(WidgetIdEnabled::create_from_hashmap(
- &map,
- ))
- .search_whole_word_enabled_widgets_map(map)
- .build(),
- );
- }
-
- // self.did_config_fail_to_save = self.update_config_file().is_err();
- }
- }
-
- pub fn toggle_search_regex(&mut self) {