mirror of
https://github.com/ClementTsang/bottom
synced 2024-11-22 04:03:06 +00:00
feature: Beginnings of in-app config (#231)
Initial refactorings and additions to support in-app config. - Refactor our current options logic to support in-app configs. That is, we can write to a config file with our changes now. - The default action when creating a new config file is to leave it blank. (TBD and for now, not sure on this one) - Previously, we would set everything in a config file on startup; now we need to read from the config TOML struct whenever. - `C` keybind is now occupied for configs. - `no_write` option to never write to a config file.
This commit is contained in:
parent
b0b174eb98
commit
6db76029e2
20 changed files with 474 additions and 396 deletions
|
@ -7,5 +7,5 @@ echo "Running pre-push hook:"
|
|||
echo "Executing: cargo +nightly clippy -- -D clippy::all"
|
||||
cargo +nightly clippy -- -D clippy::all
|
||||
|
||||
echo "Executing: cargo test"
|
||||
cargo test
|
||||
# echo "Executing: cargo test"
|
||||
# cargo test
|
||||
|
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -55,6 +55,7 @@
|
|||
"hjkl",
|
||||
"htop",
|
||||
"indexmap",
|
||||
"keybinds",
|
||||
"libc",
|
||||
"markdownlint",
|
||||
"memb",
|
||||
|
|
|
@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- [#223](https://github.com/ClementTsang/bottom/pull/223): Add tree mode for processes.
|
||||
|
||||
- [](): Add in-app configuration.
|
||||
|
||||
### Changes
|
||||
|
||||
- [#213](https://github.com/ClementTsang/bottom/pull/213), [#214](https://github.com/ClementTsang/bottom/pull/214): Updated help descriptions, added auto-complete generation.
|
||||
|
|
|
@ -46,7 +46,7 @@ If you want to help contribute by submitting a PR, by all means, I'm open! In re
|
|||
|
||||
- You can check clippy using `cargo clippy`.
|
||||
|
||||
- I use [cargo-husky](https://github.com/rhysd/cargo-husky) to automatically run a `cargo clippy` and `cargo test` check.
|
||||
- I use [cargo-husky](https://github.com/rhysd/cargo-husky) to automatically run a `cargo clippy` check.
|
||||
|
||||
- You may notice that I have fern and log as dependencies; this is mostly for easy debugging via the `debug!()` macro. It writes to the `debug.log` file that will automatically be created if you run in debug mode (so `cargo run`).
|
||||
|
||||
|
|
101
src/app.rs
101
src/app.rs
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::HashMap, time::Instant};
|
||||
use std::{collections::HashMap, io::Write, path::PathBuf, time::Instant};
|
||||
|
||||
use unicode_segmentation::GraphemeCursor;
|
||||
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||
|
@ -12,6 +12,7 @@ pub use states::*;
|
|||
|
||||
use crate::{
|
||||
canvas, constants,
|
||||
options::Config,
|
||||
utils::error::{BottomError, Result},
|
||||
Pid,
|
||||
};
|
||||
|
@ -42,6 +43,7 @@ pub struct AppConfigFields {
|
|||
pub use_old_network_legend: bool,
|
||||
pub table_gap: u16,
|
||||
pub disable_click: bool,
|
||||
pub no_write: bool,
|
||||
}
|
||||
|
||||
/// For filtering out information
|
||||
|
@ -100,6 +102,9 @@ pub struct App {
|
|||
#[builder(default = false, setter(skip))]
|
||||
pub basic_mode_use_percent: bool,
|
||||
|
||||
#[builder(default = false, setter(skip))]
|
||||
pub is_config_open: bool,
|
||||
|
||||
pub cpu_state: CpuState,
|
||||
pub mem_state: MemState,
|
||||
pub net_state: NetState,
|
||||
|
@ -113,6 +118,8 @@ pub struct App {
|
|||
pub current_widget: BottomWidget,
|
||||
pub used_widgets: UsedWidgets,
|
||||
pub filters: DataFilters,
|
||||
pub config: Config,
|
||||
pub config_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
@ -171,6 +178,8 @@ impl App {
|
|||
}
|
||||
|
||||
self.is_force_redraw = true;
|
||||
} else if self.is_config_open {
|
||||
self.close_config();
|
||||
} else {
|
||||
match self.current_widget.widget_type {
|
||||
BottomWidgetType::Proc => {
|
||||
|
@ -247,10 +256,14 @@ impl App {
|
|||
self.help_dialog_state.is_showing_help || self.delete_dialog_state.is_showing_dd
|
||||
}
|
||||
|
||||
pub fn on_tab(&mut self) {
|
||||
// Disallow usage whilst in a dialog and only in processes
|
||||
fn ignore_normal_keybinds(&self) -> bool {
|
||||
self.is_config_open || self.is_in_dialog()
|
||||
}
|
||||
|
||||
if !self.is_in_dialog() {
|
||||
pub fn on_tab(&mut self) {
|
||||
// Allow usage whilst only in processes
|
||||
|
||||
if !self.ignore_normal_keybinds() {
|
||||
match self.current_widget.widget_type {
|
||||
BottomWidgetType::Cpu => {
|
||||
if let Some(cpu_widget_state) = self
|
||||
|
@ -319,7 +332,7 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn on_slash(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if !self.ignore_normal_keybinds() {
|
||||
match &self.current_widget.widget_type {
|
||||
BottomWidgetType::Proc | BottomWidgetType::ProcSort => {
|
||||
// Toggle on
|
||||
|
@ -452,6 +465,8 @@ impl App {
|
|||
.search_toggle_ignore_case();
|
||||
proc_widget_state.update_query();
|
||||
self.proc_state.force_update = Some(self.current_widget.widget_id - 1);
|
||||
|
||||
// Also toggle it in the config file.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -653,7 +668,8 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn on_up_key(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if self.is_config_open {
|
||||
} else if !self.is_in_dialog() {
|
||||
self.decrement_position_count();
|
||||
} else if self.help_dialog_state.is_showing_help {
|
||||
self.help_scroll_up();
|
||||
|
@ -662,7 +678,8 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn on_down_key(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if self.is_config_open {
|
||||
} else if !self.is_in_dialog() {
|
||||
self.increment_position_count();
|
||||
} else if self.help_dialog_state.is_showing_help {
|
||||
self.help_scroll_down();
|
||||
|
@ -671,7 +688,8 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn on_left_key(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if self.is_config_open {
|
||||
} else if !self.is_in_dialog() {
|
||||
match self.current_widget.widget_type {
|
||||
BottomWidgetType::Proc => {
|
||||
// if let Some(proc_widget_state) = self
|
||||
|
@ -735,7 +753,8 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn on_right_key(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if self.is_config_open {
|
||||
} else if !self.is_in_dialog() {
|
||||
match self.current_widget.widget_type {
|
||||
BottomWidgetType::Proc => {
|
||||
// if let Some(proc_widget_state) = self
|
||||
|
@ -804,7 +823,7 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn skip_cursor_beginning(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if !self.ignore_normal_keybinds() {
|
||||
if let BottomWidgetType::ProcSearch = self.current_widget.widget_type {
|
||||
let is_in_search_widget = self.is_in_search_widget();
|
||||
if let Some(proc_widget_state) = self
|
||||
|
@ -836,11 +855,12 @@ impl App {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if self.is_config_open {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn skip_cursor_end(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if !self.ignore_normal_keybinds() {
|
||||
if let BottomWidgetType::ProcSearch = self.current_widget.widget_type {
|
||||
let is_in_search_widget = self.is_in_search_widget();
|
||||
if let Some(proc_widget_state) = self
|
||||
|
@ -882,6 +902,7 @@ impl App {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if self.is_config_open {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -945,7 +966,7 @@ impl App {
|
|||
}
|
||||
|
||||
// Forbid any char key presses when showing a dialog box...
|
||||
if !self.is_in_dialog() {
|
||||
if !self.ignore_normal_keybinds() {
|
||||
let current_key_press_inst = Instant::now();
|
||||
if current_key_press_inst
|
||||
.duration_since(self.last_key_press)
|
||||
|
@ -1034,6 +1055,7 @@ impl App {
|
|||
'k' | 'l' => self.on_right_key(),
|
||||
_ => {}
|
||||
}
|
||||
} else if self.is_config_open {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1086,6 +1108,9 @@ impl App {
|
|||
self.data_collection.set_frozen_time();
|
||||
}
|
||||
}
|
||||
'C' => {
|
||||
// self.open_config(),
|
||||
}
|
||||
'c' => {
|
||||
if let BottomWidgetType::Proc = self.current_widget.widget_type {
|
||||
if let Some(proc_widget_state) = self
|
||||
|
@ -1227,6 +1252,7 @@ impl App {
|
|||
's' => self.toggle_sort(),
|
||||
'I' => self.invert_sort(),
|
||||
'%' => self.toggle_percentages(),
|
||||
' ' => self.on_space(),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -1237,6 +1263,38 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn on_space(&mut self) {}
|
||||
|
||||
pub fn open_config(&mut self) {
|
||||
self.is_config_open = true;
|
||||
self.is_force_redraw = true;
|
||||
}
|
||||
|
||||
pub fn close_config(&mut self) {
|
||||
self.is_config_open = false;
|
||||
self.is_force_redraw = true;
|
||||
}
|
||||
|
||||
/// Call this whenever the config value is updated!
|
||||
fn update_config_file(&mut self) -> anyhow::Result<()> {
|
||||
if self.app_config_fields.no_write {
|
||||
// Don't write!
|
||||
// FIXME: [CONFIG] This should be made VERY clear to the user... make a thing saying "it will not write due to no_write option"
|
||||
Ok(())
|
||||
} else if let Some(config_path) = &self.config_path {
|
||||
// Update
|
||||
std::fs::File::open(config_path)?
|
||||
.write_all(toml::to_string(&self.config)?.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
// FIXME: [CONFIG] Put an actual error message?
|
||||
Err(anyhow::anyhow!(
|
||||
"Config path was missing, please try restarting bottom..."
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kill_highlighted_process(&mut self) -> Result<()> {
|
||||
if let BottomWidgetType::Proc = self.current_widget.widget_type {
|
||||
if let Some(current_selected_processes) = &self.to_delete_process_list {
|
||||
|
@ -1268,7 +1326,8 @@ impl App {
|
|||
}
|
||||
|
||||
fn expand_widget(&mut self) {
|
||||
if !self.is_in_dialog() && !self.app_config_fields.use_basic_mode {
|
||||
// TODO: [BASIC] Expansion in basic mode.
|
||||
if !self.ignore_normal_keybinds() && !self.app_config_fields.use_basic_mode {
|
||||
// Pop-out mode. We ignore if in process search.
|
||||
|
||||
match self.current_widget.widget_type {
|
||||
|
@ -1300,7 +1359,7 @@ impl App {
|
|||
- Reflection direction.
|
||||
*/
|
||||
|
||||
if !self.is_in_dialog() && !self.is_expanded {
|
||||
if !self.ignore_normal_keybinds() && !self.is_expanded {
|
||||
if let Some(new_widget_id) = &(match direction {
|
||||
WidgetDirection::Left => self.current_widget.left_neighbour,
|
||||
WidgetDirection::Right => self.current_widget.right_neighbour,
|
||||
|
@ -1731,7 +1790,7 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn skip_to_first(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if !self.ignore_normal_keybinds() {
|
||||
match self.current_widget.widget_type {
|
||||
BottomWidgetType::Proc => {
|
||||
if let Some(proc_widget_state) = self
|
||||
|
@ -1782,13 +1841,14 @@ impl App {
|
|||
_ => {}
|
||||
}
|
||||
self.reset_multi_tap_keys();
|
||||
} else {
|
||||
} else if self.is_config_open {
|
||||
} else if self.help_dialog_state.is_showing_help {
|
||||
self.help_dialog_state.scroll_state.current_scroll_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn skip_to_last(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if !self.ignore_normal_keybinds() {
|
||||
match self.current_widget.widget_type {
|
||||
BottomWidgetType::Proc => {
|
||||
if let Some(proc_widget_state) = self
|
||||
|
@ -1858,7 +1918,8 @@ impl App {
|
|||
_ => {}
|
||||
}
|
||||
self.reset_multi_tap_keys();
|
||||
} else {
|
||||
} else if self.is_config_open {
|
||||
} else if self.help_dialog_state.is_showing_help {
|
||||
self.help_dialog_state.scroll_state.current_scroll_index = self
|
||||
.help_dialog_state
|
||||
.scroll_state
|
||||
|
@ -1868,7 +1929,7 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn decrement_position_count(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if !self.ignore_normal_keybinds() {
|
||||
match self.current_widget.widget_type {
|
||||
BottomWidgetType::Proc => self.increment_process_position(-1),
|
||||
BottomWidgetType::ProcSort => self.increment_process_sort_position(-1),
|
||||
|
@ -1881,7 +1942,7 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn increment_position_count(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if !self.ignore_normal_keybinds() {
|
||||
match self.current_widget.widget_type {
|
||||
BottomWidgetType::Proc => self.increment_process_position(1),
|
||||
BottomWidgetType::ProcSort => self.increment_process_sort_position(1),
|
||||
|
|
|
@ -187,7 +187,11 @@ impl ProcessQuery for ProcWidgetState {
|
|||
let initial_or = Or {
|
||||
lhs: And {
|
||||
lhs: Prefix {
|
||||
or: Some(Box::new(list_of_ors.pop_front().unwrap())),
|
||||
or: if let Some(or) = list_of_ors.pop_front() {
|
||||
Some(Box::new(or))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
compare_prefix: None,
|
||||
regex_prefix: None,
|
||||
},
|
||||
|
|
|
@ -280,6 +280,7 @@ impl Default for ProcColumn {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: [SORTING] Sort by clicking on column header (ie: click on cpu, sort/invert cpu sort)?
|
||||
impl ProcColumn {
|
||||
/// Returns its new status.
|
||||
pub fn toggle(&mut self, column: &ProcessSorting) -> Option<bool> {
|
||||
|
@ -303,8 +304,12 @@ impl ProcColumn {
|
|||
self.ordered_columns
|
||||
.iter()
|
||||
.filter_map(|column_type| {
|
||||
if self.column_mapping.get(&column_type).unwrap().enabled {
|
||||
Some(1)
|
||||
if let Some(col_map) = self.column_mapping.get(&column_type) {
|
||||
if col_map.enabled {
|
||||
Some(1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -467,16 +472,20 @@ impl ProcWidgetState {
|
|||
}
|
||||
|
||||
pub fn toggle_command_and_name(&mut self, is_using_command: bool) {
|
||||
self.columns
|
||||
if let Some(pn) = self
|
||||
.columns
|
||||
.column_mapping
|
||||
.get_mut(&ProcessSorting::ProcessName)
|
||||
.unwrap()
|
||||
.enabled = !is_using_command;
|
||||
self.columns
|
||||
{
|
||||
pn.enabled = !is_using_command;
|
||||
}
|
||||
if let Some(c) = self
|
||||
.columns
|
||||
.column_mapping
|
||||
.get_mut(&ProcessSorting::Command)
|
||||
.unwrap()
|
||||
.enabled = is_using_command;
|
||||
{
|
||||
c.enabled = is_using_command;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cursor_position(&self) -> usize {
|
||||
|
@ -798,3 +807,19 @@ pub struct ParagraphScrollState {
|
|||
pub current_scroll_index: u16,
|
||||
pub max_scroll_index: u16,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ConfigState {
|
||||
pub current_category_index: usize,
|
||||
pub category_list: Vec<ConfigCategory>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ConfigCategory {
|
||||
pub category_name: &'static str,
|
||||
pub options_list: Vec<ConfigOption>,
|
||||
}
|
||||
|
||||
pub struct ConfigOption {
|
||||
pub set_function: Box<dyn Fn() -> anyhow::Result<()>>,
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ fn main() -> Result<()> {
|
|||
}
|
||||
let matches = clap::get_matches();
|
||||
|
||||
let config_path = read_config(matches.value_of("CONFIG_LOCATION"))
|
||||
let config_path = read_config(matches.value_of("config_location"))
|
||||
.context("Unable to access the given config file location.")?;
|
||||
let config: Config = create_or_get_config(&config_path)
|
||||
.context("Unable to properly parse or create the config file.")?;
|
||||
|
@ -49,6 +49,7 @@ fn main() -> Result<()> {
|
|||
&widget_layout,
|
||||
default_widget_id,
|
||||
&default_widget_type_option,
|
||||
config_path,
|
||||
)?;
|
||||
|
||||
// Create painter and set colours.
|
||||
|
@ -56,10 +57,8 @@ fn main() -> Result<()> {
|
|||
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();
|
||||
&config,
|
||||
)?;
|
||||
|
||||
// Set up input handling
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
|
@ -80,7 +79,7 @@ fn main() -> Result<()> {
|
|||
|
||||
// Event loop
|
||||
let (reset_sender, reset_receiver) = mpsc::channel();
|
||||
create_event_thread(
|
||||
create_collection_thread(
|
||||
sender,
|
||||
reset_receiver,
|
||||
&app.app_config_fields,
|
||||
|
@ -105,8 +104,7 @@ fn main() -> Result<()> {
|
|||
ctrlc::set_handler(move || {
|
||||
ist_clone.store(true, Ordering::SeqCst);
|
||||
termination_hook();
|
||||
})
|
||||
.unwrap();
|
||||
})?;
|
||||
let mut first_run = true;
|
||||
|
||||
while !is_terminated.load(Ordering::SeqCst) {
|
||||
|
|
140
src/canvas.rs
140
src/canvas.rs
|
@ -1,3 +1,4 @@
|
|||
use anyhow::Context;
|
||||
use itertools::izip;
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -10,6 +11,7 @@ use tui::{
|
|||
|
||||
use canvas_colours::*;
|
||||
use dialogs::*;
|
||||
use screens::*;
|
||||
use widgets::*;
|
||||
|
||||
use crate::{
|
||||
|
@ -20,12 +22,14 @@ use crate::{
|
|||
},
|
||||
constants::*,
|
||||
data_conversion::{ConvertedBatteryData, ConvertedCpuData, ConvertedProcessData},
|
||||
options::Config,
|
||||
utils::error,
|
||||
};
|
||||
|
||||
mod canvas_colours;
|
||||
mod dialogs;
|
||||
mod drawing_utils;
|
||||
mod screens;
|
||||
mod widgets;
|
||||
|
||||
/// Point is of time, data
|
||||
|
@ -65,13 +69,15 @@ pub struct Painter {
|
|||
col_constraints: Vec<Vec<Constraint>>,
|
||||
col_row_constraints: Vec<Vec<Vec<Constraint>>>,
|
||||
layout_constraints: Vec<Vec<Vec<Vec<Constraint>>>>,
|
||||
widget_layout: BottomLayout,
|
||||
derived_widget_draw_locs: Vec<Vec<Vec<Vec<Rect>>>>,
|
||||
widget_layout: BottomLayout,
|
||||
table_height_offset: u16,
|
||||
}
|
||||
|
||||
impl Painter {
|
||||
pub fn init(widget_layout: BottomLayout, table_gap: u16, is_basic_mode: bool) -> Self {
|
||||
pub fn init(
|
||||
widget_layout: BottomLayout, table_gap: u16, is_basic_mode: bool, config: &Config,
|
||||
) -> anyhow::Result<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.
|
||||
|
@ -139,7 +145,7 @@ impl Painter {
|
|||
col_constraints.push(new_col_constraints);
|
||||
});
|
||||
|
||||
Painter {
|
||||
let mut painter = Painter {
|
||||
colours: CanvasColours::default(),
|
||||
height: 0,
|
||||
width: 0,
|
||||
|
@ -152,12 +158,128 @@ impl Painter {
|
|||
widget_layout,
|
||||
derived_widget_draw_locs: Vec::default(),
|
||||
table_height_offset: if is_basic_mode { 2 } else { 4 } + table_gap,
|
||||
};
|
||||
|
||||
painter.generate_config_colours(config)?;
|
||||
painter.colours.generate_remaining_cpu_colours();
|
||||
painter.complete_painter_init();
|
||||
|
||||
Ok(painter)
|
||||
}
|
||||
|
||||
pub fn generate_config_colours(&mut self, config: &Config) -> anyhow::Result<()> {
|
||||
if let Some(colours) = &config.colors {
|
||||
if let Some(border_color) = &colours.border_color {
|
||||
self.colours
|
||||
.set_border_colour(border_color)
|
||||
.context("Update 'border_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(highlighted_border_color) = &colours.highlighted_border_color {
|
||||
self.colours
|
||||
.set_highlighted_border_colour(highlighted_border_color)
|
||||
.context("Update 'highlighted_border_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(text_color) = &colours.text_color {
|
||||
self.colours
|
||||
.set_text_colour(text_color)
|
||||
.context("Update 'text_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(avg_cpu_color) = &colours.avg_cpu_color {
|
||||
self.colours
|
||||
.set_avg_cpu_colour(avg_cpu_color)
|
||||
.context("Update 'avg_cpu_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(all_cpu_color) = &colours.all_cpu_color {
|
||||
self.colours
|
||||
.set_all_cpu_colour(all_cpu_color)
|
||||
.context("Update 'all_cpu_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(cpu_core_colors) = &colours.cpu_core_colors {
|
||||
self.colours
|
||||
.set_cpu_colours(cpu_core_colors)
|
||||
.context("Update 'cpu_core_colors' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(ram_color) = &colours.ram_color {
|
||||
self.colours
|
||||
.set_ram_colour(ram_color)
|
||||
.context("Update 'ram_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(swap_color) = &colours.swap_color {
|
||||
self.colours
|
||||
.set_swap_colour(swap_color)
|
||||
.context("Update 'swap_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(rx_color) = &colours.rx_color {
|
||||
self.colours
|
||||
.set_rx_colour(rx_color)
|
||||
.context("Update 'rx_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(tx_color) = &colours.tx_color {
|
||||
self.colours
|
||||
.set_tx_colour(tx_color)
|
||||
.context("Update 'tx_color' in your config file..")?;
|
||||
}
|
||||
|
||||
// if let Some(rx_total_color) = &colours.rx_total_color {
|
||||
// painter.colours.set_rx_total_colour(rx_total_color)?;
|
||||
// }
|
||||
|
||||
// if let Some(tx_total_color) = &colours.tx_total_color {
|
||||
// painter.colours.set_tx_total_colour(tx_total_color)?;
|
||||
// }
|
||||
|
||||
if let Some(table_header_color) = &colours.table_header_color {
|
||||
self.colours
|
||||
.set_table_header_colour(table_header_color)
|
||||
.context("Update 'table_header_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(scroll_entry_text_color) = &colours.selected_text_color {
|
||||
self.colours
|
||||
.set_scroll_entry_text_color(scroll_entry_text_color)
|
||||
.context("Update 'selected_text_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(scroll_entry_bg_color) = &colours.selected_bg_color {
|
||||
self.colours
|
||||
.set_scroll_entry_bg_color(scroll_entry_bg_color)
|
||||
.context("Update 'selected_bg_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(widget_title_color) = &colours.widget_title_color {
|
||||
self.colours
|
||||
.set_widget_title_colour(widget_title_color)
|
||||
.context("Update 'widget_title_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(graph_color) = &colours.graph_color {
|
||||
self.colours
|
||||
.set_graph_colour(graph_color)
|
||||
.context("Update 'graph_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(battery_colors) = &colours.battery_colors {
|
||||
self.colours
|
||||
.set_battery_colors(battery_colors)
|
||||
.context("Update 'battery_colors' in your config file.")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Must be run once before drawing, but after setting colours.
|
||||
/// This is to set some remaining styles and text.
|
||||
pub fn complete_painter_init(&mut self) {
|
||||
fn complete_painter_init(&mut self) {
|
||||
self.is_mac_os = cfg!(target_os = "macos");
|
||||
let mut styled_help_spans = Vec::new();
|
||||
|
||||
|
@ -191,6 +313,9 @@ impl Painter {
|
|||
self.styled_help_text = styled_help_spans;
|
||||
}
|
||||
|
||||
// FIXME: [CONFIG] write this, should call painter init and any changed colour functions...
|
||||
pub fn update_painter_colours(&mut self) {}
|
||||
|
||||
pub fn draw_data<B: Backend>(
|
||||
&mut self, terminal: &mut Terminal<B>, app_state: &mut app::App,
|
||||
) -> error::Result<()> {
|
||||
|
@ -406,6 +531,13 @@ impl Painter {
|
|||
),
|
||||
_ => {}
|
||||
}
|
||||
} else if app_state.is_config_open {
|
||||
let rect = Layout::default()
|
||||
.margin(0)
|
||||
.constraints([Constraint::Percentage(100)].as_ref())
|
||||
.split(f.size())[0];
|
||||
|
||||
self.draw_config_screen(&mut f, app_state, rect)
|
||||
} else if app_state.app_config_fields.use_basic_mode {
|
||||
// Basic mode. This basically removes all graphs but otherwise
|
||||
// the same info.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use crate::{app::App, canvas::Painter, constants};
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Alignment, Rect},
|
||||
|
@ -7,8 +8,6 @@ use tui::{
|
|||
widgets::{Block, Borders, Paragraph},
|
||||
};
|
||||
|
||||
use crate::{app::App, canvas::Painter, constants};
|
||||
|
||||
const HELP_BASE: &str = " Help ── Esc to close ";
|
||||
|
||||
pub trait HelpDialog {
|
||||
|
@ -17,6 +16,7 @@ pub trait HelpDialog {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO: [REFACTOR] Make generic dialog boxes to build off of instead?
|
||||
impl HelpDialog for Painter {
|
||||
fn draw_help_dialog<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
|
|
3
src/canvas/screens.rs
Normal file
3
src/canvas/screens.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod config_screen;
|
||||
|
||||
pub use config_screen::*;
|
57
src/canvas/screens/config_screen.rs
Normal file
57
src/canvas/screens/config_screen.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use crate::{app::App, canvas::Painter, constants};
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::Constraint,
|
||||
layout::Direction,
|
||||
layout::Layout,
|
||||
layout::{Alignment, Rect},
|
||||
terminal::Frame,
|
||||
widgets::{Block, Borders, Paragraph},
|
||||
};
|
||||
|
||||
pub trait ConfigScreen {
|
||||
fn draw_config_screen<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
);
|
||||
}
|
||||
|
||||
impl ConfigScreen for Painter {
|
||||
fn draw_config_screen<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect,
|
||||
) {
|
||||
let config_block = Block::default()
|
||||
.title(&" Config ")
|
||||
.title_style(self.colours.border_style)
|
||||
.style(self.colours.border_style)
|
||||
.borders(Borders::ALL)
|
||||
.border_style(self.colours.border_style);
|
||||
|
||||
f.render_widget(config_block, draw_loc);
|
||||
|
||||
// let margined_draw_locs = Layout::default()
|
||||
// .margin(2)
|
||||
// .direction(Direction::Horizontal)
|
||||
// .constraints(
|
||||
// [
|
||||
// Constraint::Percentage(33),
|
||||
// Constraint::Percentage(34),
|
||||
// Constraint::Percentage(33),
|
||||
// ]
|
||||
// .as_ref(),
|
||||
// )
|
||||
// .split(draw_loc)
|
||||
// .into_iter()
|
||||
// .map(|loc| {
|
||||
// // Required to properly margin in *between* the rectangles.
|
||||
// Layout::default()
|
||||
// .horizontal_margin(1)
|
||||
// .constraints([Constraint::Percentage(100)].as_ref())
|
||||
// .split(loc)[0]
|
||||
// })
|
||||
// .collect::<Vec<Rect>>();
|
||||
|
||||
// for dl in margined_draw_locs {
|
||||
// f.render_widget(Block::default().borders(Borders::ALL), dl);
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -223,7 +223,7 @@ impl BatteryDisplayWidget for Painter {
|
|||
.block(Block::default())
|
||||
.divider(tui::symbols::line::VERTICAL)
|
||||
.style(self.colours.text_style)
|
||||
.highlight_style(self.colours.currently_selected_text_style)
|
||||
.highlight_style(self.colours.currently_selected_text_style) //FIXME: [HIGHLIGHT] THIS IS BROKEN ON TUI's SIDE, override this with your own style...
|
||||
.select(battery_widget_state.currently_selected_battery_index),
|
||||
tab_draw_loc,
|
||||
);
|
||||
|
|
|
@ -78,6 +78,7 @@ impl MemGraphWidget for Painter {
|
|||
.graph_type(tui::widgets::GraphType::Line),
|
||||
);
|
||||
|
||||
// FIXME: [SWAP] Hide this if denominator is 0...
|
||||
let swap_label = format!(
|
||||
"SWP:{}{}",
|
||||
app_state.canvas_data.swap_label_percent, app_state.canvas_data.swap_label_frac
|
||||
|
|
|
@ -413,7 +413,7 @@ impl ProcessTableWidget for Painter {
|
|||
}
|
||||
});
|
||||
|
||||
// TODO: gotop's "x out of y" thing is really nice to help keep track of the scroll position.
|
||||
// TODO: gotop's "x out of y" thing is really nice to help keep track of the scroll position. Add to everything?
|
||||
f.render_stateful_widget(
|
||||
Table::new(process_headers.iter(), process_rows)
|
||||
.block(process_block)
|
||||
|
@ -531,6 +531,8 @@ impl ProcessTableWidget for Painter {
|
|||
let query = proc_widget_state.get_current_search_query().as_str();
|
||||
let grapheme_indices = UnicodeSegmentation::grapheme_indices(query, true);
|
||||
|
||||
// TODO: [CURSOR] blank cursor if not selected
|
||||
// TODO: [CURSOR] blinking cursor?
|
||||
let query_with_cursor = build_query(
|
||||
is_on_widget,
|
||||
grapheme_indices,
|
||||
|
@ -579,6 +581,8 @@ impl ProcessTableWidget for Painter {
|
|||
self.colours.text_style
|
||||
};
|
||||
|
||||
// FIXME: [MOUSE] Mouse support for these in search
|
||||
// FIXME: [MOVEMENT] Movement support for these in search
|
||||
let option_text = vec![
|
||||
Text::raw("\n"),
|
||||
Text::styled(
|
||||
|
|
62
src/clap.rs
62
src/clap.rs
|
@ -20,7 +20,7 @@ pub fn get_matches() -> clap::ArgMatches<'static> {
|
|||
|
||||
pub fn build_app() -> App<'static, 'static> {
|
||||
// Temps
|
||||
let kelvin = Arg::with_name("KELVIN")
|
||||
let kelvin = Arg::with_name("kelvin")
|
||||
.short("k")
|
||||
.long("kelvin")
|
||||
.help("Sets the temperature type to Kelvin.")
|
||||
|
@ -28,7 +28,7 @@ pub fn build_app() -> App<'static, 'static> {
|
|||
"\
|
||||
Sets the temperature type to Kelvin.\n\n",
|
||||
);
|
||||
let fahrenheit = Arg::with_name("FAHRENHEIT")
|
||||
let fahrenheit = Arg::with_name("fahrenheit")
|
||||
.short("f")
|
||||
.long("fahrenheit")
|
||||
.help("Sets the temperature type to Fahrenheit.")
|
||||
|
@ -36,7 +36,7 @@ Sets the temperature type to Kelvin.\n\n",
|
|||
"\
|
||||
Sets the temperature type to Fahrenheit.\n\n",
|
||||
);
|
||||
let celsius = Arg::with_name("CELSIUS")
|
||||
let celsius = Arg::with_name("celsius")
|
||||
.short("c")
|
||||
.long("celsius")
|
||||
.help("Sets the temperature type to Celsius.")
|
||||
|
@ -47,7 +47,7 @@ option.\n\n",
|
|||
);
|
||||
|
||||
// All flags. These are in alphabetical order
|
||||
let autohide_time = Arg::with_name("AUTOHIDE_TIME")
|
||||
let autohide_time = Arg::with_name("autohide_time")
|
||||
.long("autohide_time")
|
||||
.help("Temporarily shows the time scale in graphs.")
|
||||
.long_help(
|
||||
|
@ -56,7 +56,7 @@ Automatically hides 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.\n\n\n",
|
||||
);
|
||||
let basic = Arg::with_name("BASIC_MODE")
|
||||
let basic = Arg::with_name("basic")
|
||||
.short("b")
|
||||
.long("basic")
|
||||
.help("Hides graphs and uses a more basic look.")
|
||||
|
@ -65,7 +65,7 @@ disabled via --hide_time then this will have no effect.\n\n\n",
|
|||
Hides graphs and uses a more basic look. Design is largely
|
||||
inspired by htop's.\n\n",
|
||||
);
|
||||
let battery = Arg::with_name("BATTERY")
|
||||
let battery = Arg::with_name("battery")
|
||||
.long("battery")
|
||||
.help("Shows the battery widget.")
|
||||
.long_help(
|
||||
|
@ -73,7 +73,7 @@ inspired by htop's.\n\n",
|
|||
Shows the battery widget in default or basic mode. No effect on
|
||||
custom layouts.\n\n",
|
||||
);
|
||||
let case_sensitive = Arg::with_name("CASE_SENSITIVE")
|
||||
let case_sensitive = Arg::with_name("case_sensitive")
|
||||
.short("S")
|
||||
.long("case_sensitive")
|
||||
.help("Enables case sensitivity by default.")
|
||||
|
@ -81,14 +81,14 @@ custom layouts.\n\n",
|
|||
"\
|
||||
When searching for a process, enables case sensitivity by default.\n\n",
|
||||
);
|
||||
let disable_click = Arg::with_name("DISABLE_CLICK")
|
||||
let disable_click = Arg::with_name("disable_click")
|
||||
.long("disable_click")
|
||||
.help("Disables mouse clicks.")
|
||||
.long_help(
|
||||
"\
|
||||
Disables mouse clicks from interacting with the program.\n\n",
|
||||
);
|
||||
let dot_marker = Arg::with_name("DOT_MARKER")
|
||||
let dot_marker = Arg::with_name("dot_marker")
|
||||
.short("m")
|
||||
.long("dot_marker")
|
||||
.help("Uses a dot marker for graphs.")
|
||||
|
@ -97,7 +97,7 @@ Disables mouse clicks from interacting with the program.\n\n",
|
|||
Uses a dot marker for graphs as opposed to the default braille
|
||||
marker.\n\n",
|
||||
);
|
||||
let group = Arg::with_name("GROUP_PROCESSES")
|
||||
let group = Arg::with_name("group")
|
||||
.short("g")
|
||||
.long("group")
|
||||
.help("Groups processes with the same name by default.")
|
||||
|
@ -105,7 +105,7 @@ marker.\n\n",
|
|||
"\
|
||||
Groups processes with the same name by default.\n\n",
|
||||
);
|
||||
let hide_avg_cpu = Arg::with_name("HIDE_AVG_CPU")
|
||||
let hide_avg_cpu = Arg::with_name("hide_avg_cpu")
|
||||
.short("a")
|
||||
.long("hide_avg_cpu")
|
||||
.help("Hides the average CPU usage.")
|
||||
|
@ -113,21 +113,21 @@ Groups processes with the same name by default.\n\n",
|
|||
"\
|
||||
Hides the average CPU usage from being shown.\n\n",
|
||||
);
|
||||
let hide_table_gap = Arg::with_name("HIDE_TABLE_GAP")
|
||||
let hide_table_gap = Arg::with_name("hide_table_gap")
|
||||
.long("hide_table_gap")
|
||||
.help("Hides the spacing between table headers and entries.")
|
||||
.long_help(
|
||||
"\
|
||||
Hides the spacing between table headers and entries.\n\n",
|
||||
);
|
||||
let hide_time = Arg::with_name("HIDE_TIME")
|
||||
let hide_time = Arg::with_name("hide_time")
|
||||
.long("hide_time")
|
||||
.help("Completely hides the time scaling.")
|
||||
.long_help(
|
||||
"\
|
||||
Completely hides the time scaling from being shown.\n\n",
|
||||
);
|
||||
let left_legend = Arg::with_name("LEFT_LEGEND")
|
||||
let left_legend = Arg::with_name("left_legend")
|
||||
.short("l")
|
||||
.long("left_legend")
|
||||
.help("Puts the CPU chart legend to the left side.")
|
||||
|
@ -135,7 +135,14 @@ Completely hides the time scaling from being shown.\n\n",
|
|||
"\
|
||||
Puts the CPU chart legend to the left side rather than the right side.\n\n",
|
||||
);
|
||||
let regex = Arg::with_name("REGEX_DEFAULT")
|
||||
let no_write = Arg::with_name("no_write")
|
||||
.long("no_write")
|
||||
.help("Disables writing to the config file.")
|
||||
.long_help(
|
||||
"\
|
||||
Disables config changes in-app from writing to the config file.",
|
||||
);
|
||||
let regex = Arg::with_name("regex")
|
||||
.short("R")
|
||||
.long("regex")
|
||||
.help("Enables regex by default.")
|
||||
|
@ -143,7 +150,7 @@ Puts the CPU chart legend to the left side rather than the right side.\n\n",
|
|||
"\
|
||||
When searching for a process, enables regex by default.\n\n",
|
||||
);
|
||||
let current_usage = Arg::with_name("USE_CURR_USAGE")
|
||||
let current_usage = Arg::with_name("current_usage")
|
||||
.short("u")
|
||||
.long("current_usage")
|
||||
.help("Sets process CPU% to be based on current CPU%.")
|
||||
|
@ -152,7 +159,7 @@ When searching for a process, enables regex by default.\n\n",
|
|||
Sets process CPU% usage to be based on the current system CPU% usage
|
||||
rather than total CPU usage.\n\n",
|
||||
);
|
||||
let use_old_network_legend = Arg::with_name("USE_OLD_NETWORK_LEGEND")
|
||||
let use_old_network_legend = Arg::with_name("use_old_network_legend")
|
||||
.long("use_old_network_legend")
|
||||
.help("DEPRECATED - uses the older network legend.")
|
||||
.long_help(
|
||||
|
@ -160,7 +167,7 @@ rather than total CPU usage.\n\n",
|
|||
DEPRECATED - uses the older (pre-0.4) network widget legend.
|
||||
This display is not tested anymore and could be broken.\n\n\n",
|
||||
);
|
||||
let whole_word = Arg::with_name("WHOLE_WORD")
|
||||
let whole_word = Arg::with_name("whole_word")
|
||||
.short("W")
|
||||
.long("whole_word")
|
||||
.help("Enables whole-word matching by default.")
|
||||
|
@ -171,7 +178,7 @@ entire query by default.\n\n",
|
|||
);
|
||||
|
||||
// All options. Again, alphabetical order.
|
||||
let config = Arg::with_name("CONFIG_LOCATION")
|
||||
let config_location = Arg::with_name("config_location")
|
||||
.short("C")
|
||||
.long("config")
|
||||
.takes_value(true)
|
||||
|
@ -182,7 +189,7 @@ entire query by default.\n\n",
|
|||
Sets the location of the config file. Expects a config
|
||||
file in the TOML format. If it doesn't exist, one is created.\n\n\n",
|
||||
);
|
||||
let default_time_value = Arg::with_name("DEFAULT_TIME_VALUE")
|
||||
let default_time_value = Arg::with_name("default_time_value")
|
||||
.short("t")
|
||||
.long("default_time_value")
|
||||
.takes_value(true)
|
||||
|
@ -193,10 +200,10 @@ file in the TOML format. If it doesn't exist, one is created.\n\n\n",
|
|||
Default time value for graphs in milliseconds. The minimum
|
||||
time is 30s (30000), and the default is 60s (60000).\n\n\n",
|
||||
);
|
||||
let default_widget_count = Arg::with_name("DEFAULT_WIDGET_COUNT")
|
||||
let default_widget_count = Arg::with_name("default_widget_count")
|
||||
.long("default_widget_count")
|
||||
.takes_value(true)
|
||||
.requires_all(&["DEFAULT_WIDGET_TYPE"])
|
||||
.requires_all(&["default_widget_type"])
|
||||
.value_name("INT")
|
||||
.help("Sets the n'th selected widget type as the default.")
|
||||
.long_help(
|
||||
|
@ -218,7 +225,7 @@ the default widget. If we set '--default_widget_count 3', it would
|
|||
use CPU (3) as the default instead.
|
||||
\n\n",
|
||||
);
|
||||
let default_widget_type = Arg::with_name("DEFAULT_WIDGET_TYPE")
|
||||
let default_widget_type = Arg::with_name("default_widget_type")
|
||||
.long("default_widget_type")
|
||||
.takes_value(true)
|
||||
.value_name("WIDGET TYPE")
|
||||
|
@ -257,7 +264,7 @@ Supported widget names:
|
|||
+--------------------------+
|
||||
\n\n",
|
||||
);
|
||||
let rate = Arg::with_name("RATE_MILLIS")
|
||||
let rate = Arg::with_name("rate")
|
||||
.short("r")
|
||||
.long("rate")
|
||||
.takes_value(true)
|
||||
|
@ -268,7 +275,7 @@ Supported widget names:
|
|||
Sets a refresh rate in milliseconds. The minimum is 250ms,
|
||||
and defaults to 1000ms. Smaller values may take more resources.\n\n\n",
|
||||
);
|
||||
let time_delta = Arg::with_name("TIME_DELTA")
|
||||
let time_delta = Arg::with_name("time_delta")
|
||||
.short("d")
|
||||
.long("time_delta")
|
||||
.takes_value(true)
|
||||
|
@ -292,12 +299,12 @@ The minimum is 1s (1000), and defaults to 15s (15000).\n\n\n",
|
|||
.arg(kelvin)
|
||||
.arg(fahrenheit)
|
||||
.arg(celsius)
|
||||
.group(ArgGroup::with_name("TEMPERATURE_TYPE").args(&["KELVIN", "FAHRENHEIT", "CELSIUS"]))
|
||||
.group(ArgGroup::with_name("TEMPERATURE_TYPE").args(&["kelvin", "fahrenheit", "celsius"]))
|
||||
.arg(autohide_time)
|
||||
.arg(basic)
|
||||
.arg(battery)
|
||||
.arg(case_sensitive)
|
||||
.arg(config)
|
||||
.arg(config_location)
|
||||
.arg(default_time_value)
|
||||
.arg(default_widget_count)
|
||||
.arg(default_widget_type)
|
||||
|
@ -308,6 +315,7 @@ The minimum is 1s (1000), and defaults to 15s (15000).\n\n\n",
|
|||
.arg(hide_table_gap)
|
||||
.arg(hide_time)
|
||||
.arg(left_legend)
|
||||
.arg(no_write)
|
||||
.arg(rate)
|
||||
.arg(regex)
|
||||
.arg(time_delta)
|
||||
|
|
162
src/constants.rs
162
src/constants.rs
|
@ -245,155 +245,33 @@ pub const DEFAULT_BATTERY_LAYOUT: &str = r##"
|
|||
// Config and flags
|
||||
pub const DEFAULT_CONFIG_FILE_PATH: &str = "bottom/bottom.toml";
|
||||
|
||||
// Default config file
|
||||
// FIXME [CHORE]: Update the default config
|
||||
pub const DEFAULT_CONFIG_CONTENT: &str = r##"
|
||||
# This is a default config file for bottom. All of the settings are commented
|
||||
# out by default; if you wish to change them uncomment and modify as you see
|
||||
# fit.
|
||||
pub const CONFIG_TOP_HEAD: &str = r##"# This is bottom's config file. Values in this config file will change when changed in the
|
||||
# interface. You can also manually change these values.
|
||||
|
||||
# This group of options represents a command-line flag/option. Flags explicitly
|
||||
# added when running (ie: btm -a) will override this config file if an option
|
||||
# is also set here.
|
||||
[flags]
|
||||
"##;
|
||||
|
||||
# Whether to hide the average cpu entry.
|
||||
#hide_avg_cpu = false
|
||||
pub const CONFIG_DISPLAY_OPTIONS_HEAD: &str = r##"
|
||||
# These options represent settings that affect how bottom functions.
|
||||
# If a setting here corresponds to command-line flag, then the flag will temporarily override
|
||||
# the setting.
|
||||
"##;
|
||||
|
||||
# Whether to use dot markers rather than braille.
|
||||
#dot_marker = false
|
||||
pub const CONFIG_COLOUR_HEAD: &str = r##"
|
||||
# These options represent colour values for various parts of bottom. Note that colour support
|
||||
# will ultimately depend on the terminal - for example, the Terminal for macOS does NOT like
|
||||
# custom colours and it may glitch out.
|
||||
"##;
|
||||
|
||||
# The update rate of the application.
|
||||
#rate = 1000
|
||||
|
||||
# Whether to put the CPU legend to the left.
|
||||
#left_legend = false
|
||||
|
||||
# Whether to set CPU% on a process to be based on the total CPU or just current usage.
|
||||
#current_usage = false
|
||||
|
||||
# Whether to group processes with the same name together by default.
|
||||
#group_processes = false
|
||||
|
||||
# Whether to make process searching case sensitive by default.
|
||||
#case_sensitive = false
|
||||
|
||||
# Whether to make process searching look for matching the entire word by default.
|
||||
#whole_word = false
|
||||
|
||||
# Whether to make process searching use regex by default.
|
||||
#regex = false
|
||||
|
||||
# Defaults to Celsius. Temperature is one of:
|
||||
#temperature_type = "k"
|
||||
#temperature_type = "f"
|
||||
#temperature_type = "c"
|
||||
#temperature_type = "kelvin"
|
||||
#temperature_type = "fahrenheit"
|
||||
#temperature_type = "celsius"
|
||||
|
||||
# The default time interval (in milliseconds).
|
||||
#default_time_value = 60000
|
||||
|
||||
# The time delta on each zoom in/out action (in milliseconds).
|
||||
#time_delta = 15000
|
||||
|
||||
# Override layout default widget
|
||||
#default_widget_type = "proc"
|
||||
#default_widget_count = 1
|
||||
|
||||
# Use basic mode
|
||||
#basic = false
|
||||
|
||||
# Use the old network legend style
|
||||
#use_old_network_legend = false
|
||||
|
||||
# Remove space in tables
|
||||
#hide_table_gap = false
|
||||
|
||||
##########################################################
|
||||
|
||||
# These are all the components that support custom theming. Note that colour support
|
||||
# will, at the end of the day, depend on terminal support - for example, the
|
||||
# macOS default Terminal does NOT like custom colours and it will glitch out.
|
||||
[colors]
|
||||
|
||||
# Represents the colour of table headers (processes, CPU, disks, temperature).
|
||||
#table_header_color="LightBlue"
|
||||
|
||||
# Represents the colour of the label each widget has.
|
||||
#widget_title_color="Gray"
|
||||
|
||||
# Represents the average CPU color.
|
||||
#avg_cpu_color="Red"
|
||||
|
||||
# Represents the colour the core will use in the CPU legend and graph.
|
||||
#cpu_core_colors=["LightMagenta", "LightYellow", "LightCyan", "LightGreen", "LightBlue", "LightRed", "Cyan", "Green", "Blue", "Red"]
|
||||
|
||||
# Represents the colour RAM will use in the memory legend and graph.
|
||||
#ram_color="LightMagenta"
|
||||
|
||||
# Represents the colour SWAP will use in the memory legend and graph.
|
||||
#swap_color="LightYellow"
|
||||
|
||||
# Represents the colour rx will use in the network legend and graph.
|
||||
#rx_color="LightCyan"
|
||||
|
||||
# Represents the colour tx will use in the network legend and graph.
|
||||
#tx_color="LightGreen"
|
||||
|
||||
# Represents the colour of the border of unselected widgets.
|
||||
#border_color="Gray"
|
||||
|
||||
# Represents the colour of the border of selected widgets.
|
||||
#highlighted_border_color="LightBlue"
|
||||
|
||||
# Represents the colour of most text.
|
||||
#text_color="Gray"
|
||||
|
||||
# Represents the colour of text that is selected.
|
||||
#selected_text_color="Black"
|
||||
|
||||
# Represents the background colour of text that is selected.
|
||||
#selected_bg_color="LightBlue"
|
||||
|
||||
# Represents the colour of the lines and text of the graph.
|
||||
#graph_color="Gray"
|
||||
|
||||
# Represents the colours of the battery based on charge
|
||||
#battery_colors = ["red", "yellow", "yellow", "green", "green", "green"]
|
||||
|
||||
##########################################################
|
||||
|
||||
# Layout - layouts follow a pattern like this:
|
||||
pub const CONFIG_LAYOUT_HEAD: &str = r##"
|
||||
# These options represent how bottom will lay out its widgets. Layouts follow a pattern like this:
|
||||
# [[row]] represents a row in the application.
|
||||
# [[row.child]] represents either a widget or a column.
|
||||
# [[row.child.child]] represents a widget.
|
||||
#
|
||||
# All widgets must have the type value set to one of ["cpu", "mem", "proc", "net", "temp", "disk", "empty"].
|
||||
# All widgets must have the valid type value set to one of ["cpu", "mem", "proc", "net", "temp", "disk", "empty"].
|
||||
# All layout components have a ratio value - if this is not set, then it defaults to 1.
|
||||
|
||||
# The default widget layout:
|
||||
#[[row]]
|
||||
# ratio=30
|
||||
# [[row.child]]
|
||||
# type="cpu"
|
||||
#[[row]]
|
||||
# ratio=40
|
||||
# [[row.child]]
|
||||
# ratio=4
|
||||
# type="mem"
|
||||
# [[row.child]]
|
||||
# ratio=3
|
||||
# [[row.child.child]]
|
||||
# type="temp"
|
||||
# [[row.child.child]]
|
||||
# type="disk"
|
||||
#[[row]]
|
||||
# ratio=30
|
||||
# [[row.child]]
|
||||
# type="net"
|
||||
# [[row.child]]
|
||||
# type="proc"
|
||||
# default=true
|
||||
"##;
|
||||
|
||||
pub const CONFIG_DIVIDER: &str = r##"
|
||||
#########################################################################
|
||||
"##;
|
||||
|
|
175
src/lib.rs
175
src/lib.rs
|
@ -20,8 +20,6 @@ use crossterm::{
|
|||
terminal::{disable_raw_mode, LeaveAlternateScreen},
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
use app::{
|
||||
data_harvester::{self, processes::ProcessSorting},
|
||||
layout_manager::{UsedWidgets, WidgetDirection},
|
||||
|
@ -33,20 +31,17 @@ use options::*;
|
|||
use utils::error;
|
||||
|
||||
pub mod app;
|
||||
|
||||
pub mod utils {
|
||||
pub mod error;
|
||||
pub mod gen_util;
|
||||
pub mod logging;
|
||||
}
|
||||
|
||||
pub mod canvas;
|
||||
pub mod clap;
|
||||
pub mod constants;
|
||||
pub mod data_conversion;
|
||||
pub mod options;
|
||||
|
||||
pub mod clap;
|
||||
|
||||
#[cfg(target_family = "windows")]
|
||||
pub type Pid = usize;
|
||||
|
||||
|
@ -60,8 +55,11 @@ pub enum BottomEvent<I, J> {
|
|||
Clean,
|
||||
}
|
||||
|
||||
pub enum ResetEvent {
|
||||
pub enum CollectionThreadEvent {
|
||||
Reset,
|
||||
UpdateConfig(Box<app::AppConfigFields>),
|
||||
UpdateUsedWidgets(Box<UsedWidgets>),
|
||||
UpdateUpdateTime(u64),
|
||||
}
|
||||
|
||||
pub fn handle_mouse_event(event: MouseEvent, app: &mut App) {
|
||||
|
@ -87,7 +85,7 @@ pub fn handle_mouse_event(event: MouseEvent, app: &mut App) {
|
|||
}
|
||||
|
||||
pub fn handle_key_event_or_break(
|
||||
event: KeyEvent, app: &mut App, reset_sender: &std::sync::mpsc::Sender<ResetEvent>,
|
||||
event: KeyEvent, app: &mut App, reset_sender: &std::sync::mpsc::Sender<CollectionThreadEvent>,
|
||||
) -> bool {
|
||||
// debug!("KeyEvent: {:?}", event);
|
||||
|
||||
|
@ -144,7 +142,7 @@ pub fn handle_key_event_or_break(
|
|||
KeyCode::Up => app.move_widget_selection(&WidgetDirection::Up),
|
||||
KeyCode::Down => app.move_widget_selection(&WidgetDirection::Down),
|
||||
KeyCode::Char('r') => {
|
||||
if reset_sender.send(ResetEvent::Reset).is_ok() {
|
||||
if reset_sender.send(CollectionThreadEvent::Reset).is_ok() {
|
||||
app.reset();
|
||||
}
|
||||
}
|
||||
|
@ -213,17 +211,19 @@ pub fn read_config(config_location: Option<&str>) -> error::Result<Option<PathBu
|
|||
pub fn create_or_get_config(config_path: &Option<PathBuf>) -> error::Result<Config> {
|
||||
if let Some(path) = config_path {
|
||||
if let Ok(config_string) = fs::read_to_string(path) {
|
||||
// We found a config file!
|
||||
Ok(toml::from_str(config_string.as_str())?)
|
||||
} else {
|
||||
// Config file DNE...
|
||||
if let Some(parent_path) = path.parent() {
|
||||
fs::create_dir_all(parent_path)?;
|
||||
}
|
||||
fs::File::create(path)?.write_all(DEFAULT_CONFIG_CONTENT.as_bytes())?;
|
||||
Ok(toml::from_str(DEFAULT_CONFIG_CONTENT)?)
|
||||
fs::File::create(path)?.write_all(CONFIG_TOP_HEAD.as_bytes())?;
|
||||
Ok(Config::default())
|
||||
}
|
||||
} else {
|
||||
// Don't write otherwise...
|
||||
Ok(toml::from_str(DEFAULT_CONFIG_CONTENT)?)
|
||||
// Don't write, the config path was somehow None...
|
||||
Ok(Config::default())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,134 +239,6 @@ pub fn try_drawing(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_config_colours(
|
||||
config: &Config, painter: &mut canvas::Painter,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(colours) = &config.colors {
|
||||
if let Some(border_color) = &colours.border_color {
|
||||
painter
|
||||
.colours
|
||||
.set_border_colour(border_color)
|
||||
.context("Update 'border_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(highlighted_border_color) = &colours.highlighted_border_color {
|
||||
painter
|
||||
.colours
|
||||
.set_highlighted_border_colour(highlighted_border_color)
|
||||
.context("Update 'highlighted_border_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(text_color) = &colours.text_color {
|
||||
painter
|
||||
.colours
|
||||
.set_text_colour(text_color)
|
||||
.context("Update 'text_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(avg_cpu_color) = &colours.avg_cpu_color {
|
||||
painter
|
||||
.colours
|
||||
.set_avg_cpu_colour(avg_cpu_color)
|
||||
.context("Update 'avg_cpu_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(all_cpu_color) = &colours.all_cpu_color {
|
||||
painter
|
||||
.colours
|
||||
.set_all_cpu_colour(all_cpu_color)
|
||||
.context("Update 'all_cpu_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(cpu_core_colors) = &colours.cpu_core_colors {
|
||||
painter
|
||||
.colours
|
||||
.set_cpu_colours(cpu_core_colors)
|
||||
.context("Update 'cpu_core_colors' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(ram_color) = &colours.ram_color {
|
||||
painter
|
||||
.colours
|
||||
.set_ram_colour(ram_color)
|
||||
.context("Update 'ram_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(swap_color) = &colours.swap_color {
|
||||
painter
|
||||
.colours
|
||||
.set_swap_colour(swap_color)
|
||||
.context("Update 'swap_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(rx_color) = &colours.rx_color {
|
||||
painter
|
||||
.colours
|
||||
.set_rx_colour(rx_color)
|
||||
.context("Update 'rx_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(tx_color) = &colours.tx_color {
|
||||
painter
|
||||
.colours
|
||||
.set_tx_colour(tx_color)
|
||||
.context("Update 'tx_color' in your config file..")?;
|
||||
}
|
||||
|
||||
// if let Some(rx_total_color) = &colours.rx_total_color {
|
||||
// painter.colours.set_rx_total_colour(rx_total_color)?;
|
||||
// }
|
||||
|
||||
// if let Some(tx_total_color) = &colours.tx_total_color {
|
||||
// painter.colours.set_tx_total_colour(tx_total_color)?;
|
||||
// }
|
||||
|
||||
if let Some(table_header_color) = &colours.table_header_color {
|
||||
painter
|
||||
.colours
|
||||
.set_table_header_colour(table_header_color)
|
||||
.context("Update 'table_header_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(scroll_entry_text_color) = &colours.selected_text_color {
|
||||
painter
|
||||
.colours
|
||||
.set_scroll_entry_text_color(scroll_entry_text_color)
|
||||
.context("Update 'selected_text_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(scroll_entry_bg_color) = &colours.selected_bg_color {
|
||||
painter
|
||||
.colours
|
||||
.set_scroll_entry_bg_color(scroll_entry_bg_color)
|
||||
.context("Update 'selected_bg_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(widget_title_color) = &colours.widget_title_color {
|
||||
painter
|
||||
.colours
|
||||
.set_widget_title_colour(widget_title_color)
|
||||
.context("Update 'widget_title_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(graph_color) = &colours.graph_color {
|
||||
painter
|
||||
.colours
|
||||
.set_graph_colour(graph_color)
|
||||
.context("Update 'graph_color' in your config file..")?;
|
||||
}
|
||||
|
||||
if let Some(battery_colors) = &colours.battery_colors {
|
||||
painter
|
||||
.colours
|
||||
.set_battery_colors(battery_colors)
|
||||
.context("Update 'battery_colors' in your config file.")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cleanup_terminal(
|
||||
terminal: &mut tui::terminal::Terminal<tui::backend::CrosstermBackend<std::io::Stdout>>,
|
||||
) -> error::Result<()> {
|
||||
|
@ -712,11 +584,11 @@ pub fn create_input_thread(
|
|||
});
|
||||
}
|
||||
|
||||
pub fn create_event_thread(
|
||||
pub fn create_collection_thread(
|
||||
sender: std::sync::mpsc::Sender<
|
||||
BottomEvent<crossterm::event::KeyEvent, crossterm::event::MouseEvent>,
|
||||
>,
|
||||
reset_receiver: std::sync::mpsc::Receiver<ResetEvent>,
|
||||
reset_receiver: std::sync::mpsc::Receiver<CollectionThreadEvent>,
|
||||
app_config_fields: &app::AppConfigFields, used_widget_set: UsedWidgets,
|
||||
) {
|
||||
let temp_type = app_config_fields.temperature_type.clone();
|
||||
|
@ -733,11 +605,24 @@ pub fn create_event_thread(
|
|||
|
||||
data_state.init();
|
||||
loop {
|
||||
let mut update_time = update_rate_in_milliseconds;
|
||||
if let Ok(message) = reset_receiver.try_recv() {
|
||||
match message {
|
||||
ResetEvent::Reset => {
|
||||
CollectionThreadEvent::Reset => {
|
||||
data_state.data.first_run_cleanup();
|
||||
}
|
||||
CollectionThreadEvent::UpdateConfig(app_config_fields) => {
|
||||
data_state.set_temperature_type(app_config_fields.temperature_type.clone());
|
||||
data_state
|
||||
.set_use_current_cpu_total(app_config_fields.use_current_cpu_total);
|
||||
data_state.set_show_average_cpu(app_config_fields.show_average_cpu);
|
||||
}
|
||||
CollectionThreadEvent::UpdateUsedWidgets(used_widget_set) => {
|
||||
data_state.set_collected_data(*used_widget_set);
|
||||
}
|
||||
CollectionThreadEvent::UpdateUpdateTime(new_time) => {
|
||||
update_time = new_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
futures::executor::block_on(data_state.update_data());
|
||||
|
@ -746,7 +631,7 @@ pub fn create_event_thread(
|
|||
if sender.send(event).is_err() {
|
||||
break;
|
||||
}
|
||||
thread::sleep(Duration::from_millis(update_rate_in_milliseconds));
|
||||
thread::sleep(Duration::from_millis(update_time));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Instant;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
app::{layout_manager::*, *},
|
||||
|
@ -15,7 +18,7 @@ pub mod layout_options;
|
|||
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
#[derive(Clone, Default, Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
pub flags: Option<ConfigFlags>,
|
||||
pub colors: Option<ConfigColours>,
|
||||
|
@ -24,7 +27,7 @@ pub struct Config {
|
|||
pub temp_filter: Option<IgnoreList>,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
#[derive(Clone, Default, Deserialize, Serialize)]
|
||||
pub struct ConfigFlags {
|
||||
pub hide_avg_cpu: Option<bool>,
|
||||
pub dot_marker: Option<bool>,
|
||||
|
@ -48,9 +51,10 @@ pub struct ConfigFlags {
|
|||
pub hide_table_gap: Option<bool>,
|
||||
pub battery: Option<bool>,
|
||||
pub disable_click: Option<bool>,
|
||||
pub no_write: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
#[derive(Clone, Default, Deserialize, Serialize)]
|
||||
pub struct ConfigColours {
|
||||
pub table_header_color: Option<String>,
|
||||
pub all_cpu_color: Option<String>,
|
||||
|
@ -72,7 +76,7 @@ pub struct ConfigColours {
|
|||
pub battery_colors: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
#[derive(Clone, Default, Deserialize, Serialize)]
|
||||
pub struct IgnoreList {
|
||||
pub is_list_ignored: bool,
|
||||
pub list: Vec<String>,
|
||||
|
@ -83,6 +87,7 @@ pub struct IgnoreList {
|
|||
pub fn build_app(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config, widget_layout: &BottomLayout,
|
||||
default_widget_id: u64, default_widget_type_option: &Option<BottomWidgetType>,
|
||||
config_path: Option<PathBuf>,
|
||||
) -> Result<App> {
|
||||
use BottomWidgetType::*;
|
||||
let autohide_time = get_autohide_time(&matches, &config);
|
||||
|
@ -248,6 +253,7 @@ pub fn build_app(
|
|||
1
|
||||
},
|
||||
disable_click: get_disable_click(matches, config),
|
||||
no_write: get_no_write(matches, config),
|
||||
};
|
||||
|
||||
let used_widgets = UsedWidgets {
|
||||
|
@ -275,13 +281,15 @@ pub fn build_app(
|
|||
.temp_state(TempState::init(temp_state_map))
|
||||
.battery_state(BatteryState::init(battery_state_map))
|
||||
.basic_table_widget_state(basic_table_widget_state)
|
||||
.current_widget(widget_map.get(&initial_widget_id).unwrap().clone()) // I think the unwrap is fine here
|
||||
.current_widget(widget_map.get(&initial_widget_id).unwrap().clone()) // FIXME: [UNWRAP] - many of the unwraps are fine (like this one) but do a once-over and/or switch to expect?
|
||||
.widget_map(widget_map)
|
||||
.used_widgets(used_widgets)
|
||||
.filters(DataFilters {
|
||||
disk_filter,
|
||||
temp_filter,
|
||||
})
|
||||
.config(config.clone())
|
||||
.config_path(config_path)
|
||||
.build())
|
||||
}
|
||||
|
||||
|
@ -353,7 +361,7 @@ pub fn get_widget_layout(
|
|||
fn get_update_rate_in_milliseconds(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config,
|
||||
) -> error::Result<u64> {
|
||||
let update_rate_in_milliseconds = if let Some(update_rate) = matches.value_of("RATE_MILLIS") {
|
||||
let update_rate_in_milliseconds = if let Some(update_rate) = matches.value_of("rate") {
|
||||
update_rate.parse::<u128>()?
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(rate) = flags.rate {
|
||||
|
@ -381,11 +389,11 @@ fn get_update_rate_in_milliseconds(
|
|||
fn get_temperature(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config,
|
||||
) -> error::Result<data_harvester::temperature::TemperatureType> {
|
||||
if matches.is_present("FAHRENHEIT") {
|
||||
if matches.is_present("fahrenheit") {
|
||||
return Ok(data_harvester::temperature::TemperatureType::Fahrenheit);
|
||||
} else if matches.is_present("KELVIN") {
|
||||
} else if matches.is_present("kelvin") {
|
||||
return Ok(data_harvester::temperature::TemperatureType::Kelvin);
|
||||
} else if matches.is_present("CELSIUS") {
|
||||
} else if matches.is_present("celsius") {
|
||||
return Ok(data_harvester::temperature::TemperatureType::Celsius);
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(temp_type) = &flags.temperature_type {
|
||||
|
@ -406,7 +414,7 @@ fn get_temperature(
|
|||
|
||||
/// Yes, this function gets whether to show average CPU (true) or not (false)
|
||||
fn get_show_average_cpu(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("HIDE_AVG_CPU") {
|
||||
if matches.is_present("hide_avg_cpu") {
|
||||
return false;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(avg_cpu) = flags.hide_avg_cpu {
|
||||
|
@ -418,7 +426,7 @@ fn get_show_average_cpu(matches: &clap::ArgMatches<'static>, config: &Config) ->
|
|||
}
|
||||
|
||||
fn get_use_dot(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("DOT_MARKER") {
|
||||
if matches.is_present("dot_marker") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(dot_marker) = flags.dot_marker {
|
||||
|
@ -429,7 +437,7 @@ fn get_use_dot(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
|||
}
|
||||
|
||||
fn get_use_left_legend(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("LEFT_LEGEND") {
|
||||
if matches.is_present("left_legend") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(left_legend) = flags.left_legend {
|
||||
|
@ -441,7 +449,7 @@ fn get_use_left_legend(matches: &clap::ArgMatches<'static>, config: &Config) ->
|
|||
}
|
||||
|
||||
fn get_use_current_cpu_total(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("USE_CURR_USAGE") {
|
||||
if matches.is_present("current_usage") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(current_usage) = flags.current_usage {
|
||||
|
@ -453,7 +461,7 @@ fn get_use_current_cpu_total(matches: &clap::ArgMatches<'static>, config: &Confi
|
|||
}
|
||||
|
||||
fn get_use_basic_mode(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("BASIC_MODE") {
|
||||
if matches.is_present("basic") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(basic) = flags.basic {
|
||||
|
@ -467,7 +475,7 @@ fn get_use_basic_mode(matches: &clap::ArgMatches<'static>, config: &Config) -> b
|
|||
fn get_default_time_value(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config,
|
||||
) -> error::Result<u64> {
|
||||
let default_time = if let Some(default_time_value) = matches.value_of("DEFAULT_TIME_VALUE") {
|
||||
let default_time = if let Some(default_time_value) = matches.value_of("default_time_value") {
|
||||
default_time_value.parse::<u128>()?
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(default_time_value) = flags.default_time_value {
|
||||
|
@ -494,7 +502,7 @@ fn get_default_time_value(
|
|||
}
|
||||
|
||||
fn get_time_interval(matches: &clap::ArgMatches<'static>, config: &Config) -> error::Result<u64> {
|
||||
let time_interval = if let Some(time_interval) = matches.value_of("TIME_DELTA") {
|
||||
let time_interval = if let Some(time_interval) = matches.value_of("time_delta") {
|
||||
time_interval.parse::<u128>()?
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(time_interval) = flags.time_delta {
|
||||
|
@ -521,7 +529,7 @@ fn get_time_interval(matches: &clap::ArgMatches<'static>, config: &Config) -> er
|
|||
}
|
||||
|
||||
pub fn get_app_grouping(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("GROUP_PROCESSES") {
|
||||
if matches.is_present("group") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(grouping) = flags.group_processes {
|
||||
|
@ -532,7 +540,7 @@ pub fn get_app_grouping(matches: &clap::ArgMatches<'static>, config: &Config) ->
|
|||
}
|
||||
|
||||
pub fn get_app_case_sensitive(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("CASE_SENSITIVE") {
|
||||
if matches.is_present("case_sensitive") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(case_sensitive) = flags.case_sensitive {
|
||||
|
@ -543,7 +551,7 @@ pub fn get_app_case_sensitive(matches: &clap::ArgMatches<'static>, config: &Conf
|
|||
}
|
||||
|
||||
pub fn get_app_match_whole_word(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("WHOLE_WORD") {
|
||||
if matches.is_present("whole_word") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(whole_word) = flags.whole_word {
|
||||
|
@ -554,7 +562,7 @@ pub fn get_app_match_whole_word(matches: &clap::ArgMatches<'static>, config: &Co
|
|||
}
|
||||
|
||||
pub fn get_app_use_regex(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("REGEX_DEFAULT") {
|
||||
if matches.is_present("regex") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(regex) = flags.regex {
|
||||
|
@ -565,7 +573,7 @@ pub fn get_app_use_regex(matches: &clap::ArgMatches<'static>, config: &Config) -
|
|||
}
|
||||
|
||||
fn get_hide_time(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("HIDE_TIME") {
|
||||
if matches.is_present("hide_time") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(hide_time) = flags.hide_time {
|
||||
|
@ -576,7 +584,7 @@ fn get_hide_time(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
|||
}
|
||||
|
||||
fn get_autohide_time(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("AUTOHIDE_TIME") {
|
||||
if matches.is_present("autohide_time") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(autohide_time) = flags.autohide_time {
|
||||
|
@ -590,7 +598,7 @@ fn get_autohide_time(matches: &clap::ArgMatches<'static>, config: &Config) -> bo
|
|||
fn get_default_widget_and_count(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config,
|
||||
) -> error::Result<(Option<BottomWidgetType>, u64)> {
|
||||
let widget_type = if let Some(widget_type) = matches.value_of("DEFAULT_WIDGET_TYPE") {
|
||||
let widget_type = if let Some(widget_type) = matches.value_of("default_widget_type") {
|
||||
let parsed_widget = widget_type.parse::<BottomWidgetType>()?;
|
||||
if let BottomWidgetType::Empty = parsed_widget {
|
||||
None
|
||||
|
@ -612,7 +620,7 @@ fn get_default_widget_and_count(
|
|||
None
|
||||
};
|
||||
|
||||
let widget_count = if let Some(widget_count) = matches.value_of("DEFAULT_WIDGET_COUNT") {
|
||||
let widget_count = if let Some(widget_count) = matches.value_of("default_widget_count") {
|
||||
Some(widget_count.parse::<u128>()?)
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(widget_count) = flags.default_widget_count {
|
||||
|
@ -643,7 +651,7 @@ fn get_default_widget_and_count(
|
|||
}
|
||||
|
||||
fn get_disable_click(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("DISABLE_CLICK") {
|
||||
if matches.is_present("disable_click") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(disable_click) = flags.disable_click {
|
||||
|
@ -654,7 +662,7 @@ fn get_disable_click(matches: &clap::ArgMatches<'static>, config: &Config) -> bo
|
|||
}
|
||||
|
||||
pub fn get_use_old_network_legend(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("USE_OLD_NETWORK_LEGEND") {
|
||||
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 {
|
||||
|
@ -665,7 +673,7 @@ pub fn get_use_old_network_legend(matches: &clap::ArgMatches<'static>, config: &
|
|||
}
|
||||
|
||||
pub fn get_hide_table_gap(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("HIDE_TABLE_GAP") {
|
||||
if matches.is_present("hide_table_gap") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(hide_table_gap) = flags.hide_table_gap {
|
||||
|
@ -676,7 +684,7 @@ pub fn get_hide_table_gap(matches: &clap::ArgMatches<'static>, config: &Config)
|
|||
}
|
||||
|
||||
pub fn get_use_battery(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("BATTERY") {
|
||||
if matches.is_present("battery") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(battery) = flags.battery {
|
||||
|
@ -686,6 +694,17 @@ pub fn get_use_battery(matches: &clap::ArgMatches<'static>, config: &Config) ->
|
|||
false
|
||||
}
|
||||
|
||||
pub fn get_no_write(matches: &clap::ArgMatches<'static>, config: &Config) -> bool {
|
||||
if matches.is_present("no_write") {
|
||||
return true;
|
||||
} else if let Some(flags) = &config.flags {
|
||||
if let Some(no_write) = flags.no_write {
|
||||
return no_write;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_ignore_list(ignore_list: &Option<IgnoreList>) -> error::Result<Option<Filter>> {
|
||||
if let Some(ignore_list) = ignore_list {
|
||||
let list: Result<Vec<_>, _> = ignore_list
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::app::layout_manager::*;
|
||||
use crate::error::Result;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents a row. This has a length of some sort (optional) and a vector
|
||||
/// of children.
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Clone, Deserialize, Debug, Serialize)]
|
||||
#[serde(rename = "row")]
|
||||
pub struct Row {
|
||||
pub ratio: Option<u32>,
|
||||
|
@ -334,7 +334,7 @@ impl Row {
|
|||
/// A Col can also have an optional length and children. We only allow columns
|
||||
/// to have FinalWidgets as children, lest we get some amount of mutual
|
||||
/// recursion between Row and Col.
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Clone, Deserialize, Debug, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RowChildren {
|
||||
Widget(FinalWidget),
|
||||
|
@ -345,7 +345,7 @@ pub enum RowChildren {
|
|||
}
|
||||
|
||||
/// Represents a widget.
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Clone, Deserialize, Debug, Serialize)]
|
||||
pub struct FinalWidget {
|
||||
pub ratio: Option<u32>,
|
||||
#[serde(rename = "type")]
|
||||
|
|
Loading…
Reference in a new issue