mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-23 13:45:12 +02:00
Merge pull request #131 from ClementTsang/battery_flag
Add battery flag, fix bug, refactor
This commit is contained in:
commit
de3a1fb7c0
@ -9,11 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- [#58](https://github.com/ClementTsang/bottom/issues/58): I/O stats per process
|
- [#58](https://github.com/ClementTsang/bottom/issues/58): I/O stats per process.
|
||||||
|
|
||||||
- [#55](https://github.com/ClementTsang/bottom/issues/55): Battery monitoring widget
|
- [#55](https://github.com/ClementTsang/bottom/issues/55): Battery monitoring widget.
|
||||||
|
|
||||||
- [#114](https://github.com/ClementTsang/bottom/pull/114): Process state per process
|
- [#114](https://github.com/ClementTsang/bottom/pull/114): Process state per process.
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
@ -49,6 +49,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Fixed bug where a single empty row as a layout would crash without a proper warning.
|
- Fixed bug where a single empty row as a layout would crash without a proper warning.
|
||||||
The behaviour now errors out with a more helpful message.
|
The behaviour now errors out with a more helpful message.
|
||||||
|
|
||||||
|
- Fixed bug where empty widgets in layout would cause widget movement to not work properly when moving vertically.
|
||||||
|
|
||||||
### Development changes
|
### Development changes
|
||||||
|
|
||||||
- Switch to stateful widget style for tables.
|
- Switch to stateful widget style for tables.
|
||||||
|
13
README.md
13
README.md
@ -143,20 +143,21 @@ Run using `btm`.
|
|||||||
-s, --show_disabled_data Shows disabled CPU entries in the CPU legend
|
-s, --show_disabled_data Shows disabled CPU entries in the CPU legend
|
||||||
-b, --basic Enables basic mode, removing charts and condensing data
|
-b, --basic Enables basic mode, removing charts and condensing data
|
||||||
--autohide_time Automatically hide the time scaling in graphs after being shown for a brief moment when
|
--autohide_time Automatically hide the time scaling in graphs after being shown for a brief moment when
|
||||||
zoomed in/out. If time is disabled via --hide_time then this will have no effect.
|
zoomed in/out. If time is disabled via --hide_time then this will have no effect
|
||||||
--use_old_network_legend Use the older (pre-0.4) network legend which is separate from the network chart
|
--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
|
--hide_table_gap Hides the spacing between table headers and data
|
||||||
|
--battery Displays the battery widget for default and basic layouts
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```
|
```
|
||||||
-r, --rate <MS> Set the refresh rate in milliseconds [default: 1000]
|
-r, --rate <MS> Set the refresh rate in milliseconds [default: 1000]
|
||||||
-C, --config <PATH> Use the specified config file; if it does not exist it is automatically created
|
-C, --config <PATH> Use the specified config file; if it does not exist it is automatically created [default: see section on config files]
|
||||||
-t, --default_time_value <MS> Sets the default time interval for charts in milliseconds [default: 60000]
|
-t, --default_time_value <MS> Sets the default time interval for charts in milliseconds [default: 60000]
|
||||||
-d, --time_delta <MS> Sets the default amount each zoom in/out action changes by in milliseconds [default: 15000]
|
-d, --time_delta <MS> Sets the default amount each zoom in/out action changes by in milliseconds [default: 15000]
|
||||||
--default_widget_count <COUNT> Which number of the selected widget type to select, from left to right, top to bottom. Defaults to 1.
|
--default_widget_count <COUNT> Which number of the selected widget type to select, from left to right, top to bottom [default: 1]
|
||||||
--default_widget_type <TYPE> The default widget type to select by default.
|
--default_widget_type <TYPE> The default widget type to select by default [default: "process"]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Keybindings
|
### Keybindings
|
||||||
@ -418,8 +419,8 @@ and get the following CPU donut:
|
|||||||
### Battery
|
### Battery
|
||||||
|
|
||||||
You can get battery statistics (charge, time to fill/discharge, consumption in watts, and battery health) via the battery widget.
|
You can get battery statistics (charge, time to fill/discharge, consumption in watts, and battery health) via the battery widget.
|
||||||
Since this is only useful for devices like laptops, it is off by default. Currently, the only way to use it is to set it
|
|
||||||
as a widget via [layouts](#layout).
|
Since this is only useful for devices like laptops, it is off by default. You can either enable the widget in the default layout via the `--battery` flag, or by specifying the widget in a [layout](#layout).
|
||||||
|
|
||||||
So with this slightly silly layout:
|
So with this slightly silly layout:
|
||||||
|
|
||||||
|
447
src/app.rs
447
src/app.rs
@ -3,13 +3,12 @@ use std::{cmp::max, collections::HashMap, time::Instant};
|
|||||||
use unicode_segmentation::GraphemeCursor;
|
use unicode_segmentation::GraphemeCursor;
|
||||||
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||||
|
|
||||||
use tui::widgets::TableState;
|
|
||||||
|
|
||||||
use typed_builder::*;
|
use typed_builder::*;
|
||||||
|
|
||||||
use data_farmer::*;
|
use data_farmer::*;
|
||||||
use data_harvester::{processes, temperature};
|
use data_harvester::{processes, temperature};
|
||||||
use layout_manager::*;
|
use layout_manager::*;
|
||||||
|
pub use states::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
canvas, constants,
|
canvas, constants,
|
||||||
@ -20,60 +19,10 @@ pub mod data_farmer;
|
|||||||
pub mod data_harvester;
|
pub mod data_harvester;
|
||||||
pub mod layout_manager;
|
pub mod layout_manager;
|
||||||
mod process_killer;
|
mod process_killer;
|
||||||
|
pub mod states;
|
||||||
|
|
||||||
const MAX_SEARCH_LENGTH: usize = 200;
|
const MAX_SEARCH_LENGTH: usize = 200;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ScrollDirection {
|
|
||||||
// UP means scrolling up --- this usually DECREMENTS
|
|
||||||
UP,
|
|
||||||
// DOWN means scrolling down --- this usually INCREMENTS
|
|
||||||
DOWN,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ScrollDirection {
|
|
||||||
fn default() -> Self {
|
|
||||||
ScrollDirection::DOWN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum CursorDirection {
|
|
||||||
LEFT,
|
|
||||||
RIGHT,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// AppScrollWidgetState deals with fields for a scrollable app's current state.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct AppScrollWidgetState {
|
|
||||||
pub current_scroll_position: u64,
|
|
||||||
pub previous_scroll_position: u64,
|
|
||||||
pub scroll_direction: ScrollDirection,
|
|
||||||
pub table_state: TableState,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct AppDeleteDialogState {
|
|
||||||
pub is_showing_dd: bool,
|
|
||||||
pub is_on_yes: bool, // Defaults to "No"
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AppHelpDialogState {
|
|
||||||
pub is_showing_help: bool,
|
|
||||||
pub scroll_state: ParagraphScrollState,
|
|
||||||
pub index_shortcuts: Vec<u16>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AppHelpDialogState {
|
|
||||||
fn default() -> Self {
|
|
||||||
AppHelpDialogState {
|
|
||||||
is_showing_help: false,
|
|
||||||
scroll_state: ParagraphScrollState::default(),
|
|
||||||
index_shortcuts: vec![0; constants::HELP_TEXT.len()],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// AppConfigFields is meant to cover basic fields that would normally be set
|
/// AppConfigFields is meant to cover basic fields that would normally be set
|
||||||
/// by config files or launch options.
|
/// by config files or launch options.
|
||||||
pub struct AppConfigFields {
|
pub struct AppConfigFields {
|
||||||
@ -93,396 +42,6 @@ pub struct AppConfigFields {
|
|||||||
pub table_gap: u16,
|
pub table_gap: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// AppSearchState deals with generic searching (I might do this in the future).
|
|
||||||
pub struct AppSearchState {
|
|
||||||
pub is_enabled: bool,
|
|
||||||
pub current_search_query: String,
|
|
||||||
pub current_regex: Option<std::result::Result<regex::Regex, regex::Error>>,
|
|
||||||
pub is_blank_search: bool,
|
|
||||||
pub is_invalid_search: bool,
|
|
||||||
pub grapheme_cursor: GraphemeCursor,
|
|
||||||
pub cursor_direction: CursorDirection,
|
|
||||||
pub cursor_bar: usize,
|
|
||||||
/// This represents the position in terms of CHARACTERS, not graphemes
|
|
||||||
pub char_cursor_position: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AppSearchState {
|
|
||||||
fn default() -> Self {
|
|
||||||
AppSearchState {
|
|
||||||
is_enabled: false,
|
|
||||||
current_search_query: String::default(),
|
|
||||||
current_regex: None,
|
|
||||||
is_invalid_search: false,
|
|
||||||
is_blank_search: true,
|
|
||||||
grapheme_cursor: GraphemeCursor::new(0, 0, true),
|
|
||||||
cursor_direction: CursorDirection::RIGHT,
|
|
||||||
cursor_bar: 0,
|
|
||||||
char_cursor_position: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppSearchState {
|
|
||||||
/// Returns a reset but still enabled app search state
|
|
||||||
pub fn reset(&mut self) {
|
|
||||||
*self = AppSearchState {
|
|
||||||
is_enabled: self.is_enabled,
|
|
||||||
..AppSearchState::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_invalid_or_blank_search(&self) -> bool {
|
|
||||||
self.is_blank_search || self.is_invalid_search
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ProcessSearchState only deals with process' search's current settings and state.
|
|
||||||
pub struct ProcessSearchState {
|
|
||||||
pub search_state: AppSearchState,
|
|
||||||
pub is_searching_with_pid: bool,
|
|
||||||
pub is_ignoring_case: bool,
|
|
||||||
pub is_searching_whole_word: bool,
|
|
||||||
pub is_searching_with_regex: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ProcessSearchState {
|
|
||||||
fn default() -> Self {
|
|
||||||
ProcessSearchState {
|
|
||||||
search_state: AppSearchState::default(),
|
|
||||||
is_searching_with_pid: false,
|
|
||||||
is_ignoring_case: true,
|
|
||||||
is_searching_whole_word: false,
|
|
||||||
is_searching_with_regex: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProcessSearchState {
|
|
||||||
pub fn search_toggle_ignore_case(&mut self) {
|
|
||||||
self.is_ignoring_case = !self.is_ignoring_case;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn search_toggle_whole_word(&mut self) {
|
|
||||||
self.is_searching_whole_word = !self.is_searching_whole_word;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn search_toggle_regex(&mut self) {
|
|
||||||
self.is_searching_with_regex = !self.is_searching_with_regex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ProcWidgetState {
|
|
||||||
pub process_search_state: ProcessSearchState,
|
|
||||||
pub is_grouped: bool,
|
|
||||||
pub scroll_state: AppScrollWidgetState,
|
|
||||||
pub process_sorting_type: processes::ProcessSorting,
|
|
||||||
pub process_sorting_reverse: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProcWidgetState {
|
|
||||||
pub fn init(
|
|
||||||
is_case_sensitive: bool, is_match_whole_word: bool, is_use_regex: bool, is_grouped: bool,
|
|
||||||
) -> Self {
|
|
||||||
let mut process_search_state = ProcessSearchState::default();
|
|
||||||
if is_case_sensitive {
|
|
||||||
// By default it's off
|
|
||||||
process_search_state.search_toggle_ignore_case();
|
|
||||||
}
|
|
||||||
if is_match_whole_word {
|
|
||||||
process_search_state.search_toggle_whole_word();
|
|
||||||
}
|
|
||||||
if is_use_regex {
|
|
||||||
process_search_state.search_toggle_regex();
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcWidgetState {
|
|
||||||
process_search_state,
|
|
||||||
is_grouped,
|
|
||||||
scroll_state: AppScrollWidgetState::default(),
|
|
||||||
process_sorting_type: processes::ProcessSorting::CPU,
|
|
||||||
process_sorting_reverse: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_cursor_position(&self) -> usize {
|
|
||||||
self.process_search_state
|
|
||||||
.search_state
|
|
||||||
.grapheme_cursor
|
|
||||||
.cur_cursor()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_char_cursor_position(&self) -> usize {
|
|
||||||
self.process_search_state.search_state.char_cursor_position
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_search_enabled(&self) -> bool {
|
|
||||||
self.process_search_state.search_state.is_enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_current_search_query(&self) -> &String {
|
|
||||||
&self.process_search_state.search_state.current_search_query
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_regex(&mut self) {
|
|
||||||
if self
|
|
||||||
.process_search_state
|
|
||||||
.search_state
|
|
||||||
.current_search_query
|
|
||||||
.is_empty()
|
|
||||||
{
|
|
||||||
self.process_search_state.search_state.is_invalid_search = false;
|
|
||||||
self.process_search_state.search_state.is_blank_search = true;
|
|
||||||
} else {
|
|
||||||
let regex_string = &self.process_search_state.search_state.current_search_query;
|
|
||||||
let escaped_regex: String;
|
|
||||||
let final_regex_string = &format!(
|
|
||||||
"{}{}{}{}",
|
|
||||||
if self.process_search_state.is_searching_whole_word {
|
|
||||||
"^"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
},
|
|
||||||
if self.process_search_state.is_ignoring_case {
|
|
||||||
"(?i)"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
},
|
|
||||||
if !self.process_search_state.is_searching_with_regex {
|
|
||||||
escaped_regex = regex::escape(regex_string);
|
|
||||||
&escaped_regex
|
|
||||||
} else {
|
|
||||||
regex_string
|
|
||||||
},
|
|
||||||
if self.process_search_state.is_searching_whole_word {
|
|
||||||
"$"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let new_regex = regex::Regex::new(final_regex_string);
|
|
||||||
self.process_search_state.search_state.is_blank_search = false;
|
|
||||||
self.process_search_state.search_state.is_invalid_search = new_regex.is_err();
|
|
||||||
|
|
||||||
self.process_search_state.search_state.current_regex = Some(new_regex);
|
|
||||||
}
|
|
||||||
self.scroll_state.previous_scroll_position = 0;
|
|
||||||
self.scroll_state.current_scroll_position = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_search(&mut self) {
|
|
||||||
self.process_search_state.search_state.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn search_walk_forward(&mut self, start_position: usize) {
|
|
||||||
self.process_search_state
|
|
||||||
.search_state
|
|
||||||
.grapheme_cursor
|
|
||||||
.next_boundary(
|
|
||||||
&self.process_search_state.search_state.current_search_query[start_position..],
|
|
||||||
start_position,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn search_walk_back(&mut self, start_position: usize) {
|
|
||||||
self.process_search_state
|
|
||||||
.search_state
|
|
||||||
.grapheme_cursor
|
|
||||||
.prev_boundary(
|
|
||||||
&self.process_search_state.search_state.current_search_query[..start_position],
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ProcState {
|
|
||||||
pub widget_states: HashMap<u64, ProcWidgetState>,
|
|
||||||
pub force_update: Option<u64>,
|
|
||||||
pub force_update_all: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProcState {
|
|
||||||
pub fn init(widget_states: HashMap<u64, ProcWidgetState>) -> Self {
|
|
||||||
ProcState {
|
|
||||||
widget_states,
|
|
||||||
force_update: None,
|
|
||||||
force_update_all: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NetWidgetState {
|
|
||||||
pub current_display_time: u64,
|
|
||||||
pub autohide_timer: Option<Instant>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetWidgetState {
|
|
||||||
pub fn init(current_display_time: u64, autohide_timer: Option<Instant>) -> Self {
|
|
||||||
NetWidgetState {
|
|
||||||
current_display_time,
|
|
||||||
autohide_timer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NetState {
|
|
||||||
pub force_update: Option<u64>,
|
|
||||||
pub widget_states: HashMap<u64, NetWidgetState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetState {
|
|
||||||
pub fn init(widget_states: HashMap<u64, NetWidgetState>) -> Self {
|
|
||||||
NetState {
|
|
||||||
force_update: None,
|
|
||||||
widget_states,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CpuWidgetState {
|
|
||||||
pub current_display_time: u64,
|
|
||||||
pub is_legend_hidden: bool,
|
|
||||||
pub is_showing_tray: bool,
|
|
||||||
pub core_show_vec: Vec<bool>,
|
|
||||||
pub num_cpus_shown: usize,
|
|
||||||
pub autohide_timer: Option<Instant>,
|
|
||||||
pub scroll_state: AppScrollWidgetState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CpuWidgetState {
|
|
||||||
pub fn init(current_display_time: u64, autohide_timer: Option<Instant>) -> Self {
|
|
||||||
CpuWidgetState {
|
|
||||||
current_display_time,
|
|
||||||
is_legend_hidden: false,
|
|
||||||
is_showing_tray: false,
|
|
||||||
core_show_vec: Vec::new(),
|
|
||||||
num_cpus_shown: 0,
|
|
||||||
autohide_timer,
|
|
||||||
scroll_state: AppScrollWidgetState::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CpuState {
|
|
||||||
pub force_update: Option<u64>,
|
|
||||||
pub widget_states: HashMap<u64, CpuWidgetState>,
|
|
||||||
pub num_cpus_total: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CpuState {
|
|
||||||
pub fn init(widget_states: HashMap<u64, CpuWidgetState>) -> Self {
|
|
||||||
CpuState {
|
|
||||||
force_update: None,
|
|
||||||
widget_states,
|
|
||||||
num_cpus_total: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MemWidgetState {
|
|
||||||
pub current_display_time: u64,
|
|
||||||
pub autohide_timer: Option<Instant>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MemWidgetState {
|
|
||||||
pub fn init(current_display_time: u64, autohide_timer: Option<Instant>) -> Self {
|
|
||||||
MemWidgetState {
|
|
||||||
current_display_time,
|
|
||||||
autohide_timer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MemState {
|
|
||||||
pub force_update: Option<u64>,
|
|
||||||
pub widget_states: HashMap<u64, MemWidgetState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MemState {
|
|
||||||
pub fn init(widget_states: HashMap<u64, MemWidgetState>) -> Self {
|
|
||||||
MemState {
|
|
||||||
force_update: None,
|
|
||||||
widget_states,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TempWidgetState {
|
|
||||||
pub scroll_state: AppScrollWidgetState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TempWidgetState {
|
|
||||||
pub fn init() -> Self {
|
|
||||||
TempWidgetState {
|
|
||||||
scroll_state: AppScrollWidgetState::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TempState {
|
|
||||||
pub widget_states: HashMap<u64, TempWidgetState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TempState {
|
|
||||||
pub fn init(widget_states: HashMap<u64, TempWidgetState>) -> Self {
|
|
||||||
TempState { widget_states }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DiskWidgetState {
|
|
||||||
pub scroll_state: AppScrollWidgetState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DiskWidgetState {
|
|
||||||
pub fn init() -> Self {
|
|
||||||
DiskWidgetState {
|
|
||||||
scroll_state: AppScrollWidgetState::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DiskState {
|
|
||||||
pub widget_states: HashMap<u64, DiskWidgetState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DiskState {
|
|
||||||
pub fn init(widget_states: HashMap<u64, DiskWidgetState>) -> Self {
|
|
||||||
DiskState { widget_states }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub struct BasicTableWidgetState {
|
|
||||||
// Since this is intended (currently) to only be used for ONE widget, that's
|
|
||||||
// how it's going to be written. If we want to allow for multiple of these,
|
|
||||||
// then we can expand outwards with a normal BasicTableState and a hashmap
|
|
||||||
pub currently_displayed_widget_type: BottomWidgetType,
|
|
||||||
pub currently_displayed_widget_id: u64,
|
|
||||||
pub widget_id: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct BatteryWidgetState {
|
|
||||||
pub currently_selected_battery_index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BatteryState {
|
|
||||||
pub widget_states: HashMap<u64, BatteryWidgetState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BatteryState {
|
|
||||||
pub fn init(widget_states: HashMap<u64, BatteryWidgetState>) -> Self {
|
|
||||||
BatteryState { widget_states }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ParagraphScrollState {
|
|
||||||
pub current_scroll_index: u16,
|
|
||||||
pub max_scroll_index: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(TypedBuilder)]
|
#[derive(TypedBuilder)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
#[builder(default = false, setter(skip))]
|
#[builder(default = false, setter(skip))]
|
||||||
@ -1654,6 +1213,7 @@ impl App {
|
|||||||
| BottomWidgetType::Proc
|
| BottomWidgetType::Proc
|
||||||
| BottomWidgetType::ProcSearch
|
| BottomWidgetType::ProcSearch
|
||||||
| BottomWidgetType::Disk
|
| BottomWidgetType::Disk
|
||||||
|
| BottomWidgetType::Battery
|
||||||
if self.basic_table_widget_state.is_some() =>
|
if self.basic_table_widget_state.is_some() =>
|
||||||
{
|
{
|
||||||
if let Some(basic_table_widget_state) =
|
if let Some(basic_table_widget_state) =
|
||||||
@ -1752,6 +1312,7 @@ impl App {
|
|||||||
| BottomWidgetType::Proc
|
| BottomWidgetType::Proc
|
||||||
| BottomWidgetType::ProcSearch
|
| BottomWidgetType::ProcSearch
|
||||||
| BottomWidgetType::Disk
|
| BottomWidgetType::Disk
|
||||||
|
| BottomWidgetType::Battery
|
||||||
if self.basic_table_widget_state.is_some() =>
|
if self.basic_table_widget_state.is_some() =>
|
||||||
{
|
{
|
||||||
if let Some(basic_table_widget_state) =
|
if let Some(basic_table_widget_state) =
|
||||||
|
@ -322,14 +322,15 @@ impl BottomLayout {
|
|||||||
widget.up_neighbour = Some(current_best_widget_id);
|
widget.up_neighbour = Some(current_best_widget_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(next_row_up) = layout_mapping
|
} else {
|
||||||
|
for next_row_up in layout_mapping
|
||||||
.range(
|
.range(
|
||||||
..(
|
..(
|
||||||
row_height_percentage_start,
|
row_height_percentage_start,
|
||||||
row_height_percentage_start,
|
row_height_percentage_start,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.next_back()
|
.rev()
|
||||||
{
|
{
|
||||||
let mut current_best_distance = 0;
|
let mut current_best_distance = 0;
|
||||||
let mut current_best_widget_id = widget.widget_id;
|
let mut current_best_widget_id = widget.widget_id;
|
||||||
@ -348,7 +349,10 @@ impl BottomLayout {
|
|||||||
/ 100,
|
/ 100,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(col_width_percentage_start, col_width_percentage_end)
|
(
|
||||||
|
col_width_percentage_start,
|
||||||
|
col_width_percentage_end,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
for col_position in &(next_row_up.1).1 {
|
for col_position in &(next_row_up.1).1 {
|
||||||
@ -361,10 +365,12 @@ impl BottomLayout {
|
|||||||
candidate_col_end - candidate_col_start;
|
candidate_col_end - candidate_col_start;
|
||||||
for candidate_widget in &(next_col_row.1).1 {
|
for candidate_widget in &(next_col_row.1).1 {
|
||||||
let candidate_start = candidate_col_start
|
let candidate_start = candidate_col_start
|
||||||
+ (candidate_widget.0).0 * candidate_difference
|
+ (candidate_widget.0).0
|
||||||
|
* candidate_difference
|
||||||
/ 100;
|
/ 100;
|
||||||
let candidate_end = candidate_col_start
|
let candidate_end = candidate_col_start
|
||||||
+ (candidate_widget.0).1 * candidate_difference
|
+ (candidate_widget.0).1
|
||||||
|
* candidate_difference
|
||||||
/ 100;
|
/ 100;
|
||||||
|
|
||||||
if is_intersecting(
|
if is_intersecting(
|
||||||
@ -376,7 +382,9 @@ impl BottomLayout {
|
|||||||
(candidate_start, candidate_end),
|
(candidate_start, candidate_end),
|
||||||
);
|
);
|
||||||
|
|
||||||
if current_best_distance < candidate_distance {
|
if current_best_distance
|
||||||
|
< candidate_distance
|
||||||
|
{
|
||||||
current_best_distance =
|
current_best_distance =
|
||||||
candidate_distance + 1;
|
candidate_distance + 1;
|
||||||
current_best_widget_id =
|
current_best_widget_id =
|
||||||
@ -389,6 +397,8 @@ impl BottomLayout {
|
|||||||
|
|
||||||
if current_best_distance > 0 {
|
if current_best_distance > 0 {
|
||||||
widget.up_neighbour = Some(current_best_widget_id);
|
widget.up_neighbour = Some(current_best_widget_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,15 +440,13 @@ impl BottomLayout {
|
|||||||
widget.down_neighbour = Some(current_best_widget_id);
|
widget.down_neighbour = Some(current_best_widget_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(next_row_down) = layout_mapping
|
} else {
|
||||||
.range(
|
for next_row_down in layout_mapping.range(
|
||||||
(
|
(
|
||||||
row_height_percentage_start + 1,
|
row_height_percentage_start + 1,
|
||||||
row_height_percentage_start + 1,
|
row_height_percentage_start + 1,
|
||||||
)..,
|
)..,
|
||||||
)
|
) {
|
||||||
.next()
|
|
||||||
{
|
|
||||||
let mut current_best_distance = 0;
|
let mut current_best_distance = 0;
|
||||||
let mut current_best_widget_id = widget.widget_id;
|
let mut current_best_widget_id = widget.widget_id;
|
||||||
let (target_start_width, target_end_width) =
|
let (target_start_width, target_end_width) =
|
||||||
@ -456,11 +464,15 @@ impl BottomLayout {
|
|||||||
/ 100,
|
/ 100,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(col_width_percentage_start, col_width_percentage_end)
|
(
|
||||||
|
col_width_percentage_start,
|
||||||
|
col_width_percentage_end,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
for col_position in &(next_row_down.1).1 {
|
for col_position in &(next_row_down.1).1 {
|
||||||
if let Some(next_col_row) = (col_position.1).1.iter().next()
|
if let Some(next_col_row) =
|
||||||
|
(col_position.1).1.iter().next()
|
||||||
{
|
{
|
||||||
let (candidate_col_start, candidate_col_end) =
|
let (candidate_col_start, candidate_col_end) =
|
||||||
((col_position.0).0, (col_position.0).1);
|
((col_position.0).0, (col_position.0).1);
|
||||||
@ -468,10 +480,12 @@ impl BottomLayout {
|
|||||||
candidate_col_end - candidate_col_start;
|
candidate_col_end - candidate_col_start;
|
||||||
for candidate_widget in &(next_col_row.1).1 {
|
for candidate_widget in &(next_col_row.1).1 {
|
||||||
let candidate_start = candidate_col_start
|
let candidate_start = candidate_col_start
|
||||||
+ (candidate_widget.0).0 * candidate_difference
|
+ (candidate_widget.0).0
|
||||||
|
* candidate_difference
|
||||||
/ 100;
|
/ 100;
|
||||||
let candidate_end = candidate_col_start
|
let candidate_end = candidate_col_start
|
||||||
+ (candidate_widget.0).1 * candidate_difference
|
+ (candidate_widget.0).1
|
||||||
|
* candidate_difference
|
||||||
/ 100;
|
/ 100;
|
||||||
|
|
||||||
if is_intersecting(
|
if is_intersecting(
|
||||||
@ -483,7 +497,9 @@ impl BottomLayout {
|
|||||||
(candidate_start, candidate_end),
|
(candidate_start, candidate_end),
|
||||||
);
|
);
|
||||||
|
|
||||||
if current_best_distance < candidate_distance {
|
if current_best_distance
|
||||||
|
< candidate_distance
|
||||||
|
{
|
||||||
current_best_distance =
|
current_best_distance =
|
||||||
candidate_distance + 1;
|
candidate_distance + 1;
|
||||||
current_best_widget_id =
|
current_best_widget_id =
|
||||||
@ -496,6 +512,8 @@ impl BottomLayout {
|
|||||||
|
|
||||||
if current_best_distance > 0 {
|
if current_best_distance > 0 {
|
||||||
widget.down_neighbour = Some(current_best_widget_id);
|
widget.down_neighbour = Some(current_best_widget_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -510,7 +528,141 @@ impl BottomLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_basic_default() -> Self {
|
pub fn init_basic_default(use_battery: bool) -> Self {
|
||||||
|
let table_widgets = if use_battery {
|
||||||
|
vec![
|
||||||
|
BottomCol::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.children(vec![BottomColRow::builder()
|
||||||
|
.canvas_handle_height(true)
|
||||||
|
.children(vec![BottomWidget::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.widget_type(BottomWidgetType::Disk)
|
||||||
|
.widget_id(4)
|
||||||
|
.up_neighbour(Some(100))
|
||||||
|
.left_neighbour(Some(8))
|
||||||
|
.right_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||||
|
.build()])
|
||||||
|
.build()])
|
||||||
|
.build(),
|
||||||
|
BottomCol::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.children(vec![
|
||||||
|
BottomColRow::builder()
|
||||||
|
.canvas_handle_height(true)
|
||||||
|
.children(vec![BottomWidget::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.widget_type(BottomWidgetType::Proc)
|
||||||
|
.widget_id(DEFAULT_WIDGET_ID)
|
||||||
|
.up_neighbour(Some(100))
|
||||||
|
.down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
|
||||||
|
.left_neighbour(Some(4))
|
||||||
|
.right_neighbour(Some(8))
|
||||||
|
.build()])
|
||||||
|
.build(),
|
||||||
|
BottomColRow::builder()
|
||||||
|
.canvas_handle_height(true)
|
||||||
|
.children(vec![BottomWidget::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.widget_type(BottomWidgetType::ProcSearch)
|
||||||
|
.widget_id(DEFAULT_WIDGET_ID + 1)
|
||||||
|
.up_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||||
|
.left_neighbour(Some(4))
|
||||||
|
.right_neighbour(Some(7))
|
||||||
|
.build()])
|
||||||
|
.build(),
|
||||||
|
])
|
||||||
|
.build(),
|
||||||
|
BottomCol::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.children(vec![BottomColRow::builder()
|
||||||
|
.canvas_handle_height(true)
|
||||||
|
.children(vec![BottomWidget::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.widget_type(BottomWidgetType::Temp)
|
||||||
|
.widget_id(7)
|
||||||
|
.up_neighbour(Some(100))
|
||||||
|
.left_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||||
|
.right_neighbour(Some(8))
|
||||||
|
.build()])
|
||||||
|
.build()])
|
||||||
|
.build(),
|
||||||
|
BottomCol::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.children(vec![BottomColRow::builder()
|
||||||
|
.canvas_handle_height(true)
|
||||||
|
.children(vec![BottomWidget::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.widget_type(BottomWidgetType::Battery)
|
||||||
|
.widget_id(8)
|
||||||
|
.up_neighbour(Some(100))
|
||||||
|
.left_neighbour(Some(7))
|
||||||
|
.right_neighbour(Some(4))
|
||||||
|
.build()])
|
||||||
|
.build()])
|
||||||
|
.build(),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
vec![
|
||||||
|
BottomCol::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.children(vec![BottomColRow::builder()
|
||||||
|
.canvas_handle_height(true)
|
||||||
|
.children(vec![BottomWidget::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.widget_type(BottomWidgetType::Disk)
|
||||||
|
.widget_id(4)
|
||||||
|
.up_neighbour(Some(100))
|
||||||
|
.left_neighbour(Some(7))
|
||||||
|
.right_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||||
|
.build()])
|
||||||
|
.build()])
|
||||||
|
.build(),
|
||||||
|
BottomCol::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.children(vec![
|
||||||
|
BottomColRow::builder()
|
||||||
|
.canvas_handle_height(true)
|
||||||
|
.children(vec![BottomWidget::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.widget_type(BottomWidgetType::Proc)
|
||||||
|
.widget_id(DEFAULT_WIDGET_ID)
|
||||||
|
.up_neighbour(Some(100))
|
||||||
|
.down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
|
||||||
|
.left_neighbour(Some(4))
|
||||||
|
.right_neighbour(Some(7))
|
||||||
|
.build()])
|
||||||
|
.build(),
|
||||||
|
BottomColRow::builder()
|
||||||
|
.canvas_handle_height(true)
|
||||||
|
.children(vec![BottomWidget::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.widget_type(BottomWidgetType::ProcSearch)
|
||||||
|
.widget_id(DEFAULT_WIDGET_ID + 1)
|
||||||
|
.up_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||||
|
.left_neighbour(Some(4))
|
||||||
|
.right_neighbour(Some(7))
|
||||||
|
.build()])
|
||||||
|
.build(),
|
||||||
|
])
|
||||||
|
.build(),
|
||||||
|
BottomCol::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.children(vec![BottomColRow::builder()
|
||||||
|
.canvas_handle_height(true)
|
||||||
|
.children(vec![BottomWidget::builder()
|
||||||
|
.canvas_handle_width(true)
|
||||||
|
.widget_type(BottomWidgetType::Temp)
|
||||||
|
.widget_id(7)
|
||||||
|
.up_neighbour(Some(100))
|
||||||
|
.left_neighbour(Some(DEFAULT_WIDGET_ID))
|
||||||
|
.right_neighbour(Some(4))
|
||||||
|
.build()])
|
||||||
|
.build()])
|
||||||
|
.build(),
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
BottomLayout {
|
BottomLayout {
|
||||||
total_row_height_ratio: 3,
|
total_row_height_ratio: 3,
|
||||||
rows: vec![
|
rows: vec![
|
||||||
@ -573,79 +725,14 @@ impl BottomLayout {
|
|||||||
.build(),
|
.build(),
|
||||||
BottomRow::builder()
|
BottomRow::builder()
|
||||||
.canvas_handle_height(true)
|
.canvas_handle_height(true)
|
||||||
.children(vec![
|
.children(table_widgets)
|
||||||
BottomCol::builder()
|
|
||||||
.canvas_handle_width(true)
|
|
||||||
.children(vec![BottomColRow::builder()
|
|
||||||
.canvas_handle_height(true)
|
|
||||||
.children(vec![BottomWidget::builder()
|
|
||||||
.canvas_handle_width(true)
|
|
||||||
.widget_type(BottomWidgetType::Disk)
|
|
||||||
.widget_id(4)
|
|
||||||
.up_neighbour(Some(100))
|
|
||||||
.left_neighbour(Some(7))
|
|
||||||
.right_neighbour(Some(DEFAULT_WIDGET_ID))
|
|
||||||
.build()])
|
|
||||||
.build()])
|
|
||||||
.build(),
|
|
||||||
BottomCol::builder()
|
|
||||||
.canvas_handle_width(true)
|
|
||||||
.children(vec![
|
|
||||||
BottomColRow::builder()
|
|
||||||
.canvas_handle_height(true)
|
|
||||||
.children(vec![BottomWidget::builder()
|
|
||||||
.canvas_handle_width(true)
|
|
||||||
.widget_type(BottomWidgetType::Proc)
|
|
||||||
.widget_id(DEFAULT_WIDGET_ID)
|
|
||||||
.up_neighbour(Some(100))
|
|
||||||
.down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
|
|
||||||
.left_neighbour(Some(4))
|
|
||||||
.right_neighbour(Some(7))
|
|
||||||
.build()])
|
|
||||||
.build(),
|
|
||||||
BottomColRow::builder()
|
|
||||||
.canvas_handle_height(true)
|
|
||||||
.children(vec![BottomWidget::builder()
|
|
||||||
.canvas_handle_width(true)
|
|
||||||
.widget_type(BottomWidgetType::ProcSearch)
|
|
||||||
.widget_id(DEFAULT_WIDGET_ID + 1)
|
|
||||||
.up_neighbour(Some(DEFAULT_WIDGET_ID))
|
|
||||||
.left_neighbour(Some(4))
|
|
||||||
.right_neighbour(Some(7))
|
|
||||||
.build()])
|
|
||||||
.build(),
|
|
||||||
])
|
|
||||||
.build(),
|
|
||||||
BottomCol::builder()
|
|
||||||
.canvas_handle_width(true)
|
|
||||||
.children(vec![BottomColRow::builder()
|
|
||||||
.canvas_handle_height(true)
|
|
||||||
.children(vec![BottomWidget::builder()
|
|
||||||
.canvas_handle_width(true)
|
|
||||||
.widget_type(BottomWidgetType::Temp)
|
|
||||||
.widget_id(7)
|
|
||||||
.up_neighbour(Some(100))
|
|
||||||
.left_neighbour(Some(DEFAULT_WIDGET_ID))
|
|
||||||
.right_neighbour(Some(4))
|
|
||||||
.build()])
|
|
||||||
.build()])
|
|
||||||
.build(),
|
|
||||||
])
|
|
||||||
.build(),
|
.build(),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_default(left_legend: bool) -> Self {
|
pub fn init_default(left_legend: bool, use_battery: bool) -> Self {
|
||||||
BottomLayout {
|
let cpu_layout = if left_legend {
|
||||||
total_row_height_ratio: 100,
|
|
||||||
rows: vec![
|
|
||||||
BottomRow::builder()
|
|
||||||
.row_height_ratio(30)
|
|
||||||
.children(vec![BottomCol::builder()
|
|
||||||
.children(vec![BottomColRow::builder()
|
|
||||||
.total_widget_ratio(20)
|
|
||||||
.children(if left_legend {
|
|
||||||
vec![
|
vec![
|
||||||
BottomWidget::builder()
|
BottomWidget::builder()
|
||||||
.width_ratio(3)
|
.width_ratio(3)
|
||||||
@ -661,6 +748,7 @@ impl BottomLayout {
|
|||||||
.widget_id(1)
|
.widget_id(1)
|
||||||
.down_neighbour(Some(12))
|
.down_neighbour(Some(12))
|
||||||
.left_neighbour(Some(2))
|
.left_neighbour(Some(2))
|
||||||
|
.right_neighbour(if use_battery { Some(99) } else { None })
|
||||||
.flex_grow(true)
|
.flex_grow(true)
|
||||||
.build(),
|
.build(),
|
||||||
]
|
]
|
||||||
@ -680,13 +768,51 @@ impl BottomLayout {
|
|||||||
.widget_id(2)
|
.widget_id(2)
|
||||||
.down_neighbour(Some(12))
|
.down_neighbour(Some(12))
|
||||||
.left_neighbour(Some(1))
|
.left_neighbour(Some(1))
|
||||||
|
.right_neighbour(if use_battery { Some(99) } else { None })
|
||||||
.canvas_handle_width(true)
|
.canvas_handle_width(true)
|
||||||
.build(),
|
.build(),
|
||||||
]
|
]
|
||||||
})
|
};
|
||||||
|
|
||||||
|
let first_row_layout = if use_battery {
|
||||||
|
vec![
|
||||||
|
BottomCol::builder()
|
||||||
|
.col_width_ratio(2)
|
||||||
|
.children(vec![BottomColRow::builder()
|
||||||
|
.total_widget_ratio(20)
|
||||||
|
.children(cpu_layout)
|
||||||
|
.build()])
|
||||||
|
.build(),
|
||||||
|
BottomCol::builder()
|
||||||
|
.col_width_ratio(1)
|
||||||
|
.children(vec![BottomColRow::builder()
|
||||||
|
.children(vec![BottomWidget::builder()
|
||||||
|
.widget_type(BottomWidgetType::Battery)
|
||||||
|
.widget_id(99)
|
||||||
|
.down_neighbour(Some(12))
|
||||||
|
.left_neighbour(Some(if left_legend { 1 } else { 2 }))
|
||||||
|
.canvas_handle_width(true)
|
||||||
.build()])
|
.build()])
|
||||||
.build()])
|
.build()])
|
||||||
.build(),
|
.build(),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
vec![BottomCol::builder()
|
||||||
|
.children(vec![BottomColRow::builder()
|
||||||
|
.total_widget_ratio(20)
|
||||||
|
.children(cpu_layout)
|
||||||
|
.build()])
|
||||||
|
.build()]
|
||||||
|
};
|
||||||
|
|
||||||
|
BottomLayout {
|
||||||
|
total_row_height_ratio: 100,
|
||||||
|
rows: vec![
|
||||||
|
BottomRow::builder()
|
||||||
|
.row_height_ratio(30)
|
||||||
|
.total_col_ratio(if use_battery { 3 } else { 1 })
|
||||||
|
.children(first_row_layout)
|
||||||
|
.build(),
|
||||||
BottomRow::builder()
|
BottomRow::builder()
|
||||||
.total_col_ratio(7)
|
.total_col_ratio(7)
|
||||||
.row_height_ratio(40)
|
.row_height_ratio(40)
|
||||||
@ -907,6 +1033,7 @@ impl BottomWidgetType {
|
|||||||
Proc => "Processes",
|
Proc => "Processes",
|
||||||
Temp => "Temperature",
|
Temp => "Temperature",
|
||||||
Disk => "Disks",
|
Disk => "Disks",
|
||||||
|
Battery => "Battery",
|
||||||
_ => "",
|
_ => "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
448
src/app/states.rs
Normal file
448
src/app/states.rs
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
use std::{collections::HashMap, time::Instant};
|
||||||
|
|
||||||
|
use unicode_segmentation::GraphemeCursor;
|
||||||
|
|
||||||
|
use tui::widgets::TableState;
|
||||||
|
|
||||||
|
use crate::{app::layout_manager::BottomWidgetType, constants, data_harvester::processes};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ScrollDirection {
|
||||||
|
// UP means scrolling up --- this usually DECREMENTS
|
||||||
|
UP,
|
||||||
|
// DOWN means scrolling down --- this usually INCREMENTS
|
||||||
|
DOWN,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ScrollDirection {
|
||||||
|
fn default() -> Self {
|
||||||
|
ScrollDirection::DOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CursorDirection {
|
||||||
|
LEFT,
|
||||||
|
RIGHT,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AppScrollWidgetState deals with fields for a scrollable app's current state.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct AppScrollWidgetState {
|
||||||
|
pub current_scroll_position: u64,
|
||||||
|
pub previous_scroll_position: u64,
|
||||||
|
pub scroll_direction: ScrollDirection,
|
||||||
|
pub table_state: TableState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct AppDeleteDialogState {
|
||||||
|
pub is_showing_dd: bool,
|
||||||
|
pub is_on_yes: bool, // Defaults to "No"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AppHelpDialogState {
|
||||||
|
pub is_showing_help: bool,
|
||||||
|
pub scroll_state: ParagraphScrollState,
|
||||||
|
pub index_shortcuts: Vec<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AppHelpDialogState {
|
||||||
|
fn default() -> Self {
|
||||||
|
AppHelpDialogState {
|
||||||
|
is_showing_help: false,
|
||||||
|
scroll_state: ParagraphScrollState::default(),
|
||||||
|
index_shortcuts: vec![0; constants::HELP_TEXT.len()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AppSearchState deals with generic searching (I might do this in the future).
|
||||||
|
pub struct AppSearchState {
|
||||||
|
pub is_enabled: bool,
|
||||||
|
pub current_search_query: String,
|
||||||
|
pub current_regex: Option<std::result::Result<regex::Regex, regex::Error>>,
|
||||||
|
pub is_blank_search: bool,
|
||||||
|
pub is_invalid_search: bool,
|
||||||
|
pub grapheme_cursor: GraphemeCursor,
|
||||||
|
pub cursor_direction: CursorDirection,
|
||||||
|
pub cursor_bar: usize,
|
||||||
|
/// This represents the position in terms of CHARACTERS, not graphemes
|
||||||
|
pub char_cursor_position: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AppSearchState {
|
||||||
|
fn default() -> Self {
|
||||||
|
AppSearchState {
|
||||||
|
is_enabled: false,
|
||||||
|
current_search_query: String::default(),
|
||||||
|
current_regex: None,
|
||||||
|
is_invalid_search: false,
|
||||||
|
is_blank_search: true,
|
||||||
|
grapheme_cursor: GraphemeCursor::new(0, 0, true),
|
||||||
|
cursor_direction: CursorDirection::RIGHT,
|
||||||
|
cursor_bar: 0,
|
||||||
|
char_cursor_position: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppSearchState {
|
||||||
|
/// Returns a reset but still enabled app search state
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
*self = AppSearchState {
|
||||||
|
is_enabled: self.is_enabled,
|
||||||
|
..AppSearchState::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_invalid_or_blank_search(&self) -> bool {
|
||||||
|
self.is_blank_search || self.is_invalid_search
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ProcessSearchState only deals with process' search's current settings and state.
|
||||||
|
pub struct ProcessSearchState {
|
||||||
|
pub search_state: AppSearchState,
|
||||||
|
pub is_searching_with_pid: bool,
|
||||||
|
pub is_ignoring_case: bool,
|
||||||
|
pub is_searching_whole_word: bool,
|
||||||
|
pub is_searching_with_regex: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ProcessSearchState {
|
||||||
|
fn default() -> Self {
|
||||||
|
ProcessSearchState {
|
||||||
|
search_state: AppSearchState::default(),
|
||||||
|
is_searching_with_pid: false,
|
||||||
|
is_ignoring_case: true,
|
||||||
|
is_searching_whole_word: false,
|
||||||
|
is_searching_with_regex: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessSearchState {
|
||||||
|
pub fn search_toggle_ignore_case(&mut self) {
|
||||||
|
self.is_ignoring_case = !self.is_ignoring_case;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn search_toggle_whole_word(&mut self) {
|
||||||
|
self.is_searching_whole_word = !self.is_searching_whole_word;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn search_toggle_regex(&mut self) {
|
||||||
|
self.is_searching_with_regex = !self.is_searching_with_regex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProcWidgetState {
|
||||||
|
pub process_search_state: ProcessSearchState,
|
||||||
|
pub is_grouped: bool,
|
||||||
|
pub scroll_state: AppScrollWidgetState,
|
||||||
|
pub process_sorting_type: processes::ProcessSorting,
|
||||||
|
pub process_sorting_reverse: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcWidgetState {
|
||||||
|
pub fn init(
|
||||||
|
is_case_sensitive: bool, is_match_whole_word: bool, is_use_regex: bool, is_grouped: bool,
|
||||||
|
) -> Self {
|
||||||
|
let mut process_search_state = ProcessSearchState::default();
|
||||||
|
if is_case_sensitive {
|
||||||
|
// By default it's off
|
||||||
|
process_search_state.search_toggle_ignore_case();
|
||||||
|
}
|
||||||
|
if is_match_whole_word {
|
||||||
|
process_search_state.search_toggle_whole_word();
|
||||||
|
}
|
||||||
|
if is_use_regex {
|
||||||
|
process_search_state.search_toggle_regex();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcWidgetState {
|
||||||
|
process_search_state,
|
||||||
|
is_grouped,
|
||||||
|
scroll_state: AppScrollWidgetState::default(),
|
||||||
|
process_sorting_type: processes::ProcessSorting::CPU,
|
||||||
|
process_sorting_reverse: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cursor_position(&self) -> usize {
|
||||||
|
self.process_search_state
|
||||||
|
.search_state
|
||||||
|
.grapheme_cursor
|
||||||
|
.cur_cursor()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_char_cursor_position(&self) -> usize {
|
||||||
|
self.process_search_state.search_state.char_cursor_position
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_search_enabled(&self) -> bool {
|
||||||
|
self.process_search_state.search_state.is_enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_current_search_query(&self) -> &String {
|
||||||
|
&self.process_search_state.search_state.current_search_query
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_regex(&mut self) {
|
||||||
|
if self
|
||||||
|
.process_search_state
|
||||||
|
.search_state
|
||||||
|
.current_search_query
|
||||||
|
.is_empty()
|
||||||
|
{
|
||||||
|
self.process_search_state.search_state.is_invalid_search = false;
|
||||||
|
self.process_search_state.search_state.is_blank_search = true;
|
||||||
|
} else {
|
||||||
|
let regex_string = &self.process_search_state.search_state.current_search_query;
|
||||||
|
let escaped_regex: String;
|
||||||
|
let final_regex_string = &format!(
|
||||||
|
"{}{}{}{}",
|
||||||
|
if self.process_search_state.is_searching_whole_word {
|
||||||
|
"^"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
if self.process_search_state.is_ignoring_case {
|
||||||
|
"(?i)"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
if !self.process_search_state.is_searching_with_regex {
|
||||||
|
escaped_regex = regex::escape(regex_string);
|
||||||
|
&escaped_regex
|
||||||
|
} else {
|
||||||
|
regex_string
|
||||||
|
},
|
||||||
|
if self.process_search_state.is_searching_whole_word {
|
||||||
|
"$"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let new_regex = regex::Regex::new(final_regex_string);
|
||||||
|
self.process_search_state.search_state.is_blank_search = false;
|
||||||
|
self.process_search_state.search_state.is_invalid_search = new_regex.is_err();
|
||||||
|
|
||||||
|
self.process_search_state.search_state.current_regex = Some(new_regex);
|
||||||
|
}
|
||||||
|
self.scroll_state.previous_scroll_position = 0;
|
||||||
|
self.scroll_state.current_scroll_position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_search(&mut self) {
|
||||||
|
self.process_search_state.search_state.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn search_walk_forward(&mut self, start_position: usize) {
|
||||||
|
self.process_search_state
|
||||||
|
.search_state
|
||||||
|
.grapheme_cursor
|
||||||
|
.next_boundary(
|
||||||
|
&self.process_search_state.search_state.current_search_query[start_position..],
|
||||||
|
start_position,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn search_walk_back(&mut self, start_position: usize) {
|
||||||
|
self.process_search_state
|
||||||
|
.search_state
|
||||||
|
.grapheme_cursor
|
||||||
|
.prev_boundary(
|
||||||
|
&self.process_search_state.search_state.current_search_query[..start_position],
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProcState {
|
||||||
|
pub widget_states: HashMap<u64, ProcWidgetState>,
|
||||||
|
pub force_update: Option<u64>,
|
||||||
|
pub force_update_all: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcState {
|
||||||
|
pub fn init(widget_states: HashMap<u64, ProcWidgetState>) -> Self {
|
||||||
|
ProcState {
|
||||||
|
widget_states,
|
||||||
|
force_update: None,
|
||||||
|
force_update_all: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NetWidgetState {
|
||||||
|
pub current_display_time: u64,
|
||||||
|
pub autohide_timer: Option<Instant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetWidgetState {
|
||||||
|
pub fn init(current_display_time: u64, autohide_timer: Option<Instant>) -> Self {
|
||||||
|
NetWidgetState {
|
||||||
|
current_display_time,
|
||||||
|
autohide_timer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NetState {
|
||||||
|
pub force_update: Option<u64>,
|
||||||
|
pub widget_states: HashMap<u64, NetWidgetState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetState {
|
||||||
|
pub fn init(widget_states: HashMap<u64, NetWidgetState>) -> Self {
|
||||||
|
NetState {
|
||||||
|
force_update: None,
|
||||||
|
widget_states,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CpuWidgetState {
|
||||||
|
pub current_display_time: u64,
|
||||||
|
pub is_legend_hidden: bool,
|
||||||
|
pub is_showing_tray: bool,
|
||||||
|
pub core_show_vec: Vec<bool>,
|
||||||
|
pub num_cpus_shown: usize,
|
||||||
|
pub autohide_timer: Option<Instant>,
|
||||||
|
pub scroll_state: AppScrollWidgetState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuWidgetState {
|
||||||
|
pub fn init(current_display_time: u64, autohide_timer: Option<Instant>) -> Self {
|
||||||
|
CpuWidgetState {
|
||||||
|
current_display_time,
|
||||||
|
is_legend_hidden: false,
|
||||||
|
is_showing_tray: false,
|
||||||
|
core_show_vec: Vec::new(),
|
||||||
|
num_cpus_shown: 0,
|
||||||
|
autohide_timer,
|
||||||
|
scroll_state: AppScrollWidgetState::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CpuState {
|
||||||
|
pub force_update: Option<u64>,
|
||||||
|
pub widget_states: HashMap<u64, CpuWidgetState>,
|
||||||
|
pub num_cpus_total: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuState {
|
||||||
|
pub fn init(widget_states: HashMap<u64, CpuWidgetState>) -> Self {
|
||||||
|
CpuState {
|
||||||
|
force_update: None,
|
||||||
|
widget_states,
|
||||||
|
num_cpus_total: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MemWidgetState {
|
||||||
|
pub current_display_time: u64,
|
||||||
|
pub autohide_timer: Option<Instant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemWidgetState {
|
||||||
|
pub fn init(current_display_time: u64, autohide_timer: Option<Instant>) -> Self {
|
||||||
|
MemWidgetState {
|
||||||
|
current_display_time,
|
||||||
|
autohide_timer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MemState {
|
||||||
|
pub force_update: Option<u64>,
|
||||||
|
pub widget_states: HashMap<u64, MemWidgetState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemState {
|
||||||
|
pub fn init(widget_states: HashMap<u64, MemWidgetState>) -> Self {
|
||||||
|
MemState {
|
||||||
|
force_update: None,
|
||||||
|
widget_states,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TempWidgetState {
|
||||||
|
pub scroll_state: AppScrollWidgetState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TempWidgetState {
|
||||||
|
pub fn init() -> Self {
|
||||||
|
TempWidgetState {
|
||||||
|
scroll_state: AppScrollWidgetState::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TempState {
|
||||||
|
pub widget_states: HashMap<u64, TempWidgetState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TempState {
|
||||||
|
pub fn init(widget_states: HashMap<u64, TempWidgetState>) -> Self {
|
||||||
|
TempState { widget_states }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DiskWidgetState {
|
||||||
|
pub scroll_state: AppScrollWidgetState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskWidgetState {
|
||||||
|
pub fn init() -> Self {
|
||||||
|
DiskWidgetState {
|
||||||
|
scroll_state: AppScrollWidgetState::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DiskState {
|
||||||
|
pub widget_states: HashMap<u64, DiskWidgetState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskState {
|
||||||
|
pub fn init(widget_states: HashMap<u64, DiskWidgetState>) -> Self {
|
||||||
|
DiskState { widget_states }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct BasicTableWidgetState {
|
||||||
|
// Since this is intended (currently) to only be used for ONE widget, that's
|
||||||
|
// how it's going to be written. If we want to allow for multiple of these,
|
||||||
|
// then we can expand outwards with a normal BasicTableState and a hashmap
|
||||||
|
pub currently_displayed_widget_type: BottomWidgetType,
|
||||||
|
pub currently_displayed_widget_id: u64,
|
||||||
|
pub widget_id: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct BatteryWidgetState {
|
||||||
|
pub currently_selected_battery_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BatteryState {
|
||||||
|
pub widget_states: HashMap<u64, BatteryWidgetState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BatteryState {
|
||||||
|
pub fn init(widget_states: HashMap<u64, BatteryWidgetState>) -> Self {
|
||||||
|
BatteryState { widget_states }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ParagraphScrollState {
|
||||||
|
pub current_scroll_index: u16,
|
||||||
|
pub max_scroll_index: u16,
|
||||||
|
}
|
@ -350,6 +350,7 @@ impl Painter {
|
|||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
rect[0],
|
rect[0],
|
||||||
|
true,
|
||||||
app_state.current_widget.widget_id,
|
app_state.current_widget.widget_id,
|
||||||
),
|
),
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -410,6 +411,13 @@ impl Painter {
|
|||||||
false,
|
false,
|
||||||
widget_id,
|
widget_id,
|
||||||
),
|
),
|
||||||
|
Battery => self.draw_battery_display(
|
||||||
|
&mut f,
|
||||||
|
app_state,
|
||||||
|
vertical_chunks[4],
|
||||||
|
false,
|
||||||
|
widget_id,
|
||||||
|
),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -541,9 +549,13 @@ impl Painter {
|
|||||||
true,
|
true,
|
||||||
widget.widget_id,
|
widget.widget_id,
|
||||||
),
|
),
|
||||||
Battery => {
|
Battery => self.draw_battery_display(
|
||||||
self.draw_battery_display(f, app_state, *widget_draw_loc, widget.widget_id)
|
f,
|
||||||
}
|
app_state,
|
||||||
|
*widget_draw_loc,
|
||||||
|
true,
|
||||||
|
widget.widget_id,
|
||||||
|
),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,19 +23,34 @@ impl BasicTableArrows for Painter {
|
|||||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||||
) {
|
) {
|
||||||
// Effectively a paragraph with a ton of spacing
|
// Effectively a paragraph with a ton of spacing
|
||||||
let (left_table, right_table) =
|
let (left_table, right_table) = (
|
||||||
if let Some(basic_table_widget_state) = &app_state.basic_table_widget_state {
|
{
|
||||||
match basic_table_widget_state.currently_displayed_widget_type {
|
app_state
|
||||||
BottomWidgetType::Proc | BottomWidgetType::ProcSearch => {
|
.current_widget
|
||||||
(BottomWidgetType::Temp, BottomWidgetType::Disk)
|
.left_neighbour
|
||||||
}
|
.map(|left_widget_id| {
|
||||||
BottomWidgetType::Disk => (BottomWidgetType::Proc, BottomWidgetType::Temp),
|
app_state
|
||||||
BottomWidgetType::Temp => (BottomWidgetType::Disk, BottomWidgetType::Proc),
|
.widget_map
|
||||||
_ => (BottomWidgetType::Disk, BottomWidgetType::Temp),
|
.get(&left_widget_id)
|
||||||
}
|
.map(|left_widget| &left_widget.widget_type)
|
||||||
} else {
|
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
||||||
(BottomWidgetType::Disk, BottomWidgetType::Temp)
|
})
|
||||||
};
|
.unwrap_or_else(|| &BottomWidgetType::Temp)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
app_state
|
||||||
|
.current_widget
|
||||||
|
.right_neighbour
|
||||||
|
.map(|right_widget_id| {
|
||||||
|
app_state
|
||||||
|
.widget_map
|
||||||
|
.get(&right_widget_id)
|
||||||
|
.map(|right_widget| &right_widget.widget_type)
|
||||||
|
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| &BottomWidgetType::Disk)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let left_name = left_table.get_pretty_name();
|
let left_name = left_table.get_pretty_name();
|
||||||
let right_name = right_table.get_pretty_name();
|
let right_name = right_table.get_pretty_name();
|
||||||
@ -46,12 +61,9 @@ impl BasicTableArrows for Painter {
|
|||||||
) as usize;
|
) as usize;
|
||||||
|
|
||||||
let arrow_text = vec![
|
let arrow_text = vec![
|
||||||
Text::Styled(
|
Text::Styled(format!("\n◄ {}", left_name).into(), self.colours.text_style),
|
||||||
format!("\n◄ {}", right_name).into(),
|
|
||||||
self.colours.text_style,
|
|
||||||
),
|
|
||||||
Text::Raw(" ".repeat(num_spaces).into()),
|
Text::Raw(" ".repeat(num_spaces).into()),
|
||||||
Text::Styled(format!("{} ►", left_name).into(), self.colours.text_style),
|
Text::Styled(format!("{} ►", right_name).into(), self.colours.text_style),
|
||||||
];
|
];
|
||||||
|
|
||||||
let margined_draw_loc = Layout::default()
|
let margined_draw_loc = Layout::default()
|
||||||
|
@ -3,6 +3,7 @@ use std::cmp::max;
|
|||||||
use crate::{
|
use crate::{
|
||||||
app::App,
|
app::App,
|
||||||
canvas::{drawing_utils::calculate_basic_use_bars, Painter},
|
canvas::{drawing_utils::calculate_basic_use_bars, Painter},
|
||||||
|
constants::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use tui::{
|
use tui::{
|
||||||
@ -14,17 +15,20 @@ use tui::{
|
|||||||
|
|
||||||
pub trait BatteryDisplayWidget {
|
pub trait BatteryDisplayWidget {
|
||||||
fn draw_battery_display<B: Backend>(
|
fn draw_battery_display<B: Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||||
|
widget_id: u64,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BatteryDisplayWidget for Painter {
|
impl BatteryDisplayWidget for Painter {
|
||||||
fn draw_battery_display<B: Backend>(
|
fn draw_battery_display<B: Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||||
|
widget_id: u64,
|
||||||
) {
|
) {
|
||||||
if let Some(battery_widget_state) =
|
if let Some(battery_widget_state) =
|
||||||
app_state.battery_state.widget_states.get_mut(&widget_id)
|
app_state.battery_state.widget_states.get_mut(&widget_id)
|
||||||
{
|
{
|
||||||
|
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||||
let title = if app_state.is_expanded {
|
let title = if app_state.is_expanded {
|
||||||
const TITLE_BASE: &str = " Battery ── Esc to go back ";
|
const TITLE_BASE: &str = " Battery ── Esc to go back ";
|
||||||
let repeat_num = max(
|
let repeat_num = max(
|
||||||
@ -40,13 +44,14 @@ impl BatteryDisplayWidget for Painter {
|
|||||||
" Battery ".to_string()
|
" Battery ".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let border_and_title_style = if app_state.current_widget.widget_id == widget_id {
|
let border_and_title_style = if is_on_widget {
|
||||||
self.colours.highlighted_border_style
|
self.colours.highlighted_border_style
|
||||||
} else {
|
} else {
|
||||||
self.colours.border_style
|
self.colours.border_style
|
||||||
};
|
};
|
||||||
|
|
||||||
let battery_block = Block::default()
|
let battery_block = if draw_border {
|
||||||
|
Block::default()
|
||||||
.title(&title)
|
.title(&title)
|
||||||
.title_style(if app_state.is_expanded {
|
.title_style(if app_state.is_expanded {
|
||||||
border_and_title_style
|
border_and_title_style
|
||||||
@ -54,7 +59,14 @@ impl BatteryDisplayWidget for Painter {
|
|||||||
self.colours.widget_title_style
|
self.colours.widget_title_style
|
||||||
})
|
})
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(border_and_title_style);
|
.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(battery_details) = app_state
|
if let Some(battery_details) = app_state
|
||||||
.canvas_data
|
.canvas_data
|
||||||
|
@ -94,7 +94,11 @@ impl TempTableWidget for Painter {
|
|||||||
let temp_block = if draw_border {
|
let temp_block = if draw_border {
|
||||||
Block::default()
|
Block::default()
|
||||||
.title(&title)
|
.title(&title)
|
||||||
.title_style(border_and_title_style)
|
.title_style(if app_state.is_expanded {
|
||||||
|
border_and_title_style
|
||||||
|
} else {
|
||||||
|
self.colours.widget_title_style
|
||||||
|
})
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(border_and_title_style)
|
.border_style(border_and_title_style)
|
||||||
} else if is_on_widget {
|
} else if is_on_widget {
|
||||||
|
@ -92,6 +92,7 @@ fn get_matches() -> clap::ArgMatches<'static> {
|
|||||||
(@arg DEFAULT_WIDGET_COUNT: --default_widget_count +takes_value "Which number of the selected widget type to select, from left to right, top to bottom. Defaults to 1.")
|
(@arg DEFAULT_WIDGET_COUNT: --default_widget_count +takes_value "Which number of the selected widget type to select, from left to right, top to bottom. Defaults to 1.")
|
||||||
(@arg USE_OLD_NETWORK_LEGEND: --use_old_network_legend "Use the older (pre-0.4) network widget legend.")
|
(@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 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.")
|
||||||
)
|
)
|
||||||
.get_matches()
|
.get_matches()
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ pub struct ConfigFlags {
|
|||||||
pub default_widget_count: Option<u64>,
|
pub default_widget_count: Option<u64>,
|
||||||
pub use_old_network_legend: Option<bool>,
|
pub use_old_network_legend: Option<bool>,
|
||||||
pub hide_table_gap: Option<bool>,
|
pub hide_table_gap: Option<bool>,
|
||||||
|
pub battery: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Deserialize)]
|
#[derive(Default, Deserialize)]
|
||||||
@ -261,7 +262,7 @@ pub fn get_widget_layout(
|
|||||||
|
|
||||||
let bottom_layout = if get_use_basic_mode(matches, config) {
|
let bottom_layout = if get_use_basic_mode(matches, config) {
|
||||||
default_widget_id = DEFAULT_WIDGET_ID;
|
default_widget_id = DEFAULT_WIDGET_ID;
|
||||||
BottomLayout::init_basic_default()
|
BottomLayout::init_basic_default(get_use_battery(matches, config))
|
||||||
} else if let Some(rows) = &config.row {
|
} else if let Some(rows) = &config.row {
|
||||||
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
|
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
|
||||||
let mut total_height_ratio = 0;
|
let mut total_height_ratio = 0;
|
||||||
@ -283,7 +284,7 @@ pub fn get_widget_layout(
|
|||||||
total_row_height_ratio: total_height_ratio,
|
total_row_height_ratio: total_height_ratio,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Confirm that we have at least ONE widget - if we don't, go back to default!
|
// Confirm that we have at least ONE widget - if not, error out!
|
||||||
if iter_id > 0 {
|
if iter_id > 0 {
|
||||||
ret_bottom_layout.get_movement_mappings();
|
ret_bottom_layout.get_movement_mappings();
|
||||||
ret_bottom_layout
|
ret_bottom_layout
|
||||||
@ -294,7 +295,7 @@ pub fn get_widget_layout(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
default_widget_id = DEFAULT_WIDGET_ID;
|
default_widget_id = DEFAULT_WIDGET_ID;
|
||||||
BottomLayout::init_default(left_legend)
|
BottomLayout::init_default(left_legend, get_use_battery(matches, config))
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((bottom_layout, default_widget_id))
|
Ok((bottom_layout, default_widget_id))
|
||||||
@ -636,3 +637,16 @@ pub fn get_hide_table_gap(matches: &clap::ArgMatches<'static>, config: &Config)
|
|||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_use_battery(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||||
|
if matches.is_present("BATTERY") {
|
||||||
|
return true;
|
||||||
|
} else if let Some(flags) = &config.flags {
|
||||||
|
if let Some(battery) = flags.battery {
|
||||||
|
if battery {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user