summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClement Tsang <34804052+ClementTsang@users.noreply.github.com>2020-08-15 17:35:49 -0700
committerGitHub <noreply@github.com>2020-08-15 20:35:49 -0400
commitf3897f0538f90c682b96bc340c3c05e80be10b2d (patch)
treef24673d4d5702e48b9d3d1889f7498c97ac238a1
parent84f63f2f8306382dbf5cab819589161bf0b7c093 (diff)
feature: Allow sorting by any column
This feature allows any column to be sortable. This also adds: - Inverting sort for current column with `I` - Invoking a sort widget with `s` or `F6`. Close with same key or esc. And: - A bugfix in regards the basic menu and battery widget - A lot of refactoring
-rw-r--r--.vscode/settings.json1
-rw-r--r--CHANGELOG.md4
-rw-r--r--Cargo.toml1
-rw-r--r--README.md13
-rw-r--r--src/app.rs933
-rw-r--r--src/app/data_farmer.rs8
-rw-r--r--src/app/data_harvester.rs2
-rw-r--r--src/app/data_harvester/cpu.rs10
-rw-r--r--src/app/data_harvester/processes.rs43
-rw-r--r--src/app/layout_manager.rs267
-rw-r--r--src/app/states.rs253
-rw-r--r--src/canvas.rs47
-rw-r--r--src/canvas/dialogs/help_dialog.rs2
-rw-r--r--src/canvas/drawing_utils.rs8
-rw-r--r--src/canvas/widgets/basic_table_arrows.rs43
-rw-r--r--src/canvas/widgets/cpu_graph.rs6
-rw-r--r--src/canvas/widgets/process_table.rs226
-rw-r--r--src/constants.rs49
-rw-r--r--src/data_conversion.rs6
-rw-r--r--src/main.rs95
-rw-r--r--src/options.rs20
-rw-r--r--src/options/layout_options.rs92
-rw-r--r--tests/widget_movement_tests.rs1
23 files changed, 1379 insertions, 751 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 1f96e429..50026c86 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -31,6 +31,7 @@
"shilangyu",
"softirq",
"stime",
+ "subwidget",
"sysinfo",
"tokei",
"twrite",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 80715eed..f1ae3685 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [179](https://github.com/ClementTsang/bottom/pull/179): Show full command/process path as an option.
+- [183](https://github.com/ClementTsang/bottom/pull/183): Added sorting capabilities to any column.
+
### Changes
- Added `WASD` as an alternative widget movement system.
@@ -25,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Bug Fixes
+- [183](https://github.com/ClementTsang/bottom/pull/183): Fixed bug in basic mode where the battery widget was placed incorrectly.
+
## [0.4.5] - 2020-07-08
- No changes here, just an uptick for Crates.io using the wrong Cargo.lock.
diff --git a/Cargo.toml b/Cargo.toml
index 10106589..1b225266 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,6 +38,7 @@ backtrace = "0.3"
serde = {version = "1.0", features = ["derive"] }
unicode-segmentation = "1.6.0"
unicode-width = "0.1.7"
+# tui = {version = "0.10.0", features = ["crossterm"], default-features = false, git = "https://github.com/fdehau/tui-rs.git"}
tui = {version = "0.10.0", features = ["crossterm"], default-features = false }
# For debugging only...
diff --git a/README.md b/README.md
index 7797f8d0..f8acd08d 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,7 @@ A cross-platform graphical process/system monitor with a customizable interface
- [CPU bindings](#cpu-bindings)
- [Process bindings](#process-bindings)
- [Process search bindings](#process-search-bindings)
+ - [Process sort bindings](#process-sort-bindings)
- [Battery bindings](#battery-bindings)
- [Process searching keywords](#process-searching-keywords)
- [Supported keywords](#supported-keywords)
@@ -222,6 +223,8 @@ Run using `btm`.
| `Tab` | Group/un-group processes with the same name |
| `Ctrl-f`, `/` | Open process search widget |
| `P` | Toggle between showing the full path or just the process name |
+| `s, F6` | Open process sort widget |
+| `I` | Invert current sort |
#### Process search bindings
@@ -240,6 +243,16 @@ Run using `btm`.
| `Left` | Move cursor left |
| `Right` | Move cursor right |
+### Process sort bindings
+
+| | |
+| -------------- | ------------------------------- |
+| `Down`, `j` | Scroll down in list |
+| `Up`, `k` | Scroll up in list |
+| `Mouse scroll` | Scroll through sort widget |
+| `Esc` | Close the sort widget |
+| `Enter` | Sort by current selected column |
+
#### Battery bindings
| | |
diff --git a/src/app.rs b/src/app.rs
index 0d7a40aa..c884013f 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -144,20 +144,23 @@ impl App {
}
self.is_force_redraw = true;
- } else if self.is_filtering_or_searching() {
+ } 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() {
+ 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;
}
- self.is_force_redraw = true;
}
}
BottomWidgetType::ProcSearch => {
@@ -170,16 +173,32 @@ impl App {
.process_search_state
.search_state
.is_enabled = false;
- self.move_widget_selection_up();
+ self.move_widget_selection(&WidgetDirection::Up);
+ 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);
+ return;
}
- self.is_force_redraw = true;
}
}
_ => {}
}
- } else if self.is_expanded {
- self.is_expanded = false;
- self.is_force_redraw = true;
+
+ if self.is_expanded {
+ self.is_expanded = false;
+ self.is_force_redraw = true;
+ }
}
}
@@ -190,40 +209,6 @@ impl App {
}
}
- fn is_filtering_or_searching(&self) -> bool {
- match self.current_widget.widget_type {
- BottomWidgetType::Proc => {
- if let Some(proc_widget_state) = self
- .proc_state
- .widget_states
- .get(&self.current_widget.widget_id)
- {
- proc_widget_state
- .process_search_state
- .search_state
- .is_enabled
- } else {
- false
- }
- }
- BottomWidgetType::ProcSearch => {
- if let Some(proc_widget_state) = self
- .proc_state
- .widget_states
- .get(&(self.current_widget.widget_id - 1))
- {
- proc_widget_state
- .process_search_state
- .search_state
- .is_enabled
- } else {
- false
- }
- }
- _ => false,
- }
- }
-
fn reset_multi_tap_keys(&mut self) {
self.awaiting_second_char = false;
self.second_char = None;
@@ -254,6 +239,14 @@ impl App {
{
// Toggles process widget grouping state
proc_widget_state.is_grouped = !(proc_widget_state.is_grouped);
+
+ proc_widget_state
+ .columns
+ .column_mapping
+ .get_mut(&processes::ProcessSorting::State)
+ .unwrap()
+ .enabled = !(proc_widget_state.is_grouped);
+
self.proc_state.force_update = Some(self.current_widget.widget_id);
}
}
@@ -287,12 +280,64 @@ impl App {
.process_search_state
.search_state
.is_enabled = true;
- self.move_widget_selection_down();
+ self.move_widget_selection(&WidgetDirection::Down);
}
}
}
}
+ pub fn toggle_sort(&mut self) {
+ match &self.current_widget.widget_type {
+ widget_type @ BottomWidgetType::Proc | widget_type @ BottomWidgetType::ProcSort => {
+ let widget_id = self.current_widget.widget_id
+ - match &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(&proc_widget_state.process_sorting_type);
+ self.move_widget_selection(&WidgetDirection::Left);
+ } else {
+ // Otherwise, move right
+ self.move_widget_selection(&WidgetDirection::Right);
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+
+ pub fn invert_sort(&mut self) {
+ match &self.current_widget.widget_type {
+ widget_type @ BottomWidgetType::Proc | widget_type @ BottomWidgetType::ProcSort => {
+ let widget_id = self.current_widget.widget_id
+ - match &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.process_sorting_reverse =
+ !proc_widget_state.process_sorting_reverse;
+
+ self.proc_state.force_update = Some(widget_id);
+ }
+ }
+ _ => {}
+ }
+ }
+
pub fn toggle_ignore_case(&mut self) {
let is_in_search_widget = self.is_in_search_widget();
if let Some(proc_widget_state) = self
@@ -362,6 +407,16 @@ impl App {
} else {
self.delete_dialog_state.is_showing_dd = false;
}
+ } else if let BottomWidgetType::ProcSort = self.current_widget.widget_type {
+ if let Some(proc_widget_state) = self
+ .proc_state
+ .widget_states
+ .get_mut(&(self.current_widget.widget_id - 2))
+ {
+ self.proc_state.force_update = Some(self.current_widget.widget_id - 2);
+ proc_widget_state.update_sorting_with_columns();
+ self.toggle_sort();
+ }
}
}
@@ -457,7 +512,7 @@ impl App {
proc_widget_state
.process_search_state
.search_state
- .cursor_direction = CursorDirection::LEFT;
+ .cursor_direction = CursorDirection::Left;
proc_widget_state.update_query();
self.proc_state.force_update = Some(self.current_widget.widget_id - 1);
@@ -496,13 +551,18 @@ impl App {
if !self.is_in_dialog() {
match self.current_widget.widget_type {
BottomWidgetType::Proc => {
- if let Some(proc_widget_state) = self
- .proc_state
- .get_mut_widget_state(self.current_widget.widget_id)
- {
- proc_widget_state.current_column_index =
- proc_widget_state.current_column_index.saturating_sub(1);
- }
+ // if let Some(proc_widget_state) = self
+ // .proc_state
+ // .get_mut_widget_state(self.current_widget.widget_id)
+ // {
+ // proc_widget_state.current_column_index =
+ // proc_widget_state.current_column_index.saturating_sub(1);
+
+ // debug!(
+ // "Current column index <: {}",
+ // proc_widget_state.current_column_index
+ // );
+ // }
}
BottomWidgetType::ProcSearch => {
let is_in_search_widget = self.is_in_search_widget();
@@ -527,7 +587,7 @@ impl App {
proc_widget_state
.process_search_state
.search_state
- .cursor_direction = CursorDirection::LEFT;
+ .cursor_direction = CursorDirection::Left;
}
}
}
@@ -555,14 +615,20 @@ impl App {
if !self.is_in_dialog() {
match self.current_widget.widget_type {
BottomWidgetType::Proc => {
- if let Some(proc_widget_state) = self
- .proc_state
- .get_mut_widget_state(self.current_widget.widget_id)
- {
- if proc_widget_state.current_column_index < proc_widget_state.num_columns {
- proc_widget_state.current_column_index += 1;
- }
- }
+ // if let Some(proc_widget_state) = self
+ // .proc_state
+ // .get_mut_widget_state(self.current_widget.widget_id)
+ // {
+ // if proc_widget_state.current_column_index
+ // < proc_widget_state.columns.get_enabled_columns()
+ // {
+ // proc_widget_state.current_column_index += 1;
+ // }
+ // debug!(
+ // "Current column index >: {}",
+ // proc_widget_state.current_column_index
+ // );
+ // }
}
BottomWidgetType::ProcSearch => {
let is_in_search_widget = self.is_in_search_widget();
@@ -587,7 +653,7 @@ impl App {
proc_widget_state
.process_search_state
.search_state
- .cursor_direction = CursorDirection::RIGHT;
+ .cursor_direction = CursorDirection::Right;
}
}
}
@@ -643,7 +709,7 @@ impl App {
proc_widget_state
.process_search_state
.search_state
- .cursor_direction = CursorDirection::LEFT;
+ .cursor_direction = CursorDirection::Left;
}
}
}
@@ -689,7 +755,7 @@ impl App {
proc_widget_state
.process_search_state
.search_state
- .cursor_direction = CursorDirection::RIGHT;
+ .cursor_direction = CursorDirection::Right;
}
}
}
@@ -815,7 +881,7 @@ impl App {
proc_widget_state
.process_search_state
.search_state
- .cursor_direction = CursorDirection::RIGHT;
+ .cursor_direction = CursorDirection::Right;
return;
}
@@ -827,11 +893,17 @@ impl App {
// more obvious that we are separating dialog logic and normal logic IMO.
// This is even more so as most logic already checks for dialog state.
match caught_char {
- '1' => self.help_scroll_to_or_max(self.help_dialog_state.index_shortcuts[1]),
- '2' => self.help_scroll_to_or_max(self.help_dialog_state.index_shortcuts[2]),
- '3' => self.help_scroll_to_or_max(self.help_dialog_state.index_shortcuts[3]),
- '4' => self.help_scroll_to_or_max(self.help_dialog_state.index_shortcuts[4]),
- '5' => self.help_scroll_to_or_max(self.help_dialog_state.index_shortcuts[5]),
+ '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],
+ );
+ }
+ }
+ }
'j' | 'k' | 'g' | 'G' => self.handle_char(caught_char),
_ => {}
}
@@ -900,13 +972,13 @@ impl App {
.get_mut_widget_state(self.current_widget.widget_id)
{
match proc_widget_state.process_sorting_type {
- processes::ProcessSorting::CPU => {
+ processes::ProcessSorting::CpuPercent => {
proc_widget_state.process_sorting_reverse =
!proc_widget_state.process_sorting_reverse
}
_ => {
proc_widget_state.process_sorting_type =
- processes::ProcessSorting::CPU;
+ processes::ProcessSorting::CpuPercent;
proc_widget_state.process_sorting_reverse = true;
}
}
@@ -923,13 +995,13 @@ impl App {
.get_mut_widget_state(self.current_widget.widget_id)
{
match proc_widget_state.process_sorting_type {
- processes::ProcessSorting::MEM => {
+ processes::ProcessSorting::MemPercent => {
proc_widget_state.process_sorting_reverse =
!proc_widget_state.process_sorting_reverse
}
_ => {
proc_widget_state.process_sorting_type =
- processes::ProcessSorting::MEM;
+ processes::ProcessSorting::MemPercent;
proc_widget_state.process_sorting_reverse = true;
}
}
@@ -947,13 +1019,13 @@ impl App {
// Skip if grouped
if !proc_widget_state.is_grouped {
match proc_widget_state.process_sorting_type {
- processes::ProcessSorting::PID => {
+ processes::ProcessSorting::Pid => {
proc_widget_state.process_sorting_reverse =
!proc_widget_state.process_sorting_reverse
}
_ => {
proc_widget_state.process_sorting_type =
- processes::ProcessSorting::PID;
+ processes::ProcessSorting::Pid;
proc_widget_state.process_sorting_reverse = false;
}
}
@@ -969,8 +1041,24 @@ impl App {
.proc_state
.get_mut_widget_state(self.current_widget.widget_id)
{
- proc_widget_state.is_using_full_path =
- !proc_widget_state.is_using_full_path;
+ proc_widget_state.is_using_command = !proc_widget_state.is_using_command;
+ proc_widget_state
+ .toggle_command_and_name(proc_widget_state.is_using_command);
+
+ match &proc_widget_state.process_sorting_type {
+ processes::ProcessSorting::Command
+ | processes::ProcessSorting::ProcessName => {
+ if proc_widget_state.is_using_command {
+ proc_widget_state.process_sorting_type =
+ processes::ProcessSorting::Command;
+ } else {
+ proc_widget_state.process_sorting_type =
+ processes::ProcessSorting::ProcessName;
+ }
+ }
+ _ => {}
+ }
+
self.proc_state.force_update = Some(self.current_widget.widget_id);
}
}
@@ -982,13 +1070,18 @@ impl App {
.get_mut_widget_state(self.current_widget.widget_id)
{
match proc_widget_state.process_sorting_type {
- processes::ProcessSorting::IDENTIFIER => {
+ processes::ProcessSorting::ProcessName
+ | processes::ProcessSorting::Command => {
proc_widget_state.process_sorting_reverse =
!proc_widget_state.process_sorting_reverse
}
_ => {
proc_widget_state.process_sorting_type =
- processes::ProcessSorting::IDENTIFIER;
+ if proc_widget_state.is_using_command {
+ processes::ProcessSorting::Command
+ } else {
+ processes::ProcessSorting::ProcessName
+ };
proc_widget_state.process_sorting_reverse = false;
}
}
@@ -1001,15 +1094,17 @@ impl App {
self.help_dialog_state.is_showing_help = true;
self.is_force_redraw = true;
}
- 'H' | 'A' => self.move_widget_selection_left(),
- 'L' | 'D' => self.move_widget_selection_right(),
- 'K' | 'W' => self.move_widget_selection_up(),
- 'J' | 'S' => self.move_widget_selection_down(),
+ 'H' | 'A' => self.move_widget_selection(&WidgetDirection::Left),
+ 'L' | 'D' => self.move_widget_selection(&WidgetDirection::Right),
+ 'K' | 'W' => self.move_widget_selection(&WidgetDirection::Up),
+ 'J' | 'S' => self.move_widget_selection(&WidgetDirection::Down),
' ' => self.on_space(),
'+' => self.zoom_in(),
'-' => self.zoom_out(),
'=' => self.reset_zoom(),
'e' => self.expand_widget(),
+ 's' => self.toggle_sort(),
+ 'I' => self.invert_sort(),
_ => {}
}
@@ -1055,77 +1150,357 @@ impl App {
}
}
- pub fn move_widget_selection_left(&mut self) {
+ pub fn move_widget_selection(&mut self, direction: &WidgetDirection) {
+ /*
+ We follow these following steps:
+ 1. Send a movement signal in `direction`.
+ 2. Check if this new widget we've landed on is hidden. If not, halt.
+ 3. If it hidden, loop and either send:
+ - A signal equal to the current direction, if it is opposite of the reflection.
+ - Reflection direction.
+ */
+
if !self.is_in_dialog() && !self.is_expanded {
- if let Some(current_widget) = self.widget_map.get(&self.current_widget.widget_id) {
- if let Some(new_widget_id) = current_widget.left_neighbour {
- if let Some(new_widget) = self.widget_map.get(&new_widget_id) {
- match new_widget.widget_type {
- BottomWidgetType::Temp
- | BottomWidgetType::Proc
- | BottomWidgetType::ProcSearch
- | BottomWidgetType::Disk
- | BottomWidgetType::Battery
- if self.basic_table_widget_state.is_some() =>
- {
- if let Some(basic_table_widget_state) =
- &mut self.basic_table_widget_state
+ if let Some(new_widget_id) = &(match direction {
+ WidgetDirection::Left => self.current_widget.left_neighbour,
+ WidgetDirection::Right => self.current_widget.right_neighbour,
+ WidgetDirection::Up => self.current_widget.up_neighbour,
+ WidgetDirection::Down => self.current_widget.down_neighbour,
+ }) {
+ if let Some(new_widget) = self.widget_map.get(&new_widget_id) {
+ match &new_widget.widget_type {
+ BottomWidgetType::Temp
+ | BottomWidgetType::Proc
+ | BottomWidgetType::ProcSearch
+ | BottomWidgetType::ProcSort
+ | BottomWidgetType::Disk
+ | BottomWidgetType::Battery
+ if self.basic_table_widget_state.is_some()
+ && (*direction == WidgetDirection::Left
+ || *direction == WidgetDirection::Right) =>
+ {
+ // Gotta do this for the sort widget
+ if let BottomWidgetType::ProcSort = new_widget.widget_type {
+ if let Some(proc_widget_state) =
+ self.proc_state.widget_states.get(&(new_widget_id - 2))
{
- basic_table_widget_state.currently_displayed_widget_id =
- new_widget_id;
- basic_table_widget_state.currently_displayed_widget_type =
- new_widget.widget_type.clone();
+ if proc_widget_state.is_sort_open {
+ self.current_widget = new_widget.clone();
+ } else if let Some(next_new_widget_id) = match direction {
+ WidgetDirection::Left => new_widget.left_neighbour,
+ _ => new_widget.right_neighbour,
+ } {
+ if let Some(next_new_widget) =
+ self.widget_map.get(&next_new_widget_id)
+ {
+ self.current_widget = next_new_widget.clone();
+ }
+ }
}
+ } else {
self.current_widget = new_widget.clone();
}
- BottomWidgetType::CpuLegend => {
- if let Some(cpu_widget_state) =
- self.cpu_state.widget_states.get(&(new_widget_id - 1))
- {
- if cpu_widget_state.is_legend_hidden {
- if let Some(next_new_widget_id) = new_widget.left_neighbour
+
+ if let Some(basic_table_widget_state) =
+ &mut self.basic_table_widget_state
+ {
+ basic_table_widget_state.currently_displayed_widget_id =
+ self.current_widget.widget_id;
+ basic_table_widget_state.currently_displayed_widget_type =
+ self.current_widget.widget_type.clone();
+ }
+ }
+ BottomWidgetType::BasicTables => {
+ match &direction {
+ WidgetDirection::Up => {
+ // Note this case would fail if it moved up into a hidden
+ // widget, but it's for basic so whatever, it's all hard-coded
+ // right now anyways.
+ if let Some(next_new_widget_id) = new_widget.up_neighbour {
+ if let Some(next_new_widget) =
+ self.widget_map.get(&next_new_widget_id)
{
- if let Some(next_new_widget) =
- self.widget_map.get(&next_new_widget_id)
+ self.current_widget = next_new_widget.clone();
+ }
+ }
+ }
+ WidgetDirection::Down => {
+ // This means we're in basic mode. As such, then
+ // we want to move DOWN to the currently shown widget
+ if let Some(basic_table_widget_state) =
+ &self.basic_table_widget_state
+ {
+ if let Some(next_new_widget) = self.widget_map.get(
+ &basic_table_widget_state.currently_displayed_widget_id,
+ ) {
+ self.current_widget = next_new_widget.clone();
+ }
+ }
+ }
+ _ => self.current_widget = new_widget.clone(),
+ }
+ }
+ _ if new_widget.parent_reflector.is_some() => {
+ // It may be hidden...
+ if let Some((parent_direction, offset)) = &new_widget.parent_reflector {
+ if direction.is_opposite(parent_direction) {
+ // Keep going in the current direction if hidden...
+ let next_neighbour_id = match &direction {
+ WidgetDirection::Left => new_widget.left_neighbour,
+ WidgetDirection::Right => new_widget.right_neighbour,
+ WidgetDirection::Up => new_widget.up_neighbour,
+ WidgetDirection::Down => new_widget.down_neighbour,
+ }
+ .unwrap_or(*new_widget_id);
+ match &new_widget.widget_type {
+ BottomWidgetType::CpuLegend => {
+ if let Some(cpu_widget_state) = self
+