mirror of
https://github.com/ClementTsang/bottom
synced 2025-02-16 13:18:28 +00:00
feature: Add mouse click support for moving between widgets (#208)
Adds mouse support to the application, to move between widgets and click on elements. List of things to added: - Click to move between widgets - Click to move between widgets in basic mode - Click on widget entries - Ability to disable mouse if you don't like it, I guess
This commit is contained in:
parent
b6363096b4
commit
3d2fc76aa2
23 changed files with 901 additions and 333 deletions
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
|
@ -1,5 +1,7 @@
|
|||
{
|
||||
"cSpell.words": [
|
||||
"Artem",
|
||||
"COPR",
|
||||
"DWORD",
|
||||
"Deque",
|
||||
"EINVAL",
|
||||
|
@ -14,8 +16,10 @@
|
|||
"MSRV",
|
||||
"Mahmoud",
|
||||
"Marcin",
|
||||
"Mousebindings",
|
||||
"Nonexhaustive",
|
||||
"PKGBUILD",
|
||||
"Polishchuk",
|
||||
"Qudsi",
|
||||
"SIGTERM",
|
||||
"TEBI",
|
||||
|
@ -26,6 +30,7 @@
|
|||
"WASD",
|
||||
"Wojnarowski",
|
||||
"andys",
|
||||
"atim",
|
||||
"choco",
|
||||
"cmdline",
|
||||
"commandline",
|
||||
|
|
|
@ -11,11 +11,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- [#206](https://github.com/ClementTsang/bottom/pull/206): Adaptive network graphs --- prior to this update, graphs were stuck at a range from 0B to 1GiB. Now, they adjust to your current usage and time span, so if you're using, say, less than a MiB, it will cap at a MiB. If you're using 10GiB, then the graph will reflect that and span to a bit greater than 10GiB.
|
||||
|
||||
- [#208](https://github.com/ClementTsang/bottom/pull/208): Mouse support for tables and moving to widgets.
|
||||
|
||||
### Changes
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [211](https://github.com/ClementTsang/bottom/pull/211): Fixes a bug where you could move down in the process widget even if the process widget search was closed.
|
||||
- [#211](https://github.com/ClementTsang/bottom/pull/211): Fixes a bug where you could move down in the process widget even if the process widget search was closed.
|
||||
|
||||
## [0.4.7] - 2020-08-26
|
||||
|
||||
|
|
77
README.md
77
README.md
|
@ -25,7 +25,6 @@ A cross-platform graphical process/system monitor with a customizable interface
|
|||
- [Options](#options)
|
||||
- [Keybindings](#keybindings)
|
||||
- [General](#general)
|
||||
- [CPU bindings](#cpu-bindings)
|
||||
- [Process bindings](#process-bindings)
|
||||
- [Process search bindings](#process-search-bindings)
|
||||
- [Process sort bindings](#process-sort-bindings)
|
||||
|
@ -35,6 +34,9 @@ A cross-platform graphical process/system monitor with a customizable interface
|
|||
- [Supported comparison operators](#supported-comparison-operators)
|
||||
- [Supported logical operators](#supported-logical-operators)
|
||||
- [Supported units](#supported-units)
|
||||
- [Mousebindings](#mousebindings)
|
||||
- [General](#general-1)
|
||||
- [CPU bindings](#cpu-bindings)
|
||||
- [Features](#features)
|
||||
- [Processes](#processes)
|
||||
- [Process searching](#process-searching)
|
||||
|
@ -43,7 +45,7 @@ A cross-platform graphical process/system monitor with a customizable interface
|
|||
- [Expanding](#expanding)
|
||||
- [Basic mode](#basic-mode)
|
||||
- [Config files](#config-files)
|
||||
- [Config flags](#config-flags)
|
||||
- [Config flags and options](#config-flags-and-options)
|
||||
- [Theming](#theming)
|
||||
- [Layout](#layout)
|
||||
- [Battery](#battery)
|
||||
|
@ -176,6 +178,7 @@ Run using `btm`.
|
|||
--use_old_network_legend Use the older (pre-0.4) network legend which is separate from the network chart
|
||||
--hide_table_gap Hides the spacing between table headers and data
|
||||
--battery Displays the battery widget for default and basic layouts
|
||||
--disable_click Disables mouse clicks from interacting with the program
|
||||
```
|
||||
|
||||
### Options
|
||||
|
@ -193,34 +196,27 @@ Run using `btm`.
|
|||
|
||||
#### General
|
||||
|
||||
| | |
|
||||
| ------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||
| `q`, `Ctrl-c` | Quit |
|
||||
| `Esc` | Close dialog windows, search, widgets, or exit expanded mode |
|
||||
| `Ctrl-r` | Reset display and any collected data |
|
||||
| `f` | Freeze/unfreeze updating with new data |
|
||||
| `Ctrl-Left`<br>`Shift-Left`<br>`H`<br>`A` | Move widget selection left |
|
||||
| `Ctrl-Right`<br>`Shift-Right`<br>`L`<br>`D` | Move widget selection right |
|
||||
| `Ctrl-Up`<br>`Shift-Up`<br>`K`<br>`W` | Move widget selection up |
|
||||
| `Ctrl-Down`<br>`Shift-Down`<br>`J`<br>`S` | Move widget selection down |
|
||||
| `Left`, `h` | Move left within widget |
|
||||
| `Down`, `j` | Move down within widget |
|
||||
| `Up`,`k` | Move up within widget |
|
||||
| `Right`, `l` | Move right within widget |
|
||||
| `?` | Open help menu |
|
||||
| `gg`, `Home` | Jump to the first entry |
|
||||
| `Shift-g`, `End` | Jump to the last entry |
|
||||
| `e` | Toggle expanding the currently selected widget |
|
||||
| `+` | Zoom in on chart (decrease time range) |
|
||||
| `-` | Zoom out on chart (increase time range) |
|
||||
| `=` | Reset zoom |
|
||||
| Mouse scroll | Table: Scroll<br>Chart: Zooms in or out by scrolling up or down respectively |
|
||||
|
||||
#### CPU bindings
|
||||
|
||||
| | |
|
||||
| ------------ | --------------------------------------------------------------------- |
|
||||
| Mouse scroll | Scrolling over an CPU core/average shows only that entry on the chart |
|
||||
| | |
|
||||
| ------------------------------------------- | ------------------------------------------------------------ |
|
||||
| `q`, `Ctrl-c` | Quit |
|
||||
| `Esc` | Close dialog windows, search, widgets, or exit expanded mode |
|
||||
| `Ctrl-r` | Reset display and any collected data |
|
||||
| `f` | Freeze/unfreeze updating with new data |
|
||||
| `Ctrl-Left`<br>`Shift-Left`<br>`H`<br>`A` | Move widget selection left |
|
||||
| `Ctrl-Right`<br>`Shift-Right`<br>`L`<br>`D` | Move widget selection right |
|
||||
| `Ctrl-Up`<br>`Shift-Up`<br>`K`<br>`W` | Move widget selection up |
|
||||
| `Ctrl-Down`<br>`Shift-Down`<br>`J`<br>`S` | Move widget selection down |
|
||||
| `Left`, `h` | Move left within widget |
|
||||
| `Down`, `j` | Move down within widget |
|
||||
| `Up`,`k` | Move up within widget |
|
||||
| `Right`, `l` | Move right within widget |
|
||||
| `?` | Open help menu |
|
||||
| `gg`, `Home` | Jump to the first entry |
|
||||
| `Shift-g`, `End` | Jump to the last entry |
|
||||
| `e` | Toggle expanding the currently selected widget |
|
||||
| `+` | Zoom in on chart (decrease time range) |
|
||||
| `-` | Zoom out on chart (increase time range) |
|
||||
| `=` | Reset zoom |
|
||||
|
||||
#### Process bindings
|
||||
|
||||
|
@ -340,6 +336,21 @@ Note that the `and` operator takes precedence over the `or` operator.
|
|||
| -------- | ---------------------------------------------------- | -------------------------- |
|
||||
| `()` | `(<CONDITION 1> AND <CONDITION 2>) OR <CONDITION 3>` | Group together a condition |
|
||||
|
||||
### Mousebindings
|
||||
|
||||
#### General
|
||||
|
||||
| | |
|
||||
| ------------ | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| Mouse scroll | Table: Scroll<br>Chart: Zooms in or out by scrolling up or down respectively |
|
||||
| Mouse click | Selects the clicked widget. For tables, clicking can also select a specific entry. Can be disabled via options/flags. |
|
||||
|
||||
#### CPU bindings
|
||||
|
||||
| | |
|
||||
| ------------ | --------------------------------------------------------------------- |
|
||||
| Mouse scroll | Scrolling over an CPU core/average shows only that entry on the chart |
|
||||
|
||||
## Features
|
||||
|
||||
As yet _another_ process/system visualization and management application, bottom supports the typical features:
|
||||
|
@ -434,11 +445,11 @@ By default, bottom will look at (based on [dirs](https://github.com/dirs-dev/dir
|
|||
|
||||
Note that if a config file does not exist at either the default location or the passed in location via `-C` or `--config`, one is automatically created with no settings applied.
|
||||
|
||||
#### Config flags
|
||||
#### Config flags and options
|
||||
|
||||
The following options can be set under `[flags]` to achieve the same effect as passing in a flag on runtime. Note that if a flag is given, it will override the config file.
|
||||
|
||||
These are the following supported flag config values:
|
||||
These are the following supported flag config values, which correspond to the flag of the same name described in [Flags](#flags) and [Options](#options):
|
||||
|
||||
| Field | Type |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------- |
|
||||
|
@ -461,6 +472,7 @@ These are the following supported flag config values:
|
|||
| `temperature_type` | String (one of ["k", "f", "c", "kelvin", "fahrenheit", "celsius"]) |
|
||||
| `default_widget_type` | String (one of ["cpu", "proc", "net", "temp", "mem", "disk"], same as layout options) |
|
||||
| `default_widget_count` | Unsigned Int (represents which `default_widget_type`) |
|
||||
| `disable_click` | Boolean |
|
||||
|
||||
#### Theming
|
||||
|
||||
|
@ -614,6 +626,7 @@ Thanks to all contributors ([emoji key](https://allcontributors.org/docs/en/emoj
|
|||
|
||||
<!-- markdownlint-enable -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
## Thanks
|
||||
|
|
349
src/app.rs
349
src/app.rs
|
@ -40,6 +40,7 @@ pub struct AppConfigFields {
|
|||
pub autohide_time: bool,
|
||||
pub use_old_network_legend: bool,
|
||||
pub table_gap: u16,
|
||||
pub disable_click: bool,
|
||||
}
|
||||
|
||||
#[derive(TypedBuilder)]
|
||||
|
@ -80,6 +81,9 @@ pub struct App {
|
|||
#[builder(default = false, setter(skip))]
|
||||
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,
|
||||
|
||||
|
@ -90,9 +94,7 @@ pub struct App {
|
|||
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,
|
||||
|
@ -133,6 +135,10 @@ impl App {
|
|||
self.data_collection.reset();
|
||||
}
|
||||
|
||||
pub fn should_get_widget_bounds(&self) -> bool {
|
||||
self.is_force_redraw || self.is_determining_widget_boundary
|
||||
}
|
||||
|
||||
fn close_dd(&mut self) {
|
||||
self.delete_dialog_state.is_showing_dd = false;
|
||||
self.delete_dialog_state.is_on_yes = false;
|
||||
|
@ -279,11 +285,9 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
/// "On space" if we don't want to treat is as a character.
|
||||
pub fn on_space(&mut self) {}
|
||||
|
||||
pub fn on_slash(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
// FIXME: Add ProcSort too, it's annoying
|
||||
if let BottomWidgetType::Proc = self.current_widget.widget_type {
|
||||
// Toggle on
|
||||
if let Some(proc_widget_state) = self
|
||||
|
@ -295,6 +299,7 @@ impl App {
|
|||
.search_state
|
||||
.is_enabled = true;
|
||||
self.move_widget_selection(&WidgetDirection::Down);
|
||||
self.is_force_redraw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -302,6 +307,7 @@ impl App {
|
|||
|
||||
pub fn toggle_sort(&mut self) {
|
||||
match &self.current_widget.widget_type {
|
||||
// FIXME: [REFACTOR] Remove these @'s if unneeded, they were an idea but they're ultimately useless for me here...?
|
||||
widget_type @ BottomWidgetType::Proc | widget_type @ BottomWidgetType::ProcSort => {
|
||||
let widget_id = self.current_widget.widget_id
|
||||
- match &widget_type {
|
||||
|
@ -326,6 +332,8 @@ impl App {
|
|||
self.move_widget_selection(&WidgetDirection::Right);
|
||||
}
|
||||
}
|
||||
|
||||
self.is_force_redraw = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -1151,7 +1159,6 @@ impl App {
|
|||
'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(),
|
||||
|
@ -1214,7 +1221,16 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn move_widget_selection(&mut self, direction: &WidgetDirection) {
|
||||
// Since we only want to call reset once, we do it like this to avoid
|
||||
// redundant calls on recursion.
|
||||
self.move_widget_selection_logic(direction);
|
||||
self.reset_multi_tap_keys();
|
||||
}
|
||||
|
||||
fn move_widget_selection_logic(&mut self, direction: &WidgetDirection) {
|
||||
/*
|
||||
The actual logic for widget movement.
|
||||
|
||||
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.
|
||||
|
@ -1234,7 +1250,6 @@ impl App {
|
|||
match &new_widget.widget_type {
|
||||
BottomWidgetType::Temp
|
||||
| BottomWidgetType::Proc
|
||||
| BottomWidgetType::ProcSearch
|
||||
| BottomWidgetType::ProcSort
|
||||
| BottomWidgetType::Disk
|
||||
| BottomWidgetType::Battery
|
||||
|
@ -1272,13 +1287,16 @@ impl App {
|
|||
basic_table_widget_state.currently_displayed_widget_type =
|
||||
self.current_widget.widget_type.clone();
|
||||
}
|
||||
|
||||
// And let's not forget:
|
||||
self.is_determining_widget_boundary = true;
|
||||
}
|
||||
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.
|
||||
// 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)
|
||||
|
@ -1288,11 +1306,22 @@ impl App {
|
|||
}
|
||||
}
|
||||
WidgetDirection::Down => {
|
||||
// This means we're in basic mode. As such, then
|
||||
// we want to move DOWN to the currently shown widget
|
||||
// Assuming we're in basic mode (BasicTables), then
|
||||
// we want to move DOWN to the currently shown widget.
|
||||
if let Some(basic_table_widget_state) =
|
||||
&self.basic_table_widget_state
|
||||
&mut self.basic_table_widget_state
|
||||
{
|
||||
// We also want to move towards Proc if we had set it to ProcSort.
|
||||
if let BottomWidgetType::ProcSort =
|
||||
basic_table_widget_state.currently_displayed_widget_type
|
||||
{
|
||||
basic_table_widget_state
|
||||
.currently_displayed_widget_type =
|
||||
BottomWidgetType::Proc;
|
||||
basic_table_widget_state
|
||||
.currently_displayed_widget_id -= 2;
|
||||
}
|
||||
|
||||
if let Some(next_new_widget) = self.widget_map.get(
|
||||
&basic_table_widget_state.currently_displayed_widget_id,
|
||||
) {
|
||||
|
@ -1308,13 +1337,13 @@ impl App {
|
|||
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 {
|
||||
// unless we hit a wall of sorts.
|
||||
let option_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
|
||||
|
@ -1323,11 +1352,15 @@ impl App {
|
|||
.get(&(new_widget_id - *offset))
|
||||
{
|
||||
if cpu_widget_state.is_legend_hidden {
|
||||
if let Some(next_neighbour_widget) =
|
||||
self.widget_map.get(&next_neighbour_id)
|
||||
if let Some(next_neighbour_id) =
|
||||
option_next_neighbour_id
|
||||
{
|
||||
self.current_widget =
|
||||
next_neighbour_widget.clone();
|
||||
if let Some(next_neighbour_widget) =
|
||||
self.widget_map.get(&next_neighbour_id)
|
||||
{
|
||||
self.current_widget =
|
||||
next_neighbour_widget.clone();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.current_widget = new_widget.clone();
|
||||
|
@ -1344,12 +1377,17 @@ impl App {
|
|||
match &new_widget.widget_type {
|
||||
BottomWidgetType::ProcSearch => {
|
||||
if !proc_widget_state.is_search_enabled() {
|
||||
if let Some(next_neighbour_widget) =
|
||||
self.widget_map
|
||||
.get(&next_neighbour_id)
|
||||
if let Some(next_neighbour_id) =
|
||||
option_next_neighbour_id
|
||||
{
|
||||
self.current_widget =
|
||||
next_neighbour_widget.clone();
|
||||
if let Some(next_neighbour_widget) =
|
||||
self.widget_map
|
||||
.get(&next_neighbour_id)
|
||||
{
|
||||
self.current_widget =
|
||||
next_neighbour_widget
|
||||
.clone();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.current_widget =
|
||||
|
@ -1358,12 +1396,17 @@ impl App {
|
|||
}
|
||||
BottomWidgetType::ProcSort => {
|
||||
if !proc_widget_state.is_sort_open {
|
||||
if let Some(next_neighbour_widget) =
|
||||
self.widget_map
|
||||
.get(&next_neighbour_id)
|
||||
if let Some(next_neighbour_id) =
|
||||
option_next_neighbour_id
|
||||
{
|
||||
self.current_widget =
|
||||
next_neighbour_widget.clone();
|
||||
if let Some(next_neighbour_widget) =
|
||||
self.widget_map
|
||||
.get(&next_neighbour_id)
|
||||
{
|
||||
self.current_widget =
|
||||
next_neighbour_widget
|
||||
.clone();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.current_widget =
|
||||
|
@ -1498,7 +1541,7 @@ impl App {
|
|||
}
|
||||
|
||||
if let Some(ref_dir) = &reflection_dir {
|
||||
self.move_widget_selection(ref_dir);
|
||||
self.move_widget_selection_logic(ref_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1544,8 +1587,6 @@ impl App {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
self.reset_multi_tap_keys();
|
||||
}
|
||||
|
||||
fn handle_left_expanded_movement(&mut self) {
|
||||
|
@ -1768,11 +1809,11 @@ impl App {
|
|||
pub fn decrement_position_count(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
match self.current_widget.widget_type {
|
||||
BottomWidgetType::Proc => self.change_process_position(-1),
|
||||
BottomWidgetType::ProcSort => self.change_process_sort_position(-1),
|
||||
BottomWidgetType::Temp => self.change_temp_position(-1),
|
||||
BottomWidgetType::Disk => self.change_disk_position(-1),
|
||||
BottomWidgetType::CpuLegend => self.change_cpu_table_position(-1),
|
||||
BottomWidgetType::Proc => self.increment_process_position(-1),
|
||||
BottomWidgetType::ProcSort => self.increment_process_sort_position(-1),
|
||||
BottomWidgetType::Temp => self.increment_temp_position(-1),
|
||||
BottomWidgetType::Disk => self.increment_disk_position(-1),
|
||||
BottomWidgetType::CpuLegend => self.increment_cpu_legend_position(-1),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -1781,17 +1822,17 @@ impl App {
|
|||
pub fn increment_position_count(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
match self.current_widget.widget_type {
|
||||
BottomWidgetType::Proc => self.change_process_position(1),
|
||||
BottomWidgetType::ProcSort => self.change_process_sort_position(1),
|
||||
BottomWidgetType::Temp => self.change_temp_position(1),
|
||||
BottomWidgetType::Disk => self.change_disk_position(1),
|
||||
BottomWidgetType::CpuLegend => self.change_cpu_table_position(1),
|
||||
BottomWidgetType::Proc => self.increment_process_position(1),
|
||||
BottomWidgetType::ProcSort => self.increment_process_sort_position(1),
|
||||
BottomWidgetType::Temp => self.increment_temp_position(1),
|
||||
BottomWidgetType::Disk => self.increment_disk_position(1),
|
||||
BottomWidgetType::CpuLegend => self.increment_cpu_legend_position(1),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn change_process_sort_position(&mut self, num_to_change_by: i64) {
|
||||
fn increment_process_sort_position(&mut self, num_to_change_by: i64) {
|
||||
if let Some(proc_widget_state) = self
|
||||
.proc_state
|
||||
.get_mut_widget_state(self.current_widget.widget_id - 2)
|
||||
|
@ -1814,7 +1855,7 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
fn change_cpu_table_position(&mut self, num_to_change_by: i64) {
|
||||
fn increment_cpu_legend_position(&mut self, num_to_change_by: i64) {
|
||||
if let Some(cpu_widget_state) = self
|
||||
.cpu_state
|
||||
.widget_states
|
||||
|
@ -1838,13 +1879,12 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
fn change_process_position(&mut self, num_to_change_by: i64) {
|
||||
fn increment_process_position(&mut self, num_to_change_by: i64) {
|
||||
if let Some(proc_widget_state) = self
|
||||
.proc_state
|
||||
.get_mut_widget_state(self.current_widget.widget_id)
|
||||
{
|
||||
let current_posn = proc_widget_state.scroll_state.current_scroll_position;
|
||||
|
||||
if let Some(finalized_process_data) = self
|
||||
.canvas_data
|
||||
.finalized_process_data_map
|
||||
|
@ -1866,7 +1906,7 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
fn change_temp_position(&mut self, num_to_change_by: i64) {
|
||||
fn increment_temp_position(&mut self, num_to_change_by: i64) {
|
||||
if let Some(temp_widget_state) = self
|
||||
.temp_state
|
||||
.widget_states
|
||||
|
@ -1890,7 +1930,7 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
fn change_disk_position(&mut self, num_to_change_by: i64) {
|
||||
fn increment_disk_position(&mut self, num_to_change_by: i64) {
|
||||
if let Some(disk_widget_state) = self
|
||||
.disk_state
|
||||
.widget_states
|
||||
|
@ -2168,4 +2208,219 @@ impl App {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves the mouse to the widget that was clicked on, then propagates the click down to be
|
||||
/// handled by the widget specifically.
|
||||
pub fn left_mouse_click_movement(&mut self, x: u16, y: u16) {
|
||||
// Pretty dead simple - iterate through the widget map and go to the widget where the click
|
||||
// is within.
|
||||
if let Some(bt) = &mut self.basic_table_widget_state {
|
||||
if let (
|
||||
Some((left_tlc_x, left_tlc_y)),
|
||||
Some((left_brc_x, left_brc_y)),
|
||||
Some((right_tlc_x, right_tlc_y)),
|
||||
Some((right_brc_x, right_brc_y)),
|
||||
) = (bt.left_tlc, bt.left_brc, bt.right_tlc, bt.right_brc)
|
||||
{
|
||||
if (x >= left_tlc_x && y >= left_tlc_y) && (x <= left_brc_x && y <= left_brc_y) {
|
||||
if let Some(new_widget) =
|
||||
self.widget_map.get(&(bt.currently_displayed_widget_id))
|
||||
{
|
||||
// We have to move to the current table widget first...
|
||||
self.current_widget = new_widget.clone();
|
||||
|
||||
if let BottomWidgetType::Proc = &new_widget.widget_type {
|
||||
if let Some(proc_widget_state) =
|
||||
self.proc_state.get_widget_state(new_widget.widget_id)
|
||||
{
|
||||
if proc_widget_state.is_sort_open {
|
||||
self.move_widget_selection(&WidgetDirection::Left);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.move_widget_selection(&WidgetDirection::Left);
|
||||
return;
|
||||
}
|
||||
} else if (x >= right_tlc_x && y >= right_tlc_y)
|
||||
&& (x <= right_brc_x && y <= right_brc_y)
|
||||
{
|
||||
if let Some(new_widget) =
|
||||
self.widget_map.get(&(bt.currently_displayed_widget_id))
|
||||
{
|
||||
// We have to move to the current table widget first...
|
||||
self.current_widget = new_widget.clone();
|
||||
|
||||
if let BottomWidgetType::ProcSort = &new_widget.widget_type {
|
||||
if let Some(proc_widget_state) =
|
||||
self.proc_state.get_widget_state(new_widget.widget_id - 2)
|
||||
{
|
||||
if proc_widget_state.is_sort_open {
|
||||
self.move_widget_selection(&WidgetDirection::Right);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.move_widget_selection(&WidgetDirection::Right);
|
||||
// Bit extra logic to ensure you always land on a proc widget, not the sort
|
||||
if let BottomWidgetType::ProcSort = &self.current_widget.widget_type {
|
||||
self.move_widget_selection(&WidgetDirection::Right);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut failed_to_get = true;
|
||||
// TODO: [MOUSE] We could use a better data structure for this? Currently it's a blind
|
||||
// traversal through a hashmap, using a 2d binary tree of sorts would be better.
|
||||
for (new_widget_id, widget) in &self.widget_map {
|
||||
if let (Some((tlc_x, tlc_y)), Some((brc_x, brc_y))) =
|
||||
(widget.top_left_corner, widget.bottom_right_corner)
|
||||
{
|
||||
if (x >= tlc_x && y >= tlc_y) && (x <= brc_x && y <= brc_y) {
|
||||
if let Some(new_widget) = self.widget_map.get(&new_widget_id) {
|
||||
self.current_widget = new_widget.clone();
|
||||
|
||||
match &self.current_widget.widget_type {
|
||||
BottomWidgetType::Temp
|
||||
| BottomWidgetType::Proc
|
||||
| BottomWidgetType::ProcSort
|
||||
| BottomWidgetType::Disk
|
||||
| BottomWidgetType::Battery => {
|
||||
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();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
failed_to_get = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if failed_to_get {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now handle click propagation down to widget.
|
||||
if let Some((_tlc_x, tlc_y)) = &self.current_widget.top_left_corner {
|
||||
match &self.current_widget.widget_type {
|
||||
BottomWidgetType::Proc
|
||||
| BottomWidgetType::ProcSort
|
||||
| BottomWidgetType::CpuLegend
|
||||
| BottomWidgetType::Temp
|
||||
| BottomWidgetType::Disk => {
|
||||
// Get our index...
|
||||
let clicked_entry = y - *tlc_y;
|
||||
// + 1 so we start at 0.
|
||||
let offset = 1
|
||||
+ if self.is_drawing_border() { 1 } else { 0 }
|
||||
+ if self.is_drawing_gap(&self.current_widget) {
|
||||
self.app_config_fields.table_gap
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if clicked_entry >= offset {
|
||||
let offset_clicked_entry = clicked_entry - offset;
|
||||
match &self.current_widget.widget_type {
|
||||
BottomWidgetType::Proc => {
|
||||
if let Some(proc_widget_state) = self
|
||||
.proc_state
|
||||
.get_widget_state(self.current_widget.widget_id)
|
||||
{
|
||||
if let Some(visual_index) =
|
||||
proc_widget_state.scroll_state.table_state.selected()
|
||||
{
|
||||
self.increment_process_position(
|
||||
offset_clicked_entry as i64 - visual_index as i64,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
BottomWidgetType::ProcSort => {
|
||||
if let Some(proc_widget_state) = self
|
||||
.proc_state
|
||||
.get_widget_state(self.current_widget.widget_id - 2)
|
||||
{
|
||||
if let Some(visual_index) =
|
||||
proc_widget_state.columns.column_state.selected()
|
||||
{
|
||||
self.increment_process_sort_position(
|
||||
offset_clicked_entry as i64 - visual_index as i64,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
BottomWidgetType::CpuLegend => {
|
||||
if let Some(cpu_widget_state) = self
|
||||
.cpu_state
|
||||
.get_widget_state(self.current_widget.widget_id - 1)
|
||||
{
|
||||
if let Some(visual_index) =
|
||||
cpu_widget_state.scroll_state.table_state.selected()
|
||||
{
|
||||
self.increment_cpu_legend_position(
|
||||
offset_clicked_entry as i64 - visual_index as i64,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
BottomWidgetType::Temp => {
|
||||
if let Some(temp_widget_state) = self
|
||||
.temp_state
|
||||
.get_widget_state(self.current_widget.widget_id)
|
||||
{
|
||||
if let Some(visual_index) =
|
||||
temp_widget_state.scroll_state.table_state.selected()
|
||||
{
|
||||
self.increment_temp_position(
|
||||
offset_clicked_entry as i64 - visual_index as i64,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
BottomWidgetType::Disk => {
|
||||
if let Some(disk_widget_state) = self
|
||||
.disk_state
|
||||
.get_widget_state(self.current_widget.widget_id)
|
||||
{
|
||||
if let Some(visual_index) =
|
||||
disk_widget_state.scroll_state.table_state.selected()
|
||||
{
|
||||
self.increment_disk_position(
|
||||
offset_clicked_entry as i64 - visual_index as i64,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_drawing_border(&self) -> bool {
|
||||
self.is_expanded || !self.app_config_fields.use_basic_mode
|
||||
}
|
||||
|
||||
fn is_drawing_gap(&self, widget: &BottomWidget) -> bool {
|
||||
if let (Some((_tlc_x, tlc_y)), Some((_brc_x, brc_y))) =
|
||||
(widget.top_left_corner, widget.bottom_right_corner)
|
||||
{
|
||||
brc_y - tlc_y >= constants::TABLE_GAP_HEIGHT_LIMIT
|
||||
} else {
|
||||
self.app_config_fields.table_gap == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -867,6 +867,14 @@ pub struct BottomWidget {
|
|||
/// The value is the direction to bounce, as well as the parent offset.
|
||||
#[builder(default = None)]
|
||||
pub parent_reflector: Option<(WidgetDirection, u64)>,
|
||||
|
||||
/// Top left corner when drawn, for mouse click detection
|
||||
#[builder(default = None)]
|
||||
pub top_left_corner: Option<(u16, u16)>,
|
||||
|
||||
/// Bottom right corner when drawn, for mouse click detection
|
||||
#[builder(default = None)]
|
||||
pub bottom_right_corner: Option<(u16, u16)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
|
@ -935,7 +943,7 @@ impl std::str::FromStr for BottomWidgetType {
|
|||
"empty" => Ok(BottomWidgetType::Empty),
|
||||
"battery" | "batt" => Ok(BottomWidgetType::Battery),
|
||||
_ => Err(BottomError::ConfigError(format!(
|
||||
"invalid widget type: {}",
|
||||
"invalid widget type: {}", // FIXME: Make this more helpful, specify valid widget types (just go through the list)
|
||||
s
|
||||
))),
|
||||
}
|
||||
|
|
|
@ -730,6 +730,10 @@ pub struct BasicTableWidgetState {
|
|||
pub currently_displayed_widget_type: BottomWidgetType,
|
||||
pub currently_displayed_widget_id: u64,
|
||||
pub widget_id: i64,
|
||||
pub left_tlc: Option<(u16, u16)>,
|
||||
pub left_brc: Option<(u16, u16)>,
|
||||
pub right_tlc: Option<(u16, u16)>,
|
||||
pub right_brc: Option<(u16, u16)>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
|
@ -47,7 +47,11 @@ fn main() -> error::Result<()> {
|
|||
)?;
|
||||
|
||||
// Create painter and set colours.
|
||||
let mut painter = canvas::Painter::init(widget_layout, app.app_config_fields.table_gap);
|
||||
let mut painter = canvas::Painter::init(
|
||||
widget_layout,
|
||||
app.app_config_fields.table_gap,
|
||||
app.app_config_fields.use_basic_mode,
|
||||
);
|
||||
generate_config_colours(&config, &mut painter)?;
|
||||
painter.colours.generate_remaining_cpu_colours();
|
||||
painter.complete_painter_init();
|
||||
|
|
|
@ -68,10 +68,11 @@ pub struct Painter {
|
|||
widget_layout: BottomLayout,
|
||||
derived_widget_draw_locs: Vec<Vec<Vec<Vec<Rect>>>>,
|
||||
table_height_offset: u16,
|
||||
requires_boundary_recalculation: bool,
|
||||
}
|
||||
|
||||
impl Painter {
|
||||
pub fn init(widget_layout: BottomLayout, table_gap: u16) -> Self {
|
||||
pub fn init(widget_layout: BottomLayout, table_gap: u16, is_basic_mode: bool) -> Self {
|
||||
// Now for modularity; we have to also initialize the base layouts!
|
||||
// We want to do this ONCE and reuse; after this we can just construct
|
||||
// based on the console size.
|
||||
|
@ -151,7 +152,8 @@ impl Painter {
|
|||
layout_constraints,
|
||||
widget_layout,
|
||||
derived_widget_draw_locs: Vec::default(),
|
||||
table_height_offset: 4 + table_gap,
|
||||
table_height_offset: if is_basic_mode { 2 } else { 4 } + table_gap,
|
||||
requires_boundary_recalculation: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,6 +210,14 @@ impl Painter {
|
|||
self.width = current_width;
|
||||
}
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// If we're force drawing, reset ALL mouse boundaries.
|
||||
for widget in app_state.widget_map.values_mut() {
|
||||
widget.top_left_corner = None;
|
||||
widget.bottom_right_corner = None;
|
||||
}
|
||||
}
|
||||
|
||||
terminal.autoresize()?;
|
||||
terminal.draw(|mut f| {
|
||||
if app_state.help_dialog_state.is_showing_help {
|
||||
|
@ -398,6 +408,14 @@ impl Painter {
|
|||
} else {
|
||||
1
|
||||
});
|
||||
|
||||
// A little hack to force the widget boundary recalculation. This is required here
|
||||
// as basic mode has a height of 0 initially, which breaks things.
|
||||
if self.requires_boundary_recalculation {
|
||||
app_state.is_determining_widget_boundary = true;
|
||||
}
|
||||
self.requires_boundary_recalculation = cpu_height == 0;
|
||||
|
||||
let vertical_chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints(
|
||||
|
@ -419,18 +437,11 @@ impl Painter {
|
|||
self.draw_basic_cpu(&mut f, app_state, vertical_chunks[0], 1);
|
||||
self.draw_basic_memory(&mut f, app_state, middle_chunks[0], 2);
|
||||
self.draw_basic_network(&mut f, app_state, middle_chunks[1], 3);
|
||||
|
||||
let mut later_widget_id: Option<u64> = None;
|
||||
if let Some(basic_table_widget_state) = &app_state.basic_table_widget_state {
|
||||
let widget_id = basic_table_widget_state.currently_displayed_widget_id;
|
||||
|
||||
if let Some(current_table) = app_state.widget_map.get(&widget_id) {
|
||||
self.draw_basic_table_arrows(
|
||||
&mut f,
|
||||
app_state,
|
||||
vertical_chunks[3],
|
||||
current_table,
|
||||
);
|
||||
}
|
||||
|
||||
later_widget_id = Some(widget_id);
|
||||
match basic_table_widget_state.currently_displayed_widget_type {
|
||||
Disk => self.draw_disk_table(
|
||||
&mut f,
|
||||
|
@ -442,6 +453,7 @@ impl Painter {
|
|||
Proc | ProcSort => {
|
||||
let wid = widget_id
|
||||
- match basic_table_widget_state.currently_displayed_widget_type {
|
||||
ProcSearch => 1,
|
||||
ProcSort => 2,
|
||||
_ => 0,
|
||||
};
|
||||
|
@ -470,6 +482,10 @@ impl Painter {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(widget_id) = later_widget_id {
|
||||
self.draw_basic_table_arrows(&mut f, app_state, vertical_chunks[3], widget_id);
|
||||
}
|
||||
} else {
|
||||
// Draws using the passed in (or default) layout. NOT basic so far.
|
||||
if self.derived_widget_draw_locs.is_empty() || app_state.is_force_redraw {
|
||||
|
@ -570,6 +586,7 @@ impl Painter {
|
|||
})?;
|
||||
|
||||
app_state.is_force_redraw = false;
|
||||
app_state.is_determining_widget_boundary = false;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ impl HelpDialog for Painter {
|
|||
"─".repeat(usize::from(draw_loc.width).saturating_sub(HELP_BASE.chars().count() + 2))
|
||||
);
|
||||
|
||||
if app_state.is_force_redraw {
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// We must also recalculate how many lines are wrapping to properly get scrolling to work on
|
||||
// small terminal sizes... oh joy.
|
||||
|
||||
|
|
|
@ -1,116 +1,149 @@
|
|||
use crate::{
|
||||
app::{
|
||||
layout_manager::{BottomWidget, BottomWidgetType},
|
||||
App,
|
||||
},
|
||||
app::{layout_manager::BottomWidgetType, App},
|
||||
canvas::Painter,
|
||||
};
|
||||
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Constraint, Layout, Rect},
|
||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||
terminal::Frame,
|
||||
widgets::{Block, Paragraph, Text},
|
||||
};
|
||||
|
||||
pub trait BasicTableArrows {
|
||||
fn draw_basic_table_arrows<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &App, draw_loc: Rect, current_table: &BottomWidget,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
);
|
||||
}
|
||||
|
||||
impl BasicTableArrows for Painter {
|
||||
fn draw_basic_table_arrows<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &App, draw_loc: Rect, current_table: &BottomWidget,
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
let current_table = if let BottomWidgetType::ProcSort = current_table.widget_type {
|
||||
current_table
|
||||
.right_neighbour
|
||||
.map(|id| app_state.widget_map.get(&id).unwrap())
|
||||
.unwrap()
|
||||
} else {
|
||||
current_table
|
||||
};
|
||||
|
||||
// Effectively a paragraph with a ton of spacing
|
||||
let (left_table, right_table) = (
|
||||
{
|
||||
current_table
|
||||
.left_neighbour
|
||||
.map(|left_widget_id| {
|
||||
app_state
|
||||
.widget_map
|
||||
.get(&left_widget_id)
|
||||
.map(|left_widget| {
|
||||
if left_widget.widget_type == BottomWidgetType::ProcSort {
|
||||
left_widget
|
||||
.left_neighbour
|
||||
.map(|left_left_widget_id| {
|
||||
app_state.widget_map.get(&left_left_widget_id).map(
|
||||
|left_left_widget| &left_left_widget.widget_type,
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| Some(&BottomWidgetType::Temp))
|
||||
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
||||
} else {
|
||||
&left_widget.widget_type
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
||||
})
|
||||
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
||||
},
|
||||
{
|
||||
if let Some(current_table) = app_state.widget_map.get(&widget_id) {
|
||||
let current_table = if let BottomWidgetType::ProcSort = current_table.widget_type {
|
||||
current_table
|
||||
.right_neighbour
|
||||
.map(|right_widget_id| {
|
||||
app_state
|
||||
.widget_map
|
||||
.get(&right_widget_id)
|
||||
.map(|right_widget| {
|
||||
if right_widget.widget_type == BottomWidgetType::ProcSort {
|
||||
right_widget
|
||||
.right_neighbour
|
||||
.map(|right_right_widget_id| {
|
||||
app_state.widget_map.get(&right_right_widget_id).map(
|
||||
|right_right_widget| {
|
||||
&right_right_widget.widget_type
|
||||
},
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| Some(&BottomWidgetType::Disk))
|
||||
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
||||
} else {
|
||||
&right_widget.widget_type
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
||||
})
|
||||
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
||||
},
|
||||
);
|
||||
.map(|id| app_state.widget_map.get(&id).unwrap())
|
||||
.unwrap()
|
||||
} else {
|
||||
current_table
|
||||
};
|
||||
|
||||
let left_name = left_table.get_pretty_name();
|
||||
let right_name = right_table.get_pretty_name();
|
||||
let (left_table, right_table) = (
|
||||
{
|
||||
current_table
|
||||
.left_neighbour
|
||||
.map(|left_widget_id| {
|
||||
app_state
|
||||
.widget_map
|
||||
.get(&left_widget_id)
|
||||
.map(|left_widget| {
|
||||
if left_widget.widget_type == BottomWidgetType::ProcSort {
|
||||
left_widget
|
||||
.left_neighbour
|
||||
.map(|left_left_widget_id| {
|
||||
app_state.widget_map.get(&left_left_widget_id).map(
|
||||
|left_left_widget| {
|
||||
&left_left_widget.widget_type
|
||||
},
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| Some(&BottomWidgetType::Temp))
|
||||
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
||||
} else {
|
||||
&left_widget.widget_type
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
||||
})
|
||||
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
||||
},
|
||||
{
|
||||
current_table
|
||||
.right_neighbour
|
||||
.map(|right_widget_id| {
|
||||
app_state
|
||||
.widget_map
|
||||
.get(&right_widget_id)
|
||||
.map(|right_widget| {
|
||||
if right_widget.widget_type == BottomWidgetType::ProcSort {
|
||||
right_widget
|
||||
.right_neighbour
|
||||
.map(|right_right_widget_id| {
|
||||
app_state
|
||||
.widget_map
|
||||
.get(&right_right_widget_id)
|
||||
.map(|right_right_widget| {
|
||||
&right_right_widget.widget_type
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| Some(&BottomWidgetType::Disk))
|
||||
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
||||
} else {
|
||||
&right_widget.widget_type
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
||||
})
|
||||
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
||||
},
|
||||
);
|
||||
|
||||
let num_spaces =
|
||||
usize::from(draw_loc.width).saturating_sub(6 + left_name.len() + right_name.len());
|
||||
let left_name = left_table.get_pretty_name();
|
||||
let right_name = right_table.get_pretty_name();
|
||||
|
||||
let arrow_text = vec![
|
||||
Text::raw("\n"),
|
||||
Text::styled(format!("◄ {}", left_name), self.colours.text_style),
|
||||
Text::raw(" ".repeat(num_spaces)),
|
||||
Text::styled(format!("{} ►", right_name), self.colours.text_style),
|
||||
];
|
||||
let num_spaces =
|
||||
usize::from(draw_loc.width).saturating_sub(6 + left_name.len() + right_name.len());
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(1)
|
||||
.split(draw_loc);
|
||||
let left_arrow_text = vec![
|
||||
Text::raw("\n"),
|
||||
Text::styled(format!("◄ {}", left_name), self.colours.text_style),
|
||||
];
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(arrow_text.iter()).block(Block::default()),
|
||||
margined_draw_loc[0],
|
||||
);
|
||||
let right_arrow_text = vec![
|
||||
Text::raw("\n"),
|
||||
Text::styled(format!("{} ►", right_name), self.colours.text_style),
|
||||
];
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Length(2 + left_name.len() as u16),
|
||||
Constraint::Length(num_spaces as u16),
|
||||
Constraint::Length(2 + right_name.len() as u16),
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.horizontal_margin(1)
|
||||
.split(draw_loc);
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(left_arrow_text.iter()).block(Block::default()),
|
||||
margined_draw_loc[0],
|
||||
);
|
||||
f.render_widget(
|
||||
Paragraph::new(right_arrow_text.iter())
|
||||
.block(Block::default())
|
||||
.alignment(Alignment::Right),
|
||||
margined_draw_loc[2],
|
||||
);
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
if let Some(basic_table) = &mut app_state.basic_table_widget_state {
|
||||
basic_table.left_tlc = Some((margined_draw_loc[0].x, margined_draw_loc[0].y));
|
||||
basic_table.left_brc = Some((
|
||||
margined_draw_loc[0].x + margined_draw_loc[0].width,
|
||||
margined_draw_loc[0].y + margined_draw_loc[0].height,
|
||||
));
|
||||
basic_table.right_tlc = Some((margined_draw_loc[2].x, margined_draw_loc[2].y));
|
||||
basic_table.right_brc = Some((
|
||||
margined_draw_loc[2].x + margined_draw_loc[2].width,
|
||||
margined_draw_loc[2].y + margined_draw_loc[2].height,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Constraint, Rect},
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
terminal::Frame,
|
||||
widgets::{Block, Borders, Paragraph, Row, Table, Tabs, Text},
|
||||
};
|
||||
|
@ -109,6 +109,12 @@ impl BatteryDisplayWidget for Painter {
|
|||
// draw_loc,
|
||||
// );
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc)[0];
|
||||
|
||||
if let Some(battery_details) = app_state
|
||||
.canvas_data
|
||||
.battery_data
|
||||
|
@ -166,7 +172,7 @@ impl BatteryDisplayWidget for Painter {
|
|||
.block(battery_block)
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()),
|
||||
draw_loc,
|
||||
margined_draw_loc,
|
||||
);
|
||||
} else {
|
||||
f.render_widget(
|
||||
|
@ -178,7 +184,7 @@ impl BatteryDisplayWidget for Painter {
|
|||
.iter(),
|
||||
)
|
||||
.block(battery_block),
|
||||
draw_loc,
|
||||
margined_draw_loc,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -209,6 +215,17 @@ impl BatteryDisplayWidget for Painter {
|
|||
.select(battery_widget_state.currently_selected_battery_index),
|
||||
draw_loc,
|
||||
);
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// Update draw loc in widget map
|
||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y));
|
||||
widget.bottom_right_corner = Some((
|
||||
margined_draw_loc.x + margined_draw_loc.width,
|
||||
margined_draw_loc.y + margined_draw_loc.height,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,14 +127,23 @@ impl CpuBasicWidget for Painter {
|
|||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(1)
|
||||
.split(*chunk);
|
||||
.split(*chunk)[0];
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(cpu_column.iter()).block(Block::default()),
|
||||
margined_loc[0],
|
||||
margined_loc,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// Update draw loc in widget map
|
||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
||||
widget.bottom_right_corner =
|
||||
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,15 @@ impl CpuGraphWidget for Painter {
|
|||
if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&widget_id) {
|
||||
cpu_widget_state.is_legend_hidden = true;
|
||||
}
|
||||
|
||||
// Update draw loc in widget map
|
||||
if app_state.should_get_widget_bounds() {
|
||||
if let Some(bottom_widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
bottom_widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
||||
bottom_widget.bottom_right_corner =
|
||||
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let (graph_index, legend_index, constraints) =
|
||||
if app_state.app_config_fields.left_legend {
|
||||
|
@ -94,6 +103,35 @@ impl CpuGraphWidget for Painter {
|
|||
partitioned_draw_loc[legend_index],
|
||||
widget_id + 1,
|
||||
);
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// Update draw loc in widget map
|
||||
if let Some(cpu_widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
cpu_widget.top_left_corner = Some((
|
||||
partitioned_draw_loc[graph_index].x,
|
||||
partitioned_draw_loc[graph_index].y,
|
||||
));
|
||||
cpu_widget.bottom_right_corner = Some((
|
||||
partitioned_draw_loc[graph_index].x
|
||||
+ partitioned_draw_loc[graph_index].width,
|
||||
partitioned_draw_loc[graph_index].y
|
||||
+ partitioned_draw_loc[graph_index].height,
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(legend_widget) = app_state.widget_map.get_mut(&(widget_id + 1)) {
|
||||
legend_widget.top_left_corner = Some((
|
||||
partitioned_draw_loc[legend_index].x,
|
||||
partitioned_draw_loc[legend_index].y,
|
||||
));
|
||||
legend_widget.bottom_right_corner = Some((
|
||||
partitioned_draw_loc[legend_index].x
|
||||
+ partitioned_draw_loc[legend_index].width,
|
||||
partitioned_draw_loc[legend_index].y
|
||||
+ partitioned_draw_loc[legend_index].height,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,20 +277,35 @@ impl CpuGraphWidget for Painter {
|
|||
{
|
||||
cpu_widget_state.is_legend_hidden = false;
|
||||
let cpu_data: &mut [ConvertedCpuData] = &mut app_state.canvas_data.cpu_data;
|
||||
|
||||
let cpu_table_state = &mut cpu_widget_state.scroll_state.table_state;
|
||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||
0
|
||||
} else {
|
||||
app_state.app_config_fields.table_gap
|
||||
};
|
||||
let start_position = get_start_position(
|
||||
usize::from(draw_loc.height.saturating_sub(self.table_height_offset)),
|
||||
usize::from(
|
||||
(draw_loc.height + (1 - table_gap)).saturating_sub(self.table_height_offset),
|
||||
),
|
||||
&cpu_widget_state.scroll_state.scroll_direction,
|
||||
&mut cpu_widget_state.scroll_state.previous_scroll_position,
|
||||
cpu_widget_state.scroll_state.current_scroll_position,
|
||||
app_state.is_force_redraw,
|
||||
);
|
||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||
cpu_table_state.select(Some(
|
||||
cpu_widget_state
|
||||
.scroll_state
|
||||
.current_scroll_position
|
||||
.saturating_sub(start_position),
|
||||
));
|
||||
|
||||
let sliced_cpu_data = &cpu_data[start_position..];
|
||||
|
||||
let mut offset_scroll_index =
|
||||
cpu_widget_state.scroll_state.current_scroll_position - start_position;
|
||||
let mut offset_scroll_index = cpu_widget_state
|
||||
.scroll_state
|
||||
.current_scroll_position
|
||||
.saturating_sub(start_position);
|
||||
let show_avg_cpu = app_state.app_config_fields.show_average_cpu;
|
||||
|
||||
let cpu_rows = sliced_cpu_data.iter().enumerate().filter_map(|(itx, cpu)| {
|
||||
|
@ -306,7 +359,7 @@ impl CpuGraphWidget for Painter {
|
|||
};
|
||||
|
||||
// Draw
|
||||
f.render_widget(
|
||||
f.render_stateful_widget(
|
||||
Table::new(CPU_LEGEND_HEADER.iter(), cpu_rows)
|
||||
.block(
|
||||
Block::default()
|
||||
|
@ -321,8 +374,9 @@ impl CpuGraphWidget for Painter {
|
|||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.header_gap(app_state.app_config_fields.table_gap),
|
||||
.header_gap(table_gap),
|
||||
draw_loc,
|
||||
cpu_table_state,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,8 +39,15 @@ impl DiskTableWidget for Painter {
|
|||
) {
|
||||
if let Some(disk_widget_state) = app_state.disk_state.widget_states.get_mut(&widget_id) {
|
||||
let disk_data: &mut [Vec<String>] = &mut app_state.canvas_data.disk_data;
|
||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||
0
|
||||
} else {
|
||||
app_state.app_config_fields.table_gap
|
||||
};
|
||||
let start_position = get_start_position(
|
||||
usize::from(draw_loc.height.saturating_sub(self.table_height_offset)),
|
||||
usize::from(
|
||||
(draw_loc.height + (1 - table_gap)).saturating_sub(self.table_height_offset),
|
||||
),
|
||||
&disk_widget_state.scroll_state.scroll_direction,
|
||||
&mut disk_widget_state.scroll_state.previous_scroll_position,
|
||||
disk_widget_state.scroll_state.current_scroll_position,
|
||||
|
@ -49,15 +56,13 @@ impl DiskTableWidget for Painter {
|
|||
let is_on_widget = app_state.current_widget.widget_id == widget_id;
|
||||
let disk_table_state = &mut disk_widget_state.scroll_state.table_state;
|
||||
disk_table_state.select(Some(
|
||||
disk_widget_state.scroll_state.current_scroll_position - start_position,
|
||||
disk_widget_state
|
||||
.scroll_state
|
||||
.current_scroll_position
|
||||
.saturating_sub(start_position),
|
||||
));
|
||||
let sliced_vec = &mut disk_data[start_position..];
|
||||
let disk_rows = sliced_vec.iter().map(|disk| Row::Data(disk.iter()));
|
||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||
0
|
||||
} else {
|
||||
app_state.app_config_fields.table_gap
|
||||
};
|
||||
|
||||
// Calculate widths
|
||||
// TODO: [PRETTY] Ellipsis on strings?
|
||||
|
@ -133,7 +138,7 @@ impl DiskTableWidget for Painter {
|
|||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
.split(draw_loc)[0];
|
||||
|
||||
// Draw!
|
||||
f.render_stateful_widget(
|
||||
|
@ -149,9 +154,20 @@ impl DiskTableWidget for Painter {
|
|||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.header_gap(table_gap),
|
||||
margined_draw_loc[0],
|
||||
margined_draw_loc,
|
||||
disk_table_state,
|
||||
);
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// Update draw loc in widget map
|
||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y));
|
||||
widget.bottom_right_corner = Some((
|
||||
margined_draw_loc.x + margined_draw_loc.width,
|
||||
margined_draw_loc.y + margined_draw_loc.height,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,5 +115,14 @@ impl MemBasicWidget for Painter {
|
|||
Paragraph::new(mem_text.iter()).block(Block::default()),
|
||||
margined_loc[0],
|
||||
);
|
||||
|
||||
// Update draw loc in widget map
|
||||
if app_state.should_get_widget_bounds() {
|
||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
||||
widget.bottom_right_corner =
|
||||
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,5 +133,14 @@ impl MemGraphWidget for Painter {
|
|||
draw_loc,
|
||||
);
|
||||
}
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// Update draw loc in widget map
|
||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
||||
widget.bottom_right_corner =
|
||||
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,5 +67,14 @@ impl NetworkBasicWidget for Painter {
|
|||
Paragraph::new(total_net_text.iter()).block(Block::default()),
|
||||
total_loc[0],
|
||||
);
|
||||
|
||||
// Update draw loc in widget map
|
||||
if app_state.should_get_widget_bounds() {
|
||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
||||
widget.bottom_right_corner =
|
||||
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,17 @@ impl NetworkGraphWidget for Painter {
|
|||
} else {
|
||||
self.draw_network_graph(f, app_state, draw_loc, widget_id, false);
|
||||
}
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// Update draw loc in widget map
|
||||
// Note that in both cases, we always go to the same widget id so it's fine to do it like
|
||||
// this lol.
|
||||
if let Some(network_widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
network_widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
||||
network_widget.bottom_right_corner =
|
||||
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_network_graph<B: Backend>(
|
||||
|
@ -93,12 +104,8 @@ impl NetworkGraphWidget for Painter {
|
|||
}
|
||||
}
|
||||
|
||||
// Main idea is that we have some "limits" --- if we're, say, under a logged kibibyte,
|
||||
// then we are just gonna set the cap at a kibibyte.
|
||||
// For gibi/giga and beyond, we instead start going up by 1 rather than jumping to a tera/tebi.
|
||||
// So, it would look a bit like:
|
||||
// - < Kibi => Kibi => Mebi => Gibi => 2 Gibi => ... => 999 Gibi => Tebi => 2 Tebi => ...
|
||||
|
||||
// FIXME [NETWORKING]: Do ya think it would be possible for a more granular approach?
|
||||
// Currently we do 32 -> 33... which skips some gigabit values
|
||||
let true_max_val: f64;
|
||||
let mut labels = vec![];
|
||||
if max_val_bytes < LOG_KIBI_LIMIT {
|
||||
|
|
|
@ -109,6 +109,65 @@ impl ProcessTableWidget for Painter {
|
|||
widget_id: u64,
|
||||
) {
|
||||
if let Some(proc_widget_state) = app_state.proc_state.widget_states.get_mut(&widget_id) {
|
||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc)[0];
|
||||
|
||||
let (border_and_title_style, highlight_style) = if is_on_widget {
|
||||
(
|
||||
self.colours.highlighted_border_style,
|
||||
self.colours.currently_selected_text_style,
|
||||
)
|
||||
} else {
|
||||
(self.colours.border_style, self.colours.text_style)
|
||||
};
|
||||
|
||||
let title = if draw_border {
|
||||
if app_state.is_expanded
|
||||
&& !proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.is_enabled
|
||||
&& !proc_widget_state.is_sort_open
|
||||
{
|
||||
const TITLE_BASE: &str = " Processes ── Esc to go back ";
|
||||
format!(
|
||||
" Processes ─{}─ Esc to go back ",
|
||||
"─".repeat(
|
||||
usize::from(draw_loc.width)
|
||||
.saturating_sub(TITLE_BASE.chars().count() + 2)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
" Processes ".to_string()
|
||||
}
|
||||
} else {
|
||||
String::default()
|
||||
};
|
||||
|
||||
let title_style = if app_state.is_expanded {
|
||||
border_and_title_style
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
};
|
||||
|
||||
let process_block = if draw_border {
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(title_style)
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_and_title_style)
|
||||
} else if is_on_widget {
|
||||
Block::default()
|
||||
.borders(*SIDE_BORDERS)
|
||||
.border_style(self.colours.highlighted_border_style)
|
||||
} else {
|
||||
Block::default().borders(Borders::NONE)
|
||||
};
|
||||
|
||||
if let Some(process_data) = &app_state
|
||||
.canvas_data
|
||||
.finalized_process_data_map
|
||||
|
@ -122,10 +181,17 @@ impl ProcessTableWidget for Painter {
|
|||
// hit the process we've currently scrolled to.
|
||||
// We also need to move the list - we can
|
||||
// do so by hiding some elements!
|
||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||
|
||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||
0
|
||||
} else {
|
||||
app_state.app_config_fields.table_gap
|
||||
};
|
||||
let position = get_start_position(
|
||||
usize::from(draw_loc.height.saturating_sub(self.table_height_offset)),
|
||||
usize::from(
|
||||
(draw_loc.height + (1 - table_gap))
|
||||
.saturating_sub(self.table_height_offset),
|
||||
),
|
||||
&proc_widget_state.scroll_state.scroll_direction,
|
||||
&mut proc_widget_state.scroll_state.previous_scroll_position,
|
||||
proc_widget_state.scroll_state.current_scroll_position,
|
||||
|
@ -142,13 +208,11 @@ impl ProcessTableWidget for Painter {
|
|||
let sliced_vec = &process_data[start_position..];
|
||||
let proc_table_state = &mut proc_widget_state.scroll_state.table_state;
|
||||
proc_table_state.select(Some(
|
||||
proc_widget_state.scroll_state.current_scroll_position - start_position,
|
||||
proc_widget_state
|
||||
.scroll_state
|
||||
.current_scroll_position
|
||||
.saturating_sub(start_position),
|
||||
));
|
||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||
0
|
||||
} else {
|
||||
app_state.app_config_fields.table_gap
|
||||
};
|
||||
|
||||
// Draw!
|
||||
let is_proc_widget_grouped = proc_widget_state.is_grouped;
|
||||
|
@ -216,64 +280,6 @@ impl ProcessTableWidget for Painter {
|
|||
let intrinsic_widths =
|
||||
&(variable_intrinsic_results.0)[0..variable_intrinsic_results.1];
|
||||
|
||||
let (border_and_title_style, highlight_style) = if is_on_widget {
|
||||
(
|
||||
self.colours.highlighted_border_style,
|
||||
self.colours.currently_selected_text_style,
|
||||
)
|
||||
} else {
|
||||
(self.colours.border_style, self.colours.text_style)
|
||||
};
|
||||
|
||||
let title = if draw_border {
|
||||
if app_state.is_expanded
|
||||
&& !proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.is_enabled
|
||||
&& !proc_widget_state.is_sort_open
|
||||
{
|
||||
const TITLE_BASE: &str = " Processes ── Esc to go back ";
|
||||
format!(
|
||||
" Processes ─{}─ Esc to go back ",
|
||||
"─".repeat(
|
||||
usize::from(draw_loc.width)
|
||||
.saturating_sub(TITLE_BASE.chars().count() + 2)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
" Processes ".to_string()
|
||||
}
|
||||
} else {
|
||||
String::default()
|
||||
};
|
||||
|
||||
let title_style = if app_state.is_expanded {
|
||||
border_and_title_style
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
};
|
||||
|
||||
let process_block = if draw_border {
|
||||
Block::default()
|
||||
.title(&title)
|
||||
.title_style(title_style)
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_and_title_style)
|
||||
} else if is_on_widget {
|
||||
Block::default()
|
||||
.borders(*SIDE_BORDERS)
|
||||
.border_style(self.colours.highlighted_border_style)
|
||||
} else {
|
||||
Block::default().borders(Borders::NONE)
|
||||
};
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
|
||||
f.render_stateful_widget(
|
||||
Table::new(process_headers.iter(), process_rows)
|
||||
.block(process_block)
|
||||
|
@ -289,9 +295,22 @@ impl ProcessTableWidget for Painter {
|
|||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.header_gap(table_gap),
|
||||
margined_draw_loc[0],
|
||||
margined_draw_loc,
|
||||
proc_table_state,
|
||||
);
|
||||
} else {
|
||||
f.render_widget(process_block, margined_draw_loc);
|
||||
}
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// Update draw loc in widget map
|
||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y));
|
||||
widget.bottom_right_corner = Some((
|
||||
margined_draw_loc.x + margined_draw_loc.width,
|
||||
margined_draw_loc.y + margined_draw_loc.height,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -497,15 +516,26 @@ impl ProcessTableWidget for Painter {
|
|||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
.split(draw_loc)[0];
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(search_text.iter())
|
||||
.block(process_search_block)
|
||||
.style(self.colours.text_style)
|
||||
.alignment(Alignment::Left),
|
||||
margined_draw_loc[0],
|
||||
margined_draw_loc,
|
||||
);
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// Update draw loc in widget map
|
||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y));
|
||||
widget.bottom_right_corner = Some((
|
||||
margined_draw_loc.x + margined_draw_loc.width,
|
||||
margined_draw_loc.y + margined_draw_loc.height,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -531,21 +561,18 @@ impl ProcessTableWidget for Painter {
|
|||
.unwrap()
|
||||
.enabled
|
||||
})
|
||||
.enumerate()
|
||||
.map(|(itx, column_type)| {
|
||||
if current_scroll_position == itx {
|
||||
(
|
||||
column_type.to_string(),
|
||||
self.colours.currently_selected_text_style,
|
||||
)
|
||||
} else {
|
||||
(column_type.to_string(), self.colours.text_style)
|
||||
}
|
||||
})
|
||||
.map(|column_type| column_type.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||
0
|
||||
} else {
|
||||
app_state.app_config_fields.table_gap
|
||||
};
|
||||
let position = get_start_position(
|
||||
usize::from(draw_loc.height.saturating_sub(self.table_height_offset)),
|
||||
usize::from(
|
||||
(draw_loc.height + (1 - table_gap)).saturating_sub(self.table_height_offset),
|
||||
),
|
||||
&proc_widget_state.columns.scroll_direction,
|
||||
&mut proc_widget_state.columns.previous_scroll_position,
|
||||
current_scroll_position,
|
||||
|
@ -563,9 +590,15 @@ impl ProcessTableWidget for Painter {
|
|||
|
||||
let sort_options = sliced_vec
|
||||
.iter()
|
||||
.map(|(column, style)| Row::StyledData(vec![column].into_iter(), *style));
|
||||
.map(|column| Row::Data(vec![column].into_iter()));
|
||||
|
||||
let column_state = &mut proc_widget_state.columns.column_state;
|
||||
column_state.select(Some(
|
||||
proc_widget_state
|
||||
.columns
|
||||
.current_scroll_position
|
||||
.saturating_sub(start_position),
|
||||
));
|
||||
let current_border_style = if proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
|
@ -590,21 +623,40 @@ impl ProcessTableWidget for Painter {
|
|||
Block::default().borders(Borders::NONE)
|
||||
};
|
||||
|
||||
let highlight_style = if is_on_widget {
|
||||
self.colours.currently_selected_text_style
|
||||
} else {
|
||||
self.colours.text_style
|
||||
};
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
.split(draw_loc)[0];
|
||||
|
||||
f.render_stateful_widget(
|
||||
Table::new(["Sort By"].iter(), sort_options)
|
||||
.block(process_sort_block)
|
||||
.highlight_style(highlight_style)
|
||||
.style(self.colours.text_style)
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths(&[Constraint::Percentage(100)])
|
||||
.header_gap(1),
|
||||
margined_draw_loc[0],
|
||||
.header_gap(table_gap),
|
||||
margined_draw_loc,
|
||||
column_state,
|
||||
);
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// Update draw loc in widget map
|
||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y));
|
||||
widget.bottom_right_corner = Some((
|
||||
margined_draw_loc.x + margined_draw_loc.width,
|
||||
margined_draw_loc.y + margined_draw_loc.height,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,8 +40,15 @@ impl TempTableWidget for Painter {
|
|||
if let Some(temp_widget_state) = app_state.temp_state.widget_states.get_mut(&widget_id) {
|
||||
let temp_sensor_data: &mut [Vec<String>] = &mut app_state.canvas_data.temp_sensor_data;
|
||||
|
||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||
0
|
||||
} else {
|
||||
app_state.app_config_fields.table_gap
|
||||
};
|
||||
let start_position = get_start_position(
|
||||
usize::from(draw_loc.height.saturating_sub(self.table_height_offset)),
|
||||
usize::from(
|
||||
(draw_loc.height + (1 - table_gap)).saturating_sub(self.table_height_offset),
|
||||
),
|
||||
&temp_widget_state.scroll_state.scroll_direction,
|
||||
&mut temp_widget_state.scroll_state.previous_scroll_position,
|
||||
temp_widget_state.scroll_state.current_scroll_position,
|
||||
|
@ -50,15 +57,13 @@ impl TempTableWidget for Painter {
|
|||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||
let temp_table_state = &mut temp_widget_state.scroll_state.table_state;
|
||||
temp_table_state.select(Some(
|
||||
temp_widget_state.scroll_state.current_scroll_position - start_position,
|
||||
temp_widget_state
|
||||
.scroll_state
|
||||
.current_scroll_position
|
||||
.saturating_sub(start_position),
|
||||
));
|
||||
let sliced_vec = &temp_sensor_data[start_position..];
|
||||
let temperature_rows = sliced_vec.iter().map(|temp_row| Row::Data(temp_row.iter()));
|
||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||
0
|
||||
} else {
|
||||
app_state.app_config_fields.table_gap
|
||||
};
|
||||
|
||||
// Calculate widths
|
||||
let width = f64::from(draw_loc.width);
|
||||
|
@ -113,7 +118,7 @@ impl TempTableWidget for Painter {
|
|||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc);
|
||||
.split(draw_loc)[0];
|
||||
|
||||
// Draw
|
||||
f.render_stateful_widget(
|
||||
|
@ -129,9 +134,21 @@ impl TempTableWidget for Painter {
|
|||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.header_gap(table_gap),
|
||||
margined_draw_loc[0],
|
||||
margined_draw_loc,
|
||||
temp_table_state,
|
||||
);
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// Update draw loc in widget map
|
||||
// Note there is no difference between this and using draw_loc, but I'm too lazy to fix it.
|
||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||
widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y));
|
||||
widget.bottom_right_corner = Some((
|
||||
margined_draw_loc.x + margined_draw_loc.width,
|
||||
margined_draw_loc.y + margined_draw_loc.height,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
15
src/lib.rs
15
src/lib.rs
|
@ -85,6 +85,7 @@ pub fn get_matches() -> clap::ArgMatches<'static> {
|
|||
(@arg USE_OLD_NETWORK_LEGEND: --use_old_network_legend "Use the older (pre-0.4) network widget legend.")
|
||||
(@arg HIDE_TABLE_GAP: --hide_table_gap "Hides the spacing between the table headers and entries.")
|
||||
(@arg BATTERY: --battery "Shows the battery widget in default or basic mode. No effect on custom layouts.")
|
||||
(@arg DISABLE_CLICK: --disable_click "Disables mouse clicks from interacting with the program.")
|
||||
)
|
||||
.get_matches()
|
||||
}
|
||||
|
@ -93,6 +94,20 @@ pub fn handle_mouse_event(event: MouseEvent, app: &mut App) {
|
|||
match event {
|
||||
MouseEvent::ScrollUp(_x, _y, _modifiers) => app.handle_scroll_up(),
|
||||
MouseEvent::ScrollDown(_x, _y, _modifiers) => app.handle_scroll_down(),
|
||||
MouseEvent::Down(button, x, y, _modifiers) => {
|
||||
// debug!("Button down: {:?}, x: {}, y: {}", button, x, y);
|
||||
|
||||
if !app.app_config_fields.disable_click {
|
||||
match button {
|
||||
crossterm::event::MouseButton::Left => {
|
||||
// Trigger left click widget activity
|
||||
app.left_mouse_click_movement(x, y);
|
||||
}
|
||||
crossterm::event::MouseButton::Right => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ pub struct ConfigFlags {
|
|||
pub use_old_network_legend: Option<bool>,
|
||||
pub hide_table_gap: Option<bool>,
|
||||
pub battery: Option<bool>,
|
||||
pub disable_click: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
|
@ -192,11 +193,19 @@ pub fn build_app(
|
|||
currently_displayed_widget_type: initial_widget_type,
|
||||
currently_displayed_widget_id: initial_widget_id,
|
||||
widget_id: 100,
|
||||
left_tlc: None,
|
||||
left_brc: None,
|
||||
right_tlc: None,
|
||||
right_brc: None,
|
||||
},
|
||||
_ => BasicTableWidgetState {
|
||||
currently_displayed_widget_type: Proc,
|
||||
currently_displayed_widget_id: DEFAULT_WIDGET_ID,
|
||||
widget_id: 100,
|
||||
left_tlc: None,
|
||||
left_brc: None,
|
||||
right_tlc: None,
|
||||
right_brc: None,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
|
@ -221,6 +230,7 @@ pub fn build_app(
|
|||
} else {
|
||||
1
|
||||
},
|
||||
disable_click: get_disable_click(matches, config),
|
||||
};
|
||||
|
||||
let used_widgets = UsedWidgets {
|
||||
|
@ -259,6 +269,7 @@ pub fn get_widget_layout(
|
|||
|
||||
let bottom_layout = if get_use_basic_mode(matches, config) {
|
||||
default_widget_id = DEFAULT_WIDGET_ID;
|
||||
|
||||
BottomLayout::init_basic_default(get_use_battery(matches, config))
|
||||
} else {
|
||||
let ref_row: Vec<Row>; // Required to handle reference
|
||||
|
@ -297,7 +308,7 @@ pub fn get_widget_layout(
|
|||
total_row_height_ratio: total_height_ratio,
|
||||
};
|
||||
|
||||
// Confirm that we have at least ONE widget - if not, error out!
|
||||
// Confirm that we have at least ONE widget left - if not, error out!
|
||||
if iter_id > 0 {
|
||||
ret_bottom_layout.get_movement_mappings();
|
||||
// debug!("Bottom layout: {:#?}", ret_bottom_layout);
|
||||
|
@ -489,9 +500,7 @@ pub fn get_app_grouping(matches: &clap::ArgMatches<'static>, config: &Config) ->
|
|||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(grouping) = flags.group_processes {
|
||||
if grouping {
|
||||
return true;
|
||||
}
|
||||
return grouping;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
@ -502,9 +511,7 @@ pub fn get_app_case_sensitive(matches: &clap::ArgMatches<'static>, config: &Conf
|
|||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(case_sensitive) = flags.case_sensitive {
|
||||
if case_sensitive {
|
||||
return true;
|
||||
}
|
||||
return case_sensitive;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
@ -515,9 +522,7 @@ pub fn get_app_match_whole_word(matches: &clap::ArgMatches<'static>, config: &Co
|
|||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(whole_word) = flags.whole_word {
|
||||
if whole_word {
|
||||
return true;
|
||||
}
|
||||
return whole_word;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
@ -528,9 +533,7 @@ pub fn get_app_use_regex(matches: &clap::ArgMatches<'static>, config: &Config) -
|
|||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(regex) = flags.regex {
|
||||
if regex {
|
||||
return true;
|
||||
}
|
||||
return regex;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
@ -541,9 +544,7 @@ fn get_hide_time(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
|||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(hide_time) = flags.hide_time {
|
||||
if hide_time {
|
||||
return true;
|
||||
}
|
||||
return hide_time;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
@ -554,9 +555,7 @@ fn get_autohide_time(matches: &clap::ArgMatches<'static>, config: &Config) -> bo
|
|||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(autohide_time) = flags.autohide_time {
|
||||
if autohide_time {
|
||||
return true;
|
||||
}
|
||||
return autohide_time;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -613,14 +612,23 @@ fn get_default_widget_and_count(
|
|||
}
|
||||
}
|
||||
|
||||
fn get_disable_click(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("DISABLE_CLICK") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(disable_click) = flags.disable_click {
|
||||
return disable_click;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_use_old_network_legend(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("USE_OLD_NETWORK_LEGEND") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(use_old_network_legend) = flags.use_old_network_legend {
|
||||
if use_old_network_legend {
|
||||
return true;
|
||||
}
|
||||
return use_old_network_legend;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
@ -631,9 +639,7 @@ pub fn get_hide_table_gap(matches: &clap::ArgMatches<'static>, config: &Config)
|
|||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(hide_table_gap) = flags.hide_table_gap {
|
||||
if hide_table_gap {
|
||||
return true;
|
||||
}
|
||||
return hide_table_gap;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
@ -644,9 +650,7 @@ pub fn get_use_battery(matches: &clap::ArgMatches<'static>, config: &Config) ->
|
|||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(battery) = flags.battery {
|
||||
if battery {
|
||||
return true;
|
||||
}
|
||||
return battery;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
|
9
tests/layout_movement_tests.rs
Normal file
9
tests/layout_movement_tests.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
// TODO: Test basic mode
|
||||
// #[test]
|
||||
// fn test_basic_mode() {
|
||||
// let ret_bottom_layout = BottomLayout::init_basic_default(false);
|
||||
// }
|
||||
|
||||
// TODO: Test moving around with procs and their hidden children.
|
||||
|
||||
// TODO: Test moving around with cpus if they get hidden.
|
Loading…
Add table
Reference in a new issue