mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-04-07 20:35:14 +02:00
feature: support custom widget borders (#1642)
* run a dep bump * add widget border type * feature: support custom widget borders * fmt * remove none since it looks really bad * fix bug with title for tables with no title when expanded * fix jsonschema * fix some unused stuff
This commit is contained in:
parent
1fe17ddc21
commit
0d182e4b3a
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -620,9 +620,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.1"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
@ -1278,9 +1278,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.132"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
@ -81,7 +81,7 @@ crossterm = "0.28.1"
|
||||
ctrlc = { version = "3.4.5", features = ["termination"] }
|
||||
dirs = "5.0.1"
|
||||
# Maybe consider https://github.com/rust-lang/rustc-hash for some cases too?
|
||||
hashbrown = "0.15.0"
|
||||
hashbrown = "0.15.2"
|
||||
humantime = "2.1.0"
|
||||
indexmap = "2.6.0"
|
||||
indoc = "2.0.5"
|
||||
@ -104,7 +104,7 @@ time = { version = "0.3.36", features = ["local-offset", "formatting", "macros"]
|
||||
|
||||
# These are just used for JSON schema generation.
|
||||
schemars = { version = "0.8.21", optional = true }
|
||||
serde_json = { version = "1.0.132", optional = true }
|
||||
serde_json = { version = "1.0.133", optional = true }
|
||||
strum = { version = "0.26.3", features = ["derive"], optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
|
@ -955,6 +955,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"WidgetBorderType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Default",
|
||||
"Rounded",
|
||||
"Double",
|
||||
"Thick"
|
||||
]
|
||||
},
|
||||
"WidgetStyle": {
|
||||
"description": "General styling for generic widgets.",
|
||||
"type": "object",
|
||||
@ -1014,6 +1023,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"widget_border_type": {
|
||||
"description": "Widget borders type.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/WidgetBorderType"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"widget_title": {
|
||||
"description": "Text styling for a widget's title.",
|
||||
"anyOf": [
|
||||
|
@ -18,12 +18,12 @@ use crate::{
|
||||
App,
|
||||
},
|
||||
constants::*,
|
||||
options::config::style::ColourPalette,
|
||||
options::config::style::Styles,
|
||||
};
|
||||
|
||||
/// Handles the canvas' state.
|
||||
pub struct Painter {
|
||||
pub colours: ColourPalette,
|
||||
pub styles: Styles,
|
||||
previous_height: u16,
|
||||
previous_width: u16,
|
||||
|
||||
@ -47,7 +47,7 @@ pub enum LayoutConstraint {
|
||||
}
|
||||
|
||||
impl Painter {
|
||||
pub fn init(layout: BottomLayout, styling: ColourPalette) -> anyhow::Result<Self> {
|
||||
pub fn init(layout: BottomLayout, styling: Styles) -> 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.
|
||||
@ -131,7 +131,7 @@ impl Painter {
|
||||
});
|
||||
|
||||
let painter = Painter {
|
||||
colours: styling,
|
||||
styles: styling,
|
||||
previous_height: 0,
|
||||
previous_width: 0,
|
||||
row_constraints,
|
||||
@ -149,9 +149,9 @@ impl Painter {
|
||||
pub fn get_border_style(&self, widget_id: u64, selected_widget_id: u64) -> tui::style::Style {
|
||||
let is_on_widget = widget_id == selected_widget_id;
|
||||
if is_on_widget {
|
||||
self.colours.highlighted_border_style
|
||||
self.styles.highlighted_border_style
|
||||
} else {
|
||||
self.colours.border_style
|
||||
self.styles.border_style
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ impl Painter {
|
||||
f.render_widget(
|
||||
Paragraph::new(Span::styled(
|
||||
"Frozen, press 'f' to unfreeze",
|
||||
self.colours.selected_text_style,
|
||||
self.styles.selected_text_style,
|
||||
)),
|
||||
Layout::default()
|
||||
.horizontal_margin(1)
|
||||
@ -333,15 +333,11 @@ impl Painter {
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
self.draw_process(f, app_state, rect[0], true, widget_id);
|
||||
self.draw_process(f, app_state, rect[0], widget_id);
|
||||
}
|
||||
Battery => {
|
||||
self.draw_battery(f, app_state, rect[0], app_state.current_widget.widget_id)
|
||||
}
|
||||
Battery => self.draw_battery(
|
||||
f,
|
||||
app_state,
|
||||
rect[0],
|
||||
true,
|
||||
app_state.current_widget.widget_id,
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
} else if app_state.app_config_fields.use_basic_mode {
|
||||
@ -444,18 +440,14 @@ impl Painter {
|
||||
ProcSort => 2,
|
||||
_ => 0,
|
||||
};
|
||||
self.draw_process(f, app_state, vertical_chunks[3], false, wid);
|
||||
self.draw_process(f, app_state, vertical_chunks[3], wid);
|
||||
}
|
||||
Temp => {
|
||||
self.draw_temp_table(f, app_state, vertical_chunks[3], widget_id)
|
||||
}
|
||||
Battery => self.draw_battery(
|
||||
f,
|
||||
app_state,
|
||||
vertical_chunks[3],
|
||||
false,
|
||||
widget_id,
|
||||
),
|
||||
Battery => {
|
||||
self.draw_battery(f, app_state, vertical_chunks[3], widget_id)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -729,8 +721,8 @@ impl Painter {
|
||||
Net => self.draw_network(f, app_state, *draw_loc, widget.widget_id),
|
||||
Temp => self.draw_temp_table(f, app_state, *draw_loc, widget.widget_id),
|
||||
Disk => self.draw_disk_table(f, app_state, *draw_loc, widget.widget_id),
|
||||
Proc => self.draw_process(f, app_state, *draw_loc, true, widget.widget_id),
|
||||
Battery => self.draw_battery(f, app_state, *draw_loc, true, widget.widget_id),
|
||||
Proc => self.draw_process(f, app_state, *draw_loc, widget.widget_id),
|
||||
Battery => self.draw_battery(f, app_state, *draw_loc, widget.widget_id),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
pub mod data_table;
|
||||
pub mod time_graph;
|
||||
mod tui_widget;
|
||||
|
||||
pub mod widget_carousel;
|
||||
|
||||
pub use tui_widget::*;
|
||||
|
@ -7,10 +7,9 @@ use concat_string::concat_string;
|
||||
use tui::{
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
text::{Line, Span, Text},
|
||||
widgets::{Block, Borders, Row, Table},
|
||||
widgets::{Block, Row, Table},
|
||||
Frame,
|
||||
};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use super::{
|
||||
CalculateColumnWidths, ColumnHeader, ColumnWidthBounds, DataTable, DataTableColumn, DataToCell,
|
||||
@ -18,8 +17,8 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
app::layout_manager::BottomWidget,
|
||||
canvas::Painter,
|
||||
constants::{SIDE_BORDERS, TABLE_GAP_HEIGHT_LIMIT},
|
||||
canvas::{drawing_utils::widget_block, Painter},
|
||||
constants::TABLE_GAP_HEIGHT_LIMIT,
|
||||
utils::strings::truncate_to_text,
|
||||
};
|
||||
|
||||
@ -68,46 +67,41 @@ where
|
||||
C: DataTableColumn<H>,
|
||||
{
|
||||
fn block<'a>(&self, draw_info: &'a DrawInfo, data_len: usize) -> Block<'a> {
|
||||
let border_style = match draw_info.selection_state {
|
||||
SelectionState::NotSelected => self.styling.border_style,
|
||||
SelectionState::Selected | SelectionState::Expanded => {
|
||||
self.styling.highlighted_border_style
|
||||
}
|
||||
let is_selected = match draw_info.selection_state {
|
||||
SelectionState::NotSelected => false,
|
||||
SelectionState::Selected | SelectionState::Expanded => true,
|
||||
};
|
||||
|
||||
if !self.props.is_basic {
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_style);
|
||||
|
||||
if let Some(title) = self.generate_title(draw_info, data_len) {
|
||||
block.title(title)
|
||||
} else {
|
||||
block
|
||||
}
|
||||
} else if draw_info.is_on_widget() {
|
||||
// Implies it is basic mode but selected.
|
||||
Block::default()
|
||||
.borders(SIDE_BORDERS)
|
||||
.border_style(border_style)
|
||||
let border_style = if is_selected {
|
||||
self.styling.highlighted_border_style
|
||||
} else {
|
||||
Block::default().borders(Borders::NONE)
|
||||
self.styling.border_style
|
||||
};
|
||||
|
||||
let mut block = widget_block(self.props.is_basic, is_selected, self.styling.border_type)
|
||||
.border_style(border_style);
|
||||
|
||||
if let Some((left_title, right_title)) = self.generate_title(draw_info, data_len) {
|
||||
if !self.props.is_basic {
|
||||
block = block.title_top(left_title);
|
||||
}
|
||||
|
||||
if let Some(right_title) = right_title {
|
||||
block = block.title_top(right_title);
|
||||
}
|
||||
}
|
||||
|
||||
block
|
||||
}
|
||||
|
||||
/// Generates a title, given the available space.
|
||||
pub fn generate_title<'a>(
|
||||
&self, draw_info: &'a DrawInfo, total_items: usize,
|
||||
) -> Option<Line<'a>> {
|
||||
fn generate_title(
|
||||
&self, draw_info: &'_ DrawInfo, total_items: usize,
|
||||
) -> Option<(Line<'static>, Option<Line<'static>>)> {
|
||||
self.props.title.as_ref().map(|title| {
|
||||
let current_index = self.state.current_index.saturating_add(1);
|
||||
let draw_loc = draw_info.loc;
|
||||
let title_style = self.styling.title_style;
|
||||
let border_style = if draw_info.is_on_widget() {
|
||||
self.styling.highlighted_border_style
|
||||
} else {
|
||||
self.styling.border_style
|
||||
};
|
||||
|
||||
let title = if self.props.show_table_scroll_position {
|
||||
let pos = current_index.to_string();
|
||||
@ -123,19 +117,15 @@ where
|
||||
title.to_string()
|
||||
};
|
||||
|
||||
if draw_info.is_expanded() {
|
||||
let title_base = concat_string!(title, "── Esc to go back ");
|
||||
let lines = "─".repeat(usize::from(draw_loc.width).saturating_sub(
|
||||
UnicodeSegmentation::graphemes(title_base.as_str(), true).count() + 2,
|
||||
));
|
||||
let esc = concat_string!("─", lines, "─ Esc to go back ");
|
||||
Line::from(vec![
|
||||
Span::styled(title, title_style),
|
||||
Span::styled(esc, border_style),
|
||||
])
|
||||
let left_title = Line::from(Span::styled(title, title_style)).left_aligned();
|
||||
|
||||
let right_title = if draw_info.is_expanded() {
|
||||
Some(Line::from(" Esc to go back ").right_aligned())
|
||||
} else {
|
||||
Line::from(Span::styled(title, title_style))
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
(left_title, right_title)
|
||||
})
|
||||
}
|
||||
|
||||
@ -202,8 +192,9 @@ where
|
||||
|
||||
if !self.data.is_empty() || !self.first_draw {
|
||||
if self.first_draw {
|
||||
self.first_draw = false; // TODO: Doing it this way is fine, but it could be done better (e.g. showing
|
||||
// custom no results/entries message)
|
||||
// TODO: Doing it this way is fine, but it could be done better (e.g. showing
|
||||
// custom no results/entries message)
|
||||
self.first_draw = false;
|
||||
if let Some(first_index) = self.first_index {
|
||||
self.set_position(first_index);
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
use tui::style::Style;
|
||||
use tui::{style::Style, widgets::BorderType};
|
||||
|
||||
use crate::options::config::style::ColourPalette;
|
||||
use crate::options::config::style::Styles;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DataTableStyling {
|
||||
pub header_style: Style,
|
||||
pub border_style: Style,
|
||||
pub border_type: BorderType,
|
||||
pub highlighted_border_style: Style,
|
||||
pub text_style: Style,
|
||||
pub highlighted_text_style: Style,
|
||||
@ -13,14 +14,15 @@ pub struct DataTableStyling {
|
||||
}
|
||||
|
||||
impl DataTableStyling {
|
||||
pub fn from_palette(colours: &ColourPalette) -> Self {
|
||||
pub fn from_palette(styles: &Styles) -> Self {
|
||||
Self {
|
||||
header_style: colours.table_header_style,
|
||||
border_style: colours.border_style,
|
||||
highlighted_border_style: colours.highlighted_border_style,
|
||||
text_style: colours.text_style,
|
||||
highlighted_text_style: colours.selected_text_style,
|
||||
title_style: colours.widget_title_style,
|
||||
header_style: styles.table_header_style,
|
||||
border_style: styles.border_style,
|
||||
border_type: styles.border_type,
|
||||
highlighted_border_style: styles.highlighted_border_style,
|
||||
text_style: styles.text_style,
|
||||
highlighted_text_style: styles.selected_text_style,
|
||||
title_style: styles.widget_title_style,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,11 @@ use tui::{
|
||||
style::Style,
|
||||
symbols::Marker,
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Borders, GraphType},
|
||||
widgets::{BorderType, GraphType},
|
||||
Frame,
|
||||
};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use crate::canvas::drawing_utils::widget_block;
|
||||
|
||||
use super::time_chart::{
|
||||
Axis, Dataset, LegendPosition, Point, TimeChart, DEFAULT_LEGEND_CONSTRAINTS,
|
||||
@ -42,9 +43,15 @@ pub struct TimeGraph<'a> {
|
||||
/// The border style.
|
||||
pub border_style: Style,
|
||||
|
||||
/// The border type.
|
||||
pub border_type: BorderType,
|
||||
|
||||
/// The graph title.
|
||||
pub title: Cow<'a, str>,
|
||||
|
||||
/// Whether this graph is selected.
|
||||
pub is_selected: bool,
|
||||
|
||||
/// Whether this graph is expanded.
|
||||
pub is_expanded: bool,
|
||||
|
||||
@ -100,29 +107,6 @@ impl TimeGraph<'_> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Generates a title for the [`TimeGraph`] widget, given the available
|
||||
/// space.
|
||||
fn generate_title(&self, draw_loc: Rect) -> Line<'_> {
|
||||
if self.is_expanded {
|
||||
let title_base = concat_string!(self.title, "── Esc to go back ");
|
||||
Line::from(vec![
|
||||
Span::styled(self.title.as_ref(), self.title_style),
|
||||
Span::styled(
|
||||
concat_string!(
|
||||
"─",
|
||||
"─".repeat(usize::from(draw_loc.width).saturating_sub(
|
||||
UnicodeSegmentation::graphemes(title_base.as_str(), true).count() + 2
|
||||
)),
|
||||
"─ Esc to go back "
|
||||
),
|
||||
self.border_style,
|
||||
),
|
||||
])
|
||||
} else {
|
||||
Line::from(Span::styled(self.title.as_ref(), self.title_style))
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws a time graph at [`Rect`] location provided by `draw_loc`. A time
|
||||
/// graph is used to display data points throughout time in the x-axis.
|
||||
///
|
||||
@ -139,10 +123,18 @@ impl TimeGraph<'_> {
|
||||
// This is some ugly manual loop unswitching. Maybe unnecessary.
|
||||
// TODO: Optimize this step. Cut out unneeded points.
|
||||
let data = graph_data.iter().map(create_dataset).collect();
|
||||
let block = Block::default()
|
||||
.title(self.generate_title(draw_loc))
|
||||
.borders(Borders::ALL)
|
||||
.border_style(self.border_style);
|
||||
|
||||
let block = {
|
||||
let mut b = widget_block(false, self.is_selected, self.border_type)
|
||||
.border_style(self.border_style)
|
||||
.title_top(Line::styled(self.title.as_ref(), self.title_style));
|
||||
|
||||
if self.is_expanded {
|
||||
b = b.title_top(Line::styled(" Esc to go back ", self.title_style).right_aligned())
|
||||
}
|
||||
|
||||
b
|
||||
};
|
||||
|
||||
f.render_widget(
|
||||
TimeChart::new(data)
|
||||
@ -186,10 +178,10 @@ mod test {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use tui::{
|
||||
layout::Rect,
|
||||
style::{Color, Style},
|
||||
symbols::Marker,
|
||||
text::{Line, Span},
|
||||
text::Span,
|
||||
widgets::BorderType,
|
||||
};
|
||||
|
||||
use super::TimeGraph;
|
||||
@ -210,6 +202,8 @@ mod test {
|
||||
y_labels: &Y_LABELS,
|
||||
graph_style: Style::default().fg(Color::Red),
|
||||
border_style: Style::default().fg(Color::Blue),
|
||||
border_type: BorderType::Plain,
|
||||
is_selected: false,
|
||||
is_expanded: false,
|
||||
title_style: Style::default().fg(Color::Cyan),
|
||||
legend_position: None,
|
||||
@ -252,26 +246,4 @@ mod test {
|
||||
assert_eq!(y_axis.labels, actual.labels);
|
||||
assert_eq!(y_axis.style, actual.style);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn time_graph_gen_title() {
|
||||
let mut time_graph = create_time_graph();
|
||||
let draw_loc = Rect::new(0, 0, 32, 100);
|
||||
|
||||
let title = time_graph.generate_title(draw_loc);
|
||||
assert_eq!(
|
||||
title,
|
||||
Line::from(Span::styled(" Network ", Style::default().fg(Color::Cyan)))
|
||||
);
|
||||
|
||||
time_graph.is_expanded = true;
|
||||
let title = time_graph.generate_title(draw_loc);
|
||||
assert_eq!(
|
||||
title,
|
||||
Line::from(vec![
|
||||
Span::styled(" Network ", Style::default().fg(Color::Cyan)),
|
||||
Span::styled("───── Esc to go back ", Style::default().fg(Color::Blue))
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -84,26 +84,25 @@ impl Painter {
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: I can do this text effect as just a border now!
|
||||
let left_name = left_table.get_pretty_name();
|
||||
let right_name = right_table.get_pretty_name();
|
||||
|
||||
let num_spaces =
|
||||
usize::from(draw_loc.width).saturating_sub(6 + left_name.len() + right_name.len());
|
||||
let carousel_text_style = if widget_id == app_state.current_widget.widget_id {
|
||||
self.styles.highlighted_border_style
|
||||
} else {
|
||||
self.styles.text_style
|
||||
};
|
||||
|
||||
let left_arrow_text = vec![
|
||||
Line::default(),
|
||||
Line::from(Span::styled(
|
||||
format!("◄ {left_name}"),
|
||||
self.colours.text_style,
|
||||
)),
|
||||
Line::from(Span::styled(format!("◄ {left_name}"), carousel_text_style)),
|
||||
];
|
||||
|
||||
let right_arrow_text = vec![
|
||||
Line::default(),
|
||||
Line::from(Span::styled(
|
||||
format!("{right_name} ►"),
|
||||
self.colours.text_style,
|
||||
)),
|
||||
Line::from(Span::styled(format!("{right_name} ►"), carousel_text_style)),
|
||||
];
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
|
@ -4,19 +4,16 @@ use std::cmp::min;
|
||||
use tui::{
|
||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||
text::{Line, Span, Text},
|
||||
widgets::{Block, Borders, Paragraph, Wrap},
|
||||
widgets::{Block, Paragraph, Wrap},
|
||||
Frame,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
app::{App, KillSignal, MAX_PROCESS_SIGNAL},
|
||||
canvas::Painter,
|
||||
canvas::{drawing_utils::dialog_block, Painter},
|
||||
widgets::ProcWidgetMode,
|
||||
};
|
||||
|
||||
const DD_BASE: &str = " Confirm Kill Process ── Esc to close ";
|
||||
const DD_ERROR_BASE: &str = " Error ── Esc to close ";
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_os = "linux")] {
|
||||
const SIGNAL_TEXT: [&str; 63] = [
|
||||
@ -211,12 +208,12 @@ impl Painter {
|
||||
if MAX_PROCESS_SIGNAL == 1 || !app_state.app_config_fields.is_advanced_kill {
|
||||
let (yes_button, no_button) = match app_state.delete_dialog_state.selected_signal {
|
||||
KillSignal::Kill(_) => (
|
||||
Span::styled("Yes", self.colours.selected_text_style),
|
||||
Span::styled("No", self.colours.text_style),
|
||||
Span::styled("Yes", self.styles.selected_text_style),
|
||||
Span::styled("No", self.styles.text_style),
|
||||
),
|
||||
KillSignal::Cancel => (
|
||||
Span::styled("Yes", self.colours.text_style),
|
||||
Span::styled("No", self.colours.selected_text_style),
|
||||
Span::styled("Yes", self.styles.text_style),
|
||||
Span::styled("No", self.styles.selected_text_style),
|
||||
),
|
||||
};
|
||||
|
||||
@ -322,11 +319,11 @@ impl Painter {
|
||||
let mut buttons = SIGNAL_TEXT
|
||||
[scroll_offset + 1..min((layout.len()) + scroll_offset, SIGNAL_TEXT.len())]
|
||||
.iter()
|
||||
.map(|text| Span::styled(*text, self.colours.text_style))
|
||||
.map(|text| Span::styled(*text, self.styles.text_style))
|
||||
.collect::<Vec<Span<'_>>>();
|
||||
buttons.insert(0, Span::styled(SIGNAL_TEXT[0], self.colours.text_style));
|
||||
buttons.insert(0, Span::styled(SIGNAL_TEXT[0], self.styles.text_style));
|
||||
buttons[selected - scroll_offset] =
|
||||
Span::styled(SIGNAL_TEXT[selected], self.colours.selected_text_style);
|
||||
Span::styled(SIGNAL_TEXT[selected], self.styles.selected_text_style);
|
||||
|
||||
app_state.delete_dialog_state.button_positions = layout
|
||||
.iter()
|
||||
@ -354,45 +351,24 @@ impl Painter {
|
||||
) -> bool {
|
||||
if let Some(dd_text) = dd_text {
|
||||
let dd_title = if app_state.dd_err.is_some() {
|
||||
Line::from(vec![
|
||||
Span::styled(" Error ", self.colours.widget_title_style),
|
||||
Span::styled(
|
||||
format!(
|
||||
"─{}─ Esc to close ",
|
||||
"─".repeat(
|
||||
usize::from(draw_loc.width)
|
||||
.saturating_sub(DD_ERROR_BASE.chars().count() + 2)
|
||||
)
|
||||
),
|
||||
self.colours.border_style,
|
||||
),
|
||||
])
|
||||
Line::styled(" Error ", self.styles.widget_title_style)
|
||||
} else {
|
||||
Line::from(vec![
|
||||
Span::styled(" Confirm Kill Process ", self.colours.widget_title_style),
|
||||
Span::styled(
|
||||
format!(
|
||||
"─{}─ Esc to close ",
|
||||
"─".repeat(
|
||||
usize::from(draw_loc.width)
|
||||
.saturating_sub(DD_BASE.chars().count() + 2)
|
||||
)
|
||||
),
|
||||
self.colours.border_style,
|
||||
),
|
||||
])
|
||||
Line::styled(" Confirm Kill Process ", self.styles.widget_title_style)
|
||||
};
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(dd_text)
|
||||
.block(
|
||||
Block::default()
|
||||
.title(dd_title)
|
||||
.style(self.colours.border_style)
|
||||
.borders(Borders::ALL)
|
||||
.border_style(self.colours.border_style),
|
||||
dialog_block(self.styles.border_type)
|
||||
.title_top(dd_title)
|
||||
.title_top(
|
||||
Line::styled(" Esc to close ", self.styles.widget_title_style)
|
||||
.right_aligned(),
|
||||
)
|
||||
.style(self.styles.border_style)
|
||||
.border_style(self.styles.border_style),
|
||||
)
|
||||
.style(self.colours.text_style)
|
||||
.style(self.styles.text_style)
|
||||
.alignment(Alignment::Center)
|
||||
.wrap(Wrap { trim: true }),
|
||||
draw_loc,
|
||||
|
@ -3,19 +3,17 @@ use std::cmp::{max, min};
|
||||
use tui::{
|
||||
layout::{Alignment, Rect},
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Borders, Paragraph, Wrap},
|
||||
widgets::{Paragraph, Wrap},
|
||||
Frame,
|
||||
};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use crate::{
|
||||
app::App,
|
||||
canvas::Painter,
|
||||
canvas::{drawing_utils::dialog_block, Painter},
|
||||
constants::{self, HELP_TEXT},
|
||||
};
|
||||
|
||||
const HELP_BASE: &str = " Help ── Esc to close ";
|
||||
|
||||
// TODO: [REFACTOR] Make generic dialog boxes to build off of instead?
|
||||
impl Painter {
|
||||
fn help_text_lines(&self) -> Vec<Line<'_>> {
|
||||
@ -28,12 +26,12 @@ impl Painter {
|
||||
if itx > 0 {
|
||||
if let Some(header) = section.next() {
|
||||
styled_help_spans.push(Span::default());
|
||||
styled_help_spans.push(Span::styled(*header, self.colours.table_header_style));
|
||||
styled_help_spans.push(Span::styled(*header, self.styles.table_header_style));
|
||||
}
|
||||
}
|
||||
|
||||
section.for_each(|&text| {
|
||||
styled_help_spans.push(Span::styled(text, self.colours.text_style))
|
||||
styled_help_spans.push(Span::styled(text, self.styles.text_style))
|
||||
});
|
||||
});
|
||||
|
||||
@ -43,24 +41,12 @@ impl Painter {
|
||||
pub fn draw_help_dialog(&self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect) {
|
||||
let styled_help_text = self.help_text_lines();
|
||||
|
||||
let help_title = Line::from(vec![
|
||||
Span::styled(" Help ", self.colours.widget_title_style),
|
||||
Span::styled(
|
||||
format!(
|
||||
"─{}─ Esc to close ",
|
||||
"─".repeat(
|
||||
usize::from(draw_loc.width).saturating_sub(HELP_BASE.chars().count() + 2)
|
||||
)
|
||||
),
|
||||
self.colours.border_style,
|
||||
),
|
||||
]);
|
||||
|
||||
let block = Block::default()
|
||||
.title(help_title)
|
||||
.style(self.colours.border_style)
|
||||
.borders(Borders::ALL)
|
||||
.border_style(self.colours.border_style);
|
||||
let block = dialog_block(self.styles.border_type)
|
||||
.border_style(self.styles.border_style)
|
||||
.title_top(Line::styled(" Help ", self.styles.widget_title_style))
|
||||
.title_top(
|
||||
Line::styled(" Esc to close ", self.styles.widget_title_style).right_aligned(),
|
||||
);
|
||||
|
||||
if app_state.should_get_widget_bounds() {
|
||||
// We must also recalculate how many lines are wrapping to properly get
|
||||
@ -116,7 +102,7 @@ impl Painter {
|
||||
f.render_widget(
|
||||
Paragraph::new(styled_help_text.clone())
|
||||
.block(block)
|
||||
.style(self.colours.text_style)
|
||||
.style(self.styles.text_style)
|
||||
.alignment(Alignment::Left)
|
||||
.wrap(Wrap { trim: true })
|
||||
.scroll((
|
||||
|
@ -1,6 +1,11 @@
|
||||
use std::{cmp::min, time::Instant};
|
||||
|
||||
use tui::layout::Rect;
|
||||
use tui::{
|
||||
layout::Rect,
|
||||
widgets::{Block, BorderType, Borders},
|
||||
};
|
||||
|
||||
use super::SIDE_BORDERS;
|
||||
|
||||
/// Calculate how many bars are to be drawn within basic mode's components.
|
||||
pub fn calculate_basic_use_bars(use_percentage: f64, num_bars_available: usize) -> usize {
|
||||
@ -30,6 +35,30 @@ pub fn should_hide_x_label(
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a widget block.
|
||||
pub fn widget_block(is_basic: bool, is_selected: bool, border_type: BorderType) -> Block<'static> {
|
||||
let mut block = Block::default().border_type(border_type);
|
||||
|
||||
if is_basic {
|
||||
if is_selected {
|
||||
block = block.borders(SIDE_BORDERS);
|
||||
} else {
|
||||
block = block.borders(Borders::empty());
|
||||
}
|
||||
} else {
|
||||
block = block.borders(Borders::all());
|
||||
}
|
||||
|
||||
block
|
||||
}
|
||||
|
||||
/// Return a dialog block.
|
||||
pub fn dialog_block(border_type: BorderType) -> Block<'static> {
|
||||
Block::default()
|
||||
.border_type(border_type)
|
||||
.borders(Borders::all())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
|
@ -1,23 +1,24 @@
|
||||
use tui::{
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Borders, Cell, Paragraph, Row, Table, Tabs},
|
||||
widgets::{Cell, Paragraph, Row, Table, Tabs},
|
||||
Frame,
|
||||
};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use crate::{
|
||||
app::App,
|
||||
canvas::{drawing_utils::calculate_basic_use_bars, Painter},
|
||||
canvas::{
|
||||
drawing_utils::{calculate_basic_use_bars, widget_block},
|
||||
Painter,
|
||||
},
|
||||
constants::*,
|
||||
data_conversion::BatteryDuration,
|
||||
};
|
||||
|
||||
impl Painter {
|
||||
pub fn draw_battery(
|
||||
&self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
&self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
let should_get_widget_bounds = app_state.should_get_widget_bounds();
|
||||
if let Some(battery_widget_state) = app_state
|
||||
@ -28,9 +29,9 @@ impl Painter {
|
||||
{
|
||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||
let border_style = if is_on_widget {
|
||||
self.colours.highlighted_border_style
|
||||
self.styles.highlighted_border_style
|
||||
} else {
|
||||
self.colours.border_style
|
||||
self.styles.border_style
|
||||
};
|
||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||
0
|
||||
@ -38,35 +39,23 @@ impl Painter {
|
||||
app_state.app_config_fields.table_gap
|
||||
};
|
||||
|
||||
let title = if app_state.is_expanded {
|
||||
const TITLE_BASE: &str = " Battery ── Esc to go back ";
|
||||
Line::from(vec![
|
||||
Span::styled(" Battery ", self.colours.widget_title_style),
|
||||
Span::styled(
|
||||
format!(
|
||||
"─{}─ Esc to go back ",
|
||||
"─".repeat(usize::from(draw_loc.width).saturating_sub(
|
||||
UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
|
||||
))
|
||||
),
|
||||
border_style,
|
||||
),
|
||||
])
|
||||
} else {
|
||||
Line::from(Span::styled(" Battery ", self.colours.widget_title_style))
|
||||
};
|
||||
let block = {
|
||||
let mut block = widget_block(
|
||||
app_state.app_config_fields.use_basic_mode,
|
||||
is_on_widget,
|
||||
self.styles.border_type,
|
||||
)
|
||||
.border_style(border_style)
|
||||
.title_top(Line::styled(" Battery ", self.styles.widget_title_style));
|
||||
|
||||
let battery_block = if draw_border {
|
||||
Block::default()
|
||||
.title(title)
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_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 app_state.is_expanded {
|
||||
block = block.title_top(
|
||||
Line::styled(" Esc to go back ", self.styles.widget_title_style)
|
||||
.right_aligned(),
|
||||
)
|
||||
}
|
||||
|
||||
block
|
||||
};
|
||||
|
||||
if app_state.converted_data.battery_data.len() > 1 {
|
||||
@ -95,8 +84,8 @@ impl Painter {
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.divider(tui::symbols::line::VERTICAL)
|
||||
.style(self.colours.text_style)
|
||||
.highlight_style(self.colours.selected_text_style)
|
||||
.style(self.styles.text_style)
|
||||
.highlight_style(self.styles.selected_text_style)
|
||||
.select(battery_widget_state.currently_selected_battery_index),
|
||||
tab_draw_loc,
|
||||
);
|
||||
@ -120,9 +109,11 @@ impl Painter {
|
||||
}
|
||||
}
|
||||
|
||||
let is_basic = app_state.app_config_fields.use_basic_mode;
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)])
|
||||
.horizontal_margin(u16::from(!(is_on_widget || draw_border)))
|
||||
.horizontal_margin(u16::from(!(is_on_widget || is_basic)))
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc)[0];
|
||||
|
||||
@ -144,15 +135,15 @@ impl Painter {
|
||||
|
||||
let mut battery_charge_rows = Vec::with_capacity(2);
|
||||
battery_charge_rows.push(Row::new([
|
||||
Cell::from("Charge").style(self.colours.text_style)
|
||||
Cell::from("Charge").style(self.styles.text_style)
|
||||
]));
|
||||
battery_charge_rows.push(Row::new([Cell::from(bars).style(
|
||||
if charge_percentage < 10.0 {
|
||||
self.colours.low_battery
|
||||
self.styles.low_battery
|
||||
} else if charge_percentage < 50.0 {
|
||||
self.colours.medium_battery
|
||||
self.styles.medium_battery
|
||||
} else {
|
||||
self.colours.high_battery
|
||||
self.styles.high_battery
|
||||
},
|
||||
)]));
|
||||
|
||||
@ -160,16 +151,16 @@ impl Painter {
|
||||
battery_rows.push(Row::new([""]).bottom_margin(table_gap + 1));
|
||||
battery_rows.push(
|
||||
Row::new(["Rate", &battery_details.watt_consumption])
|
||||
.style(self.colours.text_style),
|
||||
.style(self.styles.text_style),
|
||||
);
|
||||
|
||||
battery_rows.push(
|
||||
Row::new(["State", &battery_details.state]).style(self.colours.text_style),
|
||||
Row::new(["State", &battery_details.state]).style(self.styles.text_style),
|
||||
);
|
||||
|
||||
let mut time: String; // Keep string lifetime in scope.
|
||||
{
|
||||
let style = self.colours.text_style;
|
||||
let style = self.styles.text_style;
|
||||
match &battery_details.battery_duration {
|
||||
BatteryDuration::ToEmpty(secs) => {
|
||||
time = long_time(*secs);
|
||||
@ -198,7 +189,7 @@ impl Painter {
|
||||
}
|
||||
|
||||
battery_rows.push(
|
||||
Row::new(["Health", &battery_details.health]).style(self.colours.text_style),
|
||||
Row::new(["Health", &battery_details.health]).style(self.styles.text_style),
|
||||
);
|
||||
|
||||
let header = if app_state.converted_data.battery_data.len() > 1 {
|
||||
@ -210,7 +201,7 @@ impl Painter {
|
||||
// Draw bar
|
||||
f.render_widget(
|
||||
Table::new(battery_charge_rows, [Constraint::Percentage(100)])
|
||||
.block(battery_block.clone())
|
||||
.block(block.clone())
|
||||
.header(header.clone()),
|
||||
margined_draw_loc,
|
||||
);
|
||||
@ -221,7 +212,7 @@ impl Painter {
|
||||
battery_rows,
|
||||
[Constraint::Percentage(50), Constraint::Percentage(50)],
|
||||
)
|
||||
.block(battery_block)
|
||||
.block(block)
|
||||
.header(header),
|
||||
margined_draw_loc,
|
||||
);
|
||||
@ -230,13 +221,10 @@ impl Painter {
|
||||
|
||||
contents.push(Line::from(Span::styled(
|
||||
"No data found for this battery",
|
||||
self.colours.text_style,
|
||||
self.styles.text_style,
|
||||
)));
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(contents).block(battery_block),
|
||||
margined_draw_loc,
|
||||
);
|
||||
f.render_widget(Paragraph::new(contents).block(block), margined_draw_loc);
|
||||
}
|
||||
|
||||
if should_get_widget_bounds {
|
||||
@ -253,7 +241,6 @@ impl Painter {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_hms(secs: i64) -> (i64, i64, i64) {
|
||||
let hours = secs / (60 * 60);
|
||||
let minutes = (secs / 60) - hours * 60;
|
||||
@ -266,23 +253,16 @@ fn long_time(secs: i64) -> String {
|
||||
let (hours, minutes, seconds) = get_hms(secs);
|
||||
|
||||
if hours > 0 {
|
||||
format!(
|
||||
"{} hour{}, {} minute{}, {} second{}",
|
||||
hours,
|
||||
if hours == 1 { "" } else { "s" },
|
||||
minutes,
|
||||
if minutes == 1 { "" } else { "s" },
|
||||
seconds,
|
||||
if seconds == 1 { "" } else { "s" },
|
||||
)
|
||||
let h = if hours == 1 { "hour" } else { "hours" };
|
||||
let m = if minutes == 1 { "minute" } else { "minutes" };
|
||||
let s = if seconds == 1 { "second" } else { "seconds" };
|
||||
|
||||
format!("{hours} {h}, {minutes} {m}, {seconds} {s}")
|
||||
} else {
|
||||
format!(
|
||||
"{} minute{}, {} second{}",
|
||||
minutes,
|
||||
if minutes == 1 { "" } else { "s" },
|
||||
seconds,
|
||||
if seconds == 1 { "" } else { "s" },
|
||||
)
|
||||
let m = if minutes == 1 { "minute" } else { "minutes" };
|
||||
let s = if seconds == 1 { "second" } else { "seconds" };
|
||||
|
||||
format!("{minutes} {m}, {seconds} {s}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ use std::cmp::min;
|
||||
|
||||
use tui::{
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
widgets::Block,
|
||||
Frame,
|
||||
};
|
||||
|
||||
@ -10,9 +9,9 @@ use crate::{
|
||||
app::App,
|
||||
canvas::{
|
||||
components::pipe_gauge::{LabelLimit, PipeGauge},
|
||||
drawing_utils::widget_block,
|
||||
Painter,
|
||||
},
|
||||
constants::*,
|
||||
data_collection::cpu::CpuDataType,
|
||||
data_conversion::CpuWidgetData,
|
||||
};
|
||||
@ -38,9 +37,8 @@ impl Painter {
|
||||
|
||||
if app_state.current_widget.widget_id == widget_id {
|
||||
f.render_widget(
|
||||
Block::default()
|
||||
.borders(SIDE_BORDERS)
|
||||
.border_style(self.colours.highlighted_border_style),
|
||||
widget_block(true, true, self.styles.border_type)
|
||||
.border_style(self.styles.highlighted_border_style),
|
||||
draw_loc,
|
||||
);
|
||||
}
|
||||
@ -156,10 +154,10 @@ impl Painter {
|
||||
};
|
||||
|
||||
let (outer, style) = match data_type {
|
||||
CpuDataType::Avg => ("AVG".to_string(), self.colours.avg_cpu_colour),
|
||||
CpuDataType::Avg => ("AVG".to_string(), self.styles.avg_cpu_colour),
|
||||
CpuDataType::Cpu(index) => (
|
||||
format!("{index:<3}",),
|
||||
self.colours.cpu_colour_styles[index % self.colours.cpu_colour_styles.len()],
|
||||
self.styles.cpu_colour_styles[index % self.styles.cpu_colour_styles.len()],
|
||||
),
|
||||
};
|
||||
let inner = format!("{:>3.0}%", last_entry.round());
|
||||
|
@ -136,13 +136,13 @@ impl Painter {
|
||||
CpuWidgetData::All => None,
|
||||
CpuWidgetData::Entry { data, .. } => {
|
||||
let style = if show_avg_cpu && itx == AVG_POSITION {
|
||||
self.colours.avg_cpu_colour
|
||||
self.styles.avg_cpu_colour
|
||||
} else if itx == ALL_POSITION {
|
||||
self.colours.all_cpu_colour
|
||||
self.styles.all_cpu_colour
|
||||
} else {
|
||||
let offset_position = itx - 1; // Because of the all position
|
||||
self.colours.cpu_colour_styles[(offset_position - show_avg_offset)
|
||||
% self.colours.cpu_colour_styles.len()]
|
||||
self.styles.cpu_colour_styles[(offset_position - show_avg_offset)
|
||||
% self.styles.cpu_colour_styles.len()]
|
||||
};
|
||||
|
||||
Some(GraphData {
|
||||
@ -158,11 +158,11 @@ impl Painter {
|
||||
cpu_data.get(current_scroll_position)
|
||||
{
|
||||
let style = if show_avg_cpu && current_scroll_position == AVG_POSITION {
|
||||
self.colours.avg_cpu_colour
|
||||
self.styles.avg_cpu_colour
|
||||
} else {
|
||||
let offset_position = current_scroll_position - 1; // Because of the all position
|
||||
self.colours.cpu_colour_styles
|
||||
[(offset_position - show_avg_offset) % self.colours.cpu_colour_styles.len()]
|
||||
self.styles.cpu_colour_styles
|
||||
[(offset_position - show_avg_offset) % self.styles.cpu_colour_styles.len()]
|
||||
};
|
||||
|
||||
vec![GraphData {
|
||||
@ -228,11 +228,13 @@ impl Painter {
|
||||
hide_x_labels,
|
||||
y_bounds: Y_BOUNDS,
|
||||
y_labels: &Y_LABELS,
|
||||
graph_style: self.colours.graph_style,
|
||||
graph_style: self.styles.graph_style,
|
||||
border_style,
|
||||
border_type: self.styles.border_type,
|
||||
title,
|
||||
is_selected: app_state.current_widget.widget_id == widget_id,
|
||||
is_expanded: app_state.is_expanded,
|
||||
title_style: self.colours.widget_title_style,
|
||||
title_style: self.styles.widget_title_style,
|
||||
legend_position: None,
|
||||
legend_constraints: None,
|
||||
marker,
|
||||
|
@ -1,13 +1,11 @@
|
||||
use tui::{
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
widgets::Block,
|
||||
Frame,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
app::App,
|
||||
canvas::{components::pipe_gauge::PipeGauge, Painter},
|
||||
constants::*,
|
||||
canvas::{components::pipe_gauge::PipeGauge, drawing_utils::widget_block, Painter},
|
||||
};
|
||||
|
||||
impl Painter {
|
||||
@ -19,9 +17,8 @@ impl Painter {
|
||||
|
||||
if app_state.current_widget.widget_id == widget_id {
|
||||
f.render_widget(
|
||||
Block::default()
|
||||
.borders(SIDE_BORDERS)
|
||||
.border_style(self.colours.highlighted_border_style),
|
||||
widget_block(true, true, self.styles.border_type)
|
||||
.border_style(self.styles.highlighted_border_style),
|
||||
draw_loc,
|
||||
);
|
||||
}
|
||||
@ -50,8 +47,8 @@ impl Painter {
|
||||
.ratio(ram_percentage / 100.0)
|
||||
.start_label("RAM")
|
||||
.inner_label(memory_fraction_label)
|
||||
.label_style(self.colours.ram_style)
|
||||
.gauge_style(self.colours.ram_style),
|
||||
.label_style(self.styles.ram_style)
|
||||
.gauge_style(self.styles.ram_style),
|
||||
);
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
@ -75,8 +72,8 @@ impl Painter {
|
||||
.ratio(cache_percentage / 100.0)
|
||||
.start_label("CHE")
|
||||
.inner_label(cache_fraction_label)
|
||||
.label_style(self.colours.cache_style)
|
||||
.gauge_style(self.colours.cache_style),
|
||||
.label_style(self.styles.cache_style)
|
||||
.gauge_style(self.styles.cache_style),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -100,8 +97,8 @@ impl Painter {
|
||||
.ratio(swap_percentage / 100.0)
|
||||
.start_label("SWP")
|
||||
.inner_label(swap_fraction_label)
|
||||
.label_style(self.colours.swap_style)
|
||||
.gauge_style(self.colours.swap_style),
|
||||
.label_style(self.styles.swap_style)
|
||||
.gauge_style(self.styles.swap_style),
|
||||
);
|
||||
}
|
||||
|
||||
@ -124,8 +121,8 @@ impl Painter {
|
||||
.ratio(arc_percentage / 100.0)
|
||||
.start_label("ARC")
|
||||
.inner_label(arc_fraction_label)
|
||||
.label_style(self.colours.arc_style)
|
||||
.gauge_style(self.colours.arc_style),
|
||||
.label_style(self.styles.arc_style)
|
||||
.gauge_style(self.styles.arc_style),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -133,7 +130,7 @@ impl Painter {
|
||||
#[cfg(feature = "gpu")]
|
||||
{
|
||||
if let Some(gpu_data) = &app_state.converted_data.gpu_data {
|
||||
let gpu_styles = &self.colours.gpu_colours;
|
||||
let gpu_styles = &self.styles.gpu_colours;
|
||||
let mut color_index = 0;
|
||||
|
||||
gpu_data.iter().for_each(|gpu_data_vec| {
|
||||
|
@ -55,7 +55,7 @@ impl Painter {
|
||||
let mem_label = format!("RAM:{label_percent}{label_frac}");
|
||||
points.push(GraphData {
|
||||
points: &app_state.converted_data.mem_data,
|
||||
style: self.colours.ram_style,
|
||||
style: self.styles.ram_style,
|
||||
name: Some(mem_label.into()),
|
||||
});
|
||||
}
|
||||
@ -64,7 +64,7 @@ impl Painter {
|
||||
let cache_label = format!("CHE:{label_percent}{label_frac}");
|
||||
points.push(GraphData {
|
||||
points: &app_state.converted_data.cache_data,
|
||||
style: self.colours.cache_style,
|
||||
style: self.styles.cache_style,
|
||||
name: Some(cache_label.into()),
|
||||
});
|
||||
}
|
||||
@ -72,7 +72,7 @@ impl Painter {
|
||||
let swap_label = format!("SWP:{label_percent}{label_frac}");
|
||||
points.push(GraphData {
|
||||
points: &app_state.converted_data.swap_data,
|
||||
style: self.colours.swap_style,
|
||||
style: self.styles.swap_style,
|
||||
name: Some(swap_label.into()),
|
||||
});
|
||||
}
|
||||
@ -81,7 +81,7 @@ impl Painter {
|
||||
let arc_label = format!("ARC:{label_percent}{label_frac}");
|
||||
points.push(GraphData {
|
||||
points: &app_state.converted_data.arc_data,
|
||||
style: self.colours.arc_style,
|
||||
style: self.styles.arc_style,
|
||||
name: Some(arc_label.into()),
|
||||
});
|
||||
}
|
||||
@ -89,7 +89,7 @@ impl Painter {
|
||||
{
|
||||
if let Some(gpu_data) = &app_state.converted_data.gpu_data {
|
||||
let mut color_index = 0;
|
||||
let gpu_styles = &self.colours.gpu_colours;
|
||||
let gpu_styles = &self.styles.gpu_colours;
|
||||
gpu_data.iter().for_each(|gpu| {
|
||||
let gpu_label =
|
||||
format!("{}:{}{}", gpu.name, gpu.mem_percent, gpu.mem_total);
|
||||
@ -128,11 +128,13 @@ impl Painter {
|
||||
hide_x_labels,
|
||||
y_bounds: Y_BOUNDS,
|
||||
y_labels: &Y_LABELS,
|
||||
graph_style: self.colours.graph_style,
|
||||
graph_style: self.styles.graph_style,
|
||||
border_style,
|
||||
border_type: self.styles.border_type,
|
||||
title: " Memory ".into(),
|
||||
is_selected: app_state.current_widget.widget_id == widget_id,
|
||||
is_expanded: app_state.is_expanded,
|
||||
title_style: self.colours.widget_title_style,
|
||||
title_style: self.styles.widget_title_style,
|
||||
legend_position: app_state.app_config_fields.memory_legend_position,
|
||||
legend_constraints: Some((Constraint::Ratio(3, 4), Constraint::Ratio(3, 4))),
|
||||
marker,
|
||||
|
@ -5,7 +5,10 @@ use tui::{
|
||||
Frame,
|
||||
};
|
||||
|
||||
use crate::{app::App, canvas::Painter, constants::*};
|
||||
use crate::{
|
||||
app::App,
|
||||
canvas::{drawing_utils::widget_block, Painter},
|
||||
};
|
||||
|
||||
impl Painter {
|
||||
pub fn draw_basic_network(
|
||||
@ -30,9 +33,8 @@ impl Painter {
|
||||
|
||||
if app_state.current_widget.widget_id == widget_id {
|
||||
f.render_widget(
|
||||
Block::default()
|
||||
.borders(SIDE_BORDERS)
|
||||
.border_style(self.colours.highlighted_border_style),
|
||||
widget_block(true, true, self.styles.border_type)
|
||||
.border_style(self.styles.highlighted_border_style),
|
||||
draw_loc,
|
||||
);
|
||||
}
|
||||
@ -43,13 +45,13 @@ impl Painter {
|
||||
let total_tx_label = format!("Total TX: {}", app_state.converted_data.total_tx_display);
|
||||
|
||||
let net_text = vec![
|
||||
Line::from(Span::styled(rx_label, self.colours.rx_style)),
|
||||
Line::from(Span::styled(tx_label, self.colours.tx_style)),
|
||||
Line::from(Span::styled(rx_label, self.styles.rx_style)),
|
||||
Line::from(Span::styled(tx_label, self.styles.tx_style)),
|
||||
];
|
||||
|
||||
let total_net_text = vec![
|
||||
Line::from(Span::styled(total_rx_label, self.colours.total_rx_style)),
|
||||
Line::from(Span::styled(total_tx_label, self.colours.total_tx_style)),
|
||||
Line::from(Span::styled(total_rx_label, self.styles.total_rx_style)),
|
||||
Line::from(Span::styled(total_tx_label, self.styles.total_tx_style)),
|
||||
];
|
||||
|
||||
f.render_widget(Paragraph::new(net_text).block(Block::default()), net_loc[0]);
|
||||
|
@ -107,17 +107,17 @@ impl Painter {
|
||||
vec![
|
||||
GraphData {
|
||||
points: network_data_rx,
|
||||
style: self.colours.rx_style,
|
||||
style: self.styles.rx_style,
|
||||
name: Some(format!("RX: {:7}", app_state.converted_data.rx_display).into()),
|
||||
},
|
||||
GraphData {
|
||||
points: network_data_tx,
|
||||
style: self.colours.tx_style,
|
||||
style: self.styles.tx_style,
|
||||
name: Some(format!("TX: {:7}", app_state.converted_data.tx_display).into()),
|
||||
},
|
||||
GraphData {
|
||||
points: &[],
|
||||
style: self.colours.total_rx_style,
|
||||
style: self.styles.total_rx_style,
|
||||
name: Some(
|
||||
format!("Total RX: {:7}", app_state.converted_data.total_rx_display)
|
||||
.into(),
|
||||
@ -125,7 +125,7 @@ impl Painter {
|
||||
},
|
||||
GraphData {
|
||||
points: &[],
|
||||
style: self.colours.total_tx_style,
|
||||
style: self.styles.total_tx_style,
|
||||
name: Some(
|
||||
format!("Total TX: {:7}", app_state.converted_data.total_tx_display)
|
||||
.into(),
|
||||
@ -136,12 +136,12 @@ impl Painter {
|
||||
vec![
|
||||
GraphData {
|
||||
points: network_data_rx,
|
||||
style: self.colours.rx_style,
|
||||
style: self.styles.rx_style,
|
||||
name: Some((&app_state.converted_data.rx_display).into()),
|
||||
},
|
||||
GraphData {
|
||||
points: network_data_tx,
|
||||
style: self.colours.tx_style,
|
||||
style: self.styles.tx_style,
|
||||
name: Some((&app_state.converted_data.tx_display).into()),
|
||||
},
|
||||
]
|
||||
@ -158,11 +158,13 @@ impl Painter {
|
||||
hide_x_labels,
|
||||
y_bounds,
|
||||
y_labels: &y_labels,
|
||||
graph_style: self.colours.graph_style,
|
||||
graph_style: self.styles.graph_style,
|
||||
border_style,
|
||||
border_type: self.styles.border_type,
|
||||
title: " Network ".into(),
|
||||
is_selected: app_state.current_widget.widget_id == widget_id,
|
||||
is_expanded: app_state.is_expanded,
|
||||
title_style: self.colours.widget_title_style,
|
||||
title_style: self.styles.widget_title_style,
|
||||
legend_position: app_state.app_config_fields.network_legend_position,
|
||||
legend_constraints: Some(legend_constraints),
|
||||
marker,
|
||||
@ -183,10 +185,10 @@ impl Painter {
|
||||
|
||||
// Gross but I need it to work...
|
||||
let total_network = vec![Row::new([
|
||||
Text::styled(rx_display, self.colours.rx_style),
|
||||
Text::styled(tx_display, self.colours.tx_style),
|
||||
Text::styled(total_rx_display, self.colours.total_rx_style),
|
||||
Text::styled(total_tx_display, self.colours.total_tx_style),
|
||||
Text::styled(rx_display, self.styles.rx_style),
|
||||
Text::styled(tx_display, self.styles.tx_style),
|
||||
Text::styled(total_rx_display, self.styles.total_rx_style),
|
||||
Text::styled(total_tx_display, self.styles.total_tx_style),
|
||||
])];
|
||||
|
||||
// Draw
|
||||
@ -198,15 +200,15 @@ impl Painter {
|
||||
.map(Constraint::Length)
|
||||
.collect::<Vec<_>>()),
|
||||
)
|
||||
.header(Row::new(NETWORK_HEADERS).style(self.colours.table_header_style))
|
||||
.header(Row::new(NETWORK_HEADERS).style(self.styles.table_header_style))
|
||||
.block(Block::default().borders(Borders::ALL).border_style(
|
||||
if app_state.current_widget.widget_id == widget_id {
|
||||
self.colours.highlighted_border_style
|
||||
self.styles.highlighted_border_style
|
||||
} else {
|
||||
self.colours.border_style
|
||||
self.styles.border_style
|
||||
},
|
||||
))
|
||||
.style(self.colours.text_style),
|
||||
.style(self.styles.text_style),
|
||||
draw_loc,
|
||||
);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use tui::{
|
||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||
style::Style,
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Borders, Paragraph},
|
||||
widgets::Paragraph,
|
||||
Frame,
|
||||
};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
@ -11,9 +11,9 @@ use crate::{
|
||||
app::{App, AppSearchState},
|
||||
canvas::{
|
||||
components::data_table::{DrawInfo, SelectionState},
|
||||
drawing_utils::widget_block,
|
||||
Painter,
|
||||
},
|
||||
constants::*,
|
||||
};
|
||||
|
||||
const SORT_MENU_WIDTH: u16 = 7;
|
||||
@ -23,11 +23,11 @@ impl Painter {
|
||||
/// - `widget_id` here represents the widget ID of the process widget
|
||||
/// itself!
|
||||
pub fn draw_process(
|
||||
&self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
&self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
if let Some(proc_widget_state) = app_state.states.proc_state.widget_states.get(&widget_id) {
|
||||
let search_height = if draw_border { 5 } else { 3 };
|
||||
let is_basic = app_state.app_config_fields.use_basic_mode;
|
||||
let search_height = if !is_basic { 5 } else { 3 };
|
||||
let is_sort_open = proc_widget_state.is_sort_open;
|
||||
|
||||
let mut proc_draw_loc = draw_loc;
|
||||
@ -38,13 +38,7 @@ impl Painter {
|
||||
.split(draw_loc);
|
||||
proc_draw_loc = processes_chunk[0];
|
||||
|
||||
self.draw_search_field(
|
||||
f,
|
||||
app_state,
|
||||
processes_chunk[1],
|
||||
draw_border,
|
||||
widget_id + 1,
|
||||
);
|
||||
self.draw_search_field(f, app_state, processes_chunk[1], widget_id + 1);
|
||||
}
|
||||
|
||||
if is_sort_open {
|
||||
@ -110,8 +104,7 @@ impl Painter {
|
||||
/// - `widget_id` represents the widget ID of the search box itself --- NOT
|
||||
/// the process widget state that is stored.
|
||||
fn draw_search_field(
|
||||
&self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
|
||||
widget_id: u64,
|
||||
&self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
fn build_query_span(
|
||||
search_state: &AppSearchState, available_width: usize, is_on_widget: bool,
|
||||
@ -157,16 +150,18 @@ impl Painter {
|
||||
}
|
||||
}
|
||||
|
||||
let is_basic = app_state.app_config_fields.use_basic_mode;
|
||||
|
||||
if let Some(proc_widget_state) = app_state
|
||||
.states
|
||||
.proc_state
|
||||
.widget_states
|
||||
.get_mut(&(widget_id - 1))
|
||||
{
|
||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||
let is_selected = widget_id == app_state.current_widget.widget_id;
|
||||
let num_columns = usize::from(draw_loc.width);
|
||||
const SEARCH_TITLE: &str = "> ";
|
||||
let offset = if draw_border { 4 } else { 2 }; // width of 3 removed for >_|
|
||||
let offset = 4;
|
||||
let available_width = if num_columns > (offset + 3) {
|
||||
num_columns - offset
|
||||
} else {
|
||||
@ -182,18 +177,18 @@ impl Painter {
|
||||
let query_with_cursor = build_query_span(
|
||||
&proc_widget_state.proc_search.search_state,
|
||||
available_width,
|
||||
is_on_widget,
|
||||
self.colours.selected_text_style,
|
||||
self.colours.text_style,
|
||||
is_selected,
|
||||
self.styles.selected_text_style,
|
||||
self.styles.text_style,
|
||||
);
|
||||
|
||||
let mut search_text = vec![Line::from({
|
||||
let mut search_vec = vec![Span::styled(
|
||||
SEARCH_TITLE,
|
||||
if is_on_widget {
|
||||
self.colours.table_header_style
|
||||
if is_selected {
|
||||
self.styles.table_header_style
|
||||
} else {
|
||||
self.colours.text_style
|
||||
self.styles.text_style
|
||||
},
|
||||
)];
|
||||
search_vec.extend(query_with_cursor);
|
||||
@ -203,21 +198,21 @@ impl Painter {
|
||||
|
||||
// Text options shamelessly stolen from VS Code.
|
||||
let case_style = if !proc_widget_state.proc_search.is_ignoring_case {
|
||||
self.colours.selected_text_style
|
||||
self.styles.selected_text_style
|
||||
} else {
|
||||
self.colours.text_style
|
||||
self.styles.text_style
|
||||
};
|
||||
|
||||
let whole_word_style = if proc_widget_state.proc_search.is_searching_whole_word {
|
||||
self.colours.selected_text_style
|
||||
self.styles.selected_text_style
|
||||
} else {
|
||||
self.colours.text_style
|
||||
self.styles.text_style
|
||||
};
|
||||
|
||||
let regex_style = if proc_widget_state.proc_search.is_searching_with_regex {
|
||||
self.colours.selected_text_style
|
||||
self.styles.selected_text_style
|
||||
} else {
|
||||
self.colours.text_style
|
||||
self.styles.text_style
|
||||
};
|
||||
|
||||
// TODO: [MOUSE] Mouse support for these in search
|
||||
@ -245,54 +240,42 @@ impl Painter {
|
||||
} else {
|
||||
""
|
||||
},
|
||||
self.colours.invalid_query_style,
|
||||
self.styles.invalid_query_style,
|
||||
)));
|
||||
search_text.push(option_text);
|
||||
|
||||
let current_border_style =
|
||||
if proc_widget_state.proc_search.search_state.is_invalid_search {
|
||||
self.colours.invalid_query_style
|
||||
} else if is_on_widget {
|
||||
self.colours.highlighted_border_style
|
||||
self.styles.invalid_query_style
|
||||
} else if is_selected {
|
||||
self.styles.highlighted_border_style
|
||||
} else {
|
||||
self.colours.border_style
|
||||
self.styles.border_style
|
||||
};
|
||||
|
||||
let title = Span::styled(
|
||||
if draw_border {
|
||||
const TITLE_BASE: &str = " Esc to close ";
|
||||
let repeat_num =
|
||||
usize::from(draw_loc.width).saturating_sub(TITLE_BASE.chars().count() + 2);
|
||||
format!("{} Esc to close ", "─".repeat(repeat_num))
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
current_border_style,
|
||||
);
|
||||
let process_search_block = {
|
||||
let mut block = widget_block(is_basic, is_selected, self.styles.border_type)
|
||||
.border_style(current_border_style);
|
||||
|
||||
let process_search_block = if draw_border {
|
||||
Block::default()
|
||||
.title(title)
|
||||
.borders(Borders::ALL)
|
||||
.border_style(current_border_style)
|
||||
} else if is_on_widget {
|
||||
Block::default()
|
||||
.borders(SIDE_BORDERS)
|
||||
.border_style(current_border_style)
|
||||
} else {
|
||||
Block::default().borders(Borders::NONE)
|
||||
if !is_basic {
|
||||
block = block.title_top(
|
||||
Line::styled(" Esc to close ", current_border_style).right_aligned(),
|
||||
)
|
||||
}
|
||||
|
||||
block
|
||||
};
|
||||
|
||||
let margined_draw_loc = Layout::default()
|
||||
.constraints([Constraint::Percentage(100)])
|
||||
.horizontal_margin(u16::from(!(is_on_widget || draw_border)))
|
||||
.horizontal_margin(u16::from(is_basic && !is_selected))
|
||||
.direction(Direction::Horizontal)
|
||||
.split(draw_loc)[0];
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(search_text)
|
||||
.block(process_search_block)
|
||||
.style(self.colours.text_style)
|
||||
.style(self.styles.text_style)
|
||||
.alignment(Alignment::Left),
|
||||
margined_draw_loc,
|
||||
);
|
||||
|
@ -16,7 +16,7 @@ use std::{
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use config::style::ColourPalette;
|
||||
use config::style::Styles;
|
||||
pub use config::Config;
|
||||
pub(crate) use error::{OptionError, OptionResult};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
@ -144,7 +144,7 @@ fn create_config_at_path(path: &Path) -> anyhow::Result<Config> {
|
||||
/// - If the user does NOT pass in a path explicitly, then just show a warning,
|
||||
/// but continue. This is in case they do not want to write a default config file at
|
||||
/// the XDG locations, for example.
|
||||
pub fn get_or_create_config(config_path: Option<&Path>) -> anyhow::Result<Config> {
|
||||
pub(crate) fn get_or_create_config(config_path: Option<&Path>) -> anyhow::Result<Config> {
|
||||
let adjusted_config_path = get_config_path(config_path);
|
||||
|
||||
match &adjusted_config_path {
|
||||
@ -196,9 +196,7 @@ pub fn get_or_create_config(config_path: Option<&Path>) -> anyhow::Result<Config
|
||||
}
|
||||
|
||||
/// Initialize the app.
|
||||
pub(crate) fn init_app(
|
||||
args: BottomArgs, config: Config,
|
||||
) -> Result<(App, BottomLayout, ColourPalette)> {
|
||||
pub(crate) fn init_app(args: BottomArgs, config: Config) -> Result<(App, BottomLayout, Styles)> {
|
||||
use BottomWidgetType::*;
|
||||
|
||||
// Since everything takes a reference, but we want to take ownership here to
|
||||
@ -206,7 +204,7 @@ pub(crate) fn init_app(
|
||||
let args = &args;
|
||||
let config = &config;
|
||||
|
||||
let styling = ColourPalette::new(args, config)?;
|
||||
let styling = Styles::new(args, config)?;
|
||||
|
||||
let (widget_layout, default_widget_id, default_widget_type_option) =
|
||||
get_widget_layout(args, config)
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Config options around styling.
|
||||
|
||||
mod battery;
|
||||
mod borders;
|
||||
mod cpu;
|
||||
mod graphs;
|
||||
mod memory;
|
||||
@ -19,7 +20,7 @@ use memory::MemoryStyle;
|
||||
use network::NetworkStyle;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tables::TableStyle;
|
||||
use tui::style::Style;
|
||||
use tui::{style::Style, widgets::BorderType};
|
||||
use utils::{opt, set_colour, set_colour_list, set_style};
|
||||
use widgets::WidgetStyle;
|
||||
|
||||
@ -92,45 +93,47 @@ pub(crate) struct StyleConfig {
|
||||
pub(crate) widgets: Option<WidgetStyle>,
|
||||
}
|
||||
|
||||
/// The actual internal representation of the configured colours,
|
||||
/// as a "palette".
|
||||
/// The actual internal representation of the configured styles.
|
||||
#[derive(Debug)]
|
||||
pub struct ColourPalette {
|
||||
pub ram_style: Style,
|
||||
pub struct Styles {
|
||||
pub(crate) ram_style: Style,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub cache_style: Style,
|
||||
pub swap_style: Style,
|
||||
pub arc_style: Style,
|
||||
pub gpu_colours: Vec<Style>,
|
||||
pub rx_style: Style,
|
||||
pub tx_style: Style,
|
||||
pub total_rx_style: Style,
|
||||
pub total_tx_style: Style,
|
||||
pub all_cpu_colour: Style,
|
||||
pub avg_cpu_colour: Style,
|
||||
pub cpu_colour_styles: Vec<Style>,
|
||||
pub border_style: Style,
|
||||
pub highlighted_border_style: Style,
|
||||
pub text_style: Style,
|
||||
pub selected_text_style: Style,
|
||||
pub table_header_style: Style,
|
||||
pub widget_title_style: Style,
|
||||
pub graph_style: Style,
|
||||
pub graph_legend_style: Style,
|
||||
pub high_battery: Style,
|
||||
pub medium_battery: Style,
|
||||
pub low_battery: Style,
|
||||
pub invalid_query_style: Style,
|
||||
pub disabled_text_style: Style,
|
||||
pub(crate) cache_style: Style,
|
||||
pub(crate) swap_style: Style,
|
||||
#[cfg(feature = "zfs")]
|
||||
pub(crate) arc_style: Style,
|
||||
#[cfg(feature = "gpu")]
|
||||
pub(crate) gpu_colours: Vec<Style>,
|
||||
pub(crate) rx_style: Style,
|
||||
pub(crate) tx_style: Style,
|
||||
pub(crate) total_rx_style: Style,
|
||||
pub(crate) total_tx_style: Style,
|
||||
pub(crate) all_cpu_colour: Style,
|
||||
pub(crate) avg_cpu_colour: Style,
|
||||
pub(crate) cpu_colour_styles: Vec<Style>,
|
||||
pub(crate) border_style: Style,
|
||||
pub(crate) highlighted_border_style: Style,
|
||||
pub(crate) text_style: Style,
|
||||
pub(crate) selected_text_style: Style,
|
||||
pub(crate) table_header_style: Style,
|
||||
pub(crate) widget_title_style: Style,
|
||||
pub(crate) graph_style: Style,
|
||||
pub(crate) graph_legend_style: Style,
|
||||
pub(crate) high_battery: Style,
|
||||
pub(crate) medium_battery: Style,
|
||||
pub(crate) low_battery: Style,
|
||||
pub(crate) invalid_query_style: Style,
|
||||
pub(crate) disabled_text_style: Style,
|
||||
pub(crate) border_type: BorderType,
|
||||
}
|
||||
|
||||
impl Default for ColourPalette {
|
||||
impl Default for Styles {
|
||||
fn default() -> Self {
|
||||
Self::default_palette()
|
||||
Self::default_style()
|
||||
}
|
||||
}
|
||||
|
||||
impl ColourPalette {
|
||||
impl Styles {
|
||||
pub fn new(args: &BottomArgs, config: &Config) -> anyhow::Result<Self> {
|
||||
let mut palette = match &args.style.theme {
|
||||
Some(theme) => Self::from_theme(theme)?,
|
||||
@ -141,8 +144,8 @@ impl ColourPalette {
|
||||
};
|
||||
|
||||
// Apply theme from config on top.
|
||||
if let Some(style) = &config.styles {
|
||||
palette.set_colours_from_palette(style)?;
|
||||
if let Some(config_style) = &config.styles {
|
||||
palette.set_styles_from_config(config_style)?;
|
||||
}
|
||||
|
||||
Ok(palette)
|
||||
@ -151,7 +154,7 @@ impl ColourPalette {
|
||||
fn from_theme(theme: &str) -> anyhow::Result<Self> {
|
||||
let lower_case = theme.to_lowercase();
|
||||
match lower_case.as_str() {
|
||||
"default" => Ok(Self::default_palette()),
|
||||
"default" => Ok(Self::default_style()),
|
||||
"default-light" => Ok(Self::default_light_mode()),
|
||||
"gruvbox" => Ok(Self::gruvbox_palette()),
|
||||
"gruvbox-light" => Ok(Self::gruvbox_light_palette()),
|
||||
@ -164,7 +167,7 @@ impl ColourPalette {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_colours_from_palette(&mut self, config: &StyleConfig) -> OptionResult<()> {
|
||||
fn set_styles_from_config(&mut self, config: &StyleConfig) -> OptionResult<()> {
|
||||
// CPU
|
||||
set_colour!(self.avg_cpu_colour, config.cpu, avg_entry_color);
|
||||
set_colour!(self.all_cpu_colour, config.cpu, all_entry_color);
|
||||
@ -215,6 +218,12 @@ impl ColourPalette {
|
||||
selected_border_color
|
||||
);
|
||||
|
||||
if let Some(widgets) = &config.widgets {
|
||||
if let Some(widget_borders) = widgets.widget_border_type {
|
||||
self.border_type = widget_borders.into();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -224,20 +233,14 @@ mod test {
|
||||
|
||||
use tui::style::{Color, Style};
|
||||
|
||||
use super::ColourPalette;
|
||||
use super::Styles;
|
||||
use crate::options::config::style::utils::str_to_colour;
|
||||
|
||||
#[test]
|
||||
fn default_selected_colour_works() {
|
||||
let mut colours = ColourPalette::default();
|
||||
let original_selected_text_colour = ColourPalette::default_palette()
|
||||
.selected_text_style
|
||||
.fg
|
||||
.unwrap();
|
||||
let original_selected_bg_colour = ColourPalette::default_palette()
|
||||
.selected_text_style
|
||||
.bg
|
||||
.unwrap();
|
||||
let mut colours = Styles::default();
|
||||
let original_selected_text_colour = Styles::default_style().selected_text_style.fg.unwrap();
|
||||
let original_selected_bg_colour = Styles::default_style().selected_text_style.bg.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
colours.selected_text_style,
|
||||
@ -259,11 +262,11 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn built_in_colour_schemes_work() {
|
||||
ColourPalette::from_theme("default").unwrap();
|
||||
ColourPalette::from_theme("default-light").unwrap();
|
||||
ColourPalette::from_theme("gruvbox").unwrap();
|
||||
ColourPalette::from_theme("gruvbox-light").unwrap();
|
||||
ColourPalette::from_theme("nord").unwrap();
|
||||
ColourPalette::from_theme("nord-light").unwrap();
|
||||
Styles::from_theme("default").unwrap();
|
||||
Styles::from_theme("default-light").unwrap();
|
||||
Styles::from_theme("gruvbox").unwrap();
|
||||
Styles::from_theme("gruvbox-light").unwrap();
|
||||
Styles::from_theme("nord").unwrap();
|
||||
Styles::from_theme("nord-light").unwrap();
|
||||
}
|
||||
}
|
||||
|
42
src/options/config/style/borders.rs
Normal file
42
src/options/config/style/borders.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tui::widgets::BorderType;
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug, Serialize)]
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
pub(crate) enum WidgetBorderType {
|
||||
#[default]
|
||||
Default,
|
||||
Rounded,
|
||||
Double,
|
||||
Thick,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for WidgetBorderType {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let value = String::deserialize(deserializer)?.to_lowercase();
|
||||
match value.as_str() {
|
||||
"default" => Ok(WidgetBorderType::Default),
|
||||
"rounded" => Ok(WidgetBorderType::Rounded),
|
||||
"double" => Ok(WidgetBorderType::Double),
|
||||
"thick" => Ok(WidgetBorderType::Thick),
|
||||
_ => Err(serde::de::Error::custom(
|
||||
"doesn't match any widget border type",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WidgetBorderType> for BorderType {
|
||||
fn from(value: WidgetBorderType) -> Self {
|
||||
match value {
|
||||
WidgetBorderType::Default => BorderType::Plain,
|
||||
WidgetBorderType::Rounded => BorderType::Rounded,
|
||||
WidgetBorderType::Double => BorderType::Double,
|
||||
WidgetBorderType::Thick => BorderType::Thick,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
use tui::style::{Color, Modifier, Style};
|
||||
use tui::{
|
||||
style::{Color, Modifier, Style},
|
||||
widgets::BorderType,
|
||||
};
|
||||
|
||||
use super::color;
|
||||
use crate::options::config::style::ColourPalette;
|
||||
use crate::options::config::style::Styles;
|
||||
|
||||
impl ColourPalette {
|
||||
pub(crate) fn default_palette() -> Self {
|
||||
impl Styles {
|
||||
pub(crate) fn default_style() -> Self {
|
||||
const FIRST_COLOUR: Color = Color::LightMagenta;
|
||||
const SECOND_COLOUR: Color = Color::LightYellow;
|
||||
const THIRD_COLOUR: Color = Color::LightCyan;
|
||||
@ -22,7 +25,9 @@ impl ColourPalette {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: color!(FIFTH_COLOUR),
|
||||
swap_style: color!(SECOND_COLOUR),
|
||||
#[cfg(feature = "zfs")]
|
||||
arc_style: color!(THIRD_COLOUR),
|
||||
#[cfg(feature = "gpu")]
|
||||
gpu_colours: vec![
|
||||
color!(FOURTH_COLOUR),
|
||||
color!(Color::LightBlue),
|
||||
@ -61,6 +66,7 @@ impl ColourPalette {
|
||||
low_battery: color!(Color::Red),
|
||||
invalid_query_style: color!(Color::Red),
|
||||
disabled_text_style: color!(Color::DarkGray),
|
||||
border_type: BorderType::Plain,
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +76,9 @@ impl ColourPalette {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: color!(Color::LightRed),
|
||||
swap_style: color!(Color::Red),
|
||||
#[cfg(feature = "zfs")]
|
||||
arc_style: color!(Color::LightBlue),
|
||||
#[cfg(feature = "gpu")]
|
||||
gpu_colours: vec![
|
||||
color!(Color::LightGreen),
|
||||
color!(Color::LightCyan),
|
||||
@ -101,7 +109,7 @@ impl ColourPalette {
|
||||
graph_style: color!(Color::Black),
|
||||
graph_legend_style: color!(Color::Black),
|
||||
disabled_text_style: color!(Color::Gray),
|
||||
..Self::default_palette()
|
||||
..Self::default_style()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,21 @@
|
||||
use tui::style::{Color, Modifier};
|
||||
use tui::{
|
||||
style::{Color, Modifier},
|
||||
widgets::BorderType,
|
||||
};
|
||||
|
||||
use super::{color, hex};
|
||||
use crate::options::config::style::{utils::convert_hex_to_color, ColourPalette};
|
||||
use crate::options::config::style::{utils::convert_hex_to_color, Styles};
|
||||
|
||||
impl ColourPalette {
|
||||
impl Styles {
|
||||
pub(crate) fn gruvbox_palette() -> Self {
|
||||
Self {
|
||||
ram_style: hex!("#8ec07c"),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: hex!("#b16286"),
|
||||
swap_style: hex!("#fabd2f"),
|
||||
#[cfg(feature = "zfs")]
|
||||
arc_style: hex!("#689d6a"),
|
||||
#[cfg(feature = "gpu")]
|
||||
gpu_colours: vec![
|
||||
hex!("#d79921"),
|
||||
hex!("#458588"),
|
||||
@ -61,6 +66,7 @@ impl ColourPalette {
|
||||
low_battery: hex!("#fb4934"),
|
||||
invalid_query_style: color!(Color::Red),
|
||||
disabled_text_style: hex!("#665c54"),
|
||||
border_type: BorderType::Plain,
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +76,9 @@ impl ColourPalette {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: hex!("#d79921"),
|
||||
swap_style: hex!("#cc241d"),
|
||||
#[cfg(feature = "zfs")]
|
||||
arc_style: hex!("#689d6a"),
|
||||
#[cfg(feature = "gpu")]
|
||||
gpu_colours: vec![
|
||||
hex!("#9d0006"),
|
||||
hex!("#98971a"),
|
||||
@ -121,6 +129,7 @@ impl ColourPalette {
|
||||
low_battery: hex!("#cc241d"),
|
||||
invalid_query_style: color!(Color::Red),
|
||||
disabled_text_style: hex!("#d5c4a1"),
|
||||
border_type: BorderType::Plain,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,21 @@
|
||||
use tui::style::{Color, Modifier};
|
||||
use tui::{
|
||||
style::{Color, Modifier},
|
||||
widgets::BorderType,
|
||||
};
|
||||
|
||||
use super::{color, hex};
|
||||
use crate::options::config::style::{utils::convert_hex_to_color, ColourPalette};
|
||||
use crate::options::config::style::{utils::convert_hex_to_color, Styles};
|
||||
|
||||
impl ColourPalette {
|
||||
impl Styles {
|
||||
pub(crate) fn nord_palette() -> Self {
|
||||
Self {
|
||||
ram_style: hex!("#88c0d0"),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: hex!("#d8dee9"),
|
||||
swap_style: hex!("#d08770"),
|
||||
#[cfg(feature = "zfs")]
|
||||
arc_style: hex!("#5e81ac"),
|
||||
#[cfg(feature = "gpu")]
|
||||
gpu_colours: vec![
|
||||
hex!("#8fbcbb"),
|
||||
hex!("#81a1c1"),
|
||||
@ -49,6 +54,7 @@ impl ColourPalette {
|
||||
low_battery: hex!("#bf616a"),
|
||||
invalid_query_style: color!(Color::Red),
|
||||
disabled_text_style: hex!("#4c566a"),
|
||||
border_type: BorderType::Plain,
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +64,9 @@ impl ColourPalette {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
cache_style: hex!("#4c566a"),
|
||||
swap_style: hex!("#d08770"),
|
||||
#[cfg(feature = "zfs")]
|
||||
arc_style: hex!("#5e81ac"),
|
||||
#[cfg(feature = "gpu")]
|
||||
gpu_colours: vec![
|
||||
hex!("#8fbcbb"),
|
||||
hex!("#88c0d0"),
|
||||
@ -97,6 +105,7 @@ impl ColourPalette {
|
||||
low_battery: hex!("#bf616a"),
|
||||
invalid_query_style: color!(Color::Red),
|
||||
disabled_text_style: hex!("#d8dee9"),
|
||||
border_type: BorderType::Plain,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{ColorStr, TextStyleConfig};
|
||||
use super::{borders::WidgetBorderType, ColorStr, TextStyleConfig};
|
||||
|
||||
/// General styling for generic widgets.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
@ -26,4 +26,7 @@ pub(crate) struct WidgetStyle {
|
||||
|
||||
/// Text styling for text when representing something that is disabled.
|
||||
pub(crate) disabled_text: Option<TextStyleConfig>,
|
||||
|
||||
/// Widget borders type.
|
||||
pub(crate) widget_border_type: Option<WidgetBorderType>,
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ use crate::{
|
||||
},
|
||||
data_collection::cpu::CpuDataType,
|
||||
data_conversion::CpuWidgetData,
|
||||
options::config::{cpu::CpuDefault, style::ColourPalette},
|
||||
options::config::{cpu::CpuDefault, style::Styles},
|
||||
};
|
||||
|
||||
pub enum CpuWidgetColumn {
|
||||
@ -106,15 +106,14 @@ impl DataToCell<CpuWidgetColumn> for CpuWidgetTableData {
|
||||
#[inline(always)]
|
||||
fn style_row<'a>(&self, row: Row<'a>, painter: &Painter) -> Row<'a> {
|
||||
let style = match self {
|
||||
CpuWidgetTableData::All => painter.colours.all_cpu_colour,
|
||||
CpuWidgetTableData::All => painter.styles.all_cpu_colour,
|
||||
CpuWidgetTableData::Entry {
|
||||
data_type,
|
||||
last_entry: _,
|
||||
} => match data_type {
|
||||
CpuDataType::Avg => painter.colours.avg_cpu_colour,
|
||||
CpuDataType::Avg => painter.styles.avg_cpu_colour,
|
||||
CpuDataType::Cpu(index) => {
|
||||
painter.colours.cpu_colour_styles
|
||||
[index % painter.colours.cpu_colour_styles.len()]
|
||||
painter.styles.cpu_colour_styles[index % painter.styles.cpu_colour_styles.len()]
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -142,7 +141,7 @@ pub struct CpuWidgetState {
|
||||
impl CpuWidgetState {
|
||||
pub(crate) fn new(
|
||||
config: &AppConfigFields, default_selection: CpuDefault, current_display_time: u64,
|
||||
autohide_timer: Option<Instant>, colours: &ColourPalette,
|
||||
autohide_timer: Option<Instant>, colours: &Styles,
|
||||
) -> Self {
|
||||
const COLUMNS: [Column<CpuWidgetColumn>; 2] = [
|
||||
Column::soft(CpuWidgetColumn::Cpu, Some(0.5)),
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
ColumnHeader, DataTableColumn, DataTableProps, DataTableStyling, DataToCell, SortColumn,
|
||||
SortDataTable, SortDataTableProps, SortOrder, SortsRow,
|
||||
},
|
||||
options::config::style::ColourPalette,
|
||||
options::config::style::Styles,
|
||||
utils::{data_prefixes::get_decimal_bytes, general::sort_partial_fn},
|
||||
};
|
||||
|
||||
@ -275,9 +275,7 @@ const fn default_disk_columns() -> [SortColumn<DiskColumn>; 8] {
|
||||
}
|
||||
|
||||
impl DiskTableWidget {
|
||||
pub fn new(
|
||||
config: &AppConfigFields, palette: &ColourPalette, columns: Option<&[DiskColumn]>,
|
||||
) -> Self {
|
||||
pub fn new(config: &AppConfigFields, palette: &Styles, columns: Option<&[DiskColumn]>) -> Self {
|
||||
let props = SortDataTableProps {
|
||||
inner: DataTableProps {
|
||||
title: Some(" Disks ".into()),
|
||||
|
@ -23,7 +23,7 @@ use crate::{
|
||||
DataTableStyling, SortColumn, SortDataTable, SortDataTableProps, SortOrder, SortsRow,
|
||||
},
|
||||
data_collection::processes::{Pid, ProcessHarvest},
|
||||
options::config::style::ColourPalette,
|
||||
options::config::style::Styles,
|
||||
};
|
||||
|
||||
/// ProcessSearchState only deals with process' search's current settings and
|
||||
@ -160,7 +160,7 @@ pub struct ProcWidgetState {
|
||||
}
|
||||
|
||||
impl ProcWidgetState {
|
||||
fn new_sort_table(config: &AppConfigFields, palette: &ColourPalette) -> SortTable {
|
||||
fn new_sort_table(config: &AppConfigFields, palette: &Styles) -> SortTable {
|
||||
const COLUMNS: [Column<SortTableColumn>; 1] = [Column::hard(SortTableColumn, 7)];
|
||||
|
||||
let props = DataTableProps {
|
||||
@ -177,7 +177,7 @@ impl ProcWidgetState {
|
||||
}
|
||||
|
||||
fn new_process_table(
|
||||
config: &AppConfigFields, colours: &ColourPalette, columns: Vec<SortColumn<ProcColumn>>,
|
||||
config: &AppConfigFields, colours: &Styles, columns: Vec<SortColumn<ProcColumn>>,
|
||||
default_index: usize, default_order: SortOrder,
|
||||
) -> ProcessTable {
|
||||
let inner_props = DataTableProps {
|
||||
@ -200,7 +200,7 @@ impl ProcWidgetState {
|
||||
|
||||
pub fn new(
|
||||
config: &AppConfigFields, mode: ProcWidgetMode, table_config: ProcTableConfig,
|
||||
colours: &ColourPalette, config_columns: &Option<IndexSet<ProcWidgetColumn>>,
|
||||
colours: &Styles, config_columns: &Option<IndexSet<ProcWidgetColumn>>,
|
||||
) -> Self {
|
||||
let process_search_state = {
|
||||
let mut pss = ProcessSearchState::default();
|
||||
@ -1130,7 +1130,7 @@ mod test {
|
||||
|
||||
fn init_state(table_config: ProcTableConfig, columns: &[ProcWidgetColumn]) -> ProcWidgetState {
|
||||
let config = AppConfigFields::default();
|
||||
let styling = ColourPalette::default();
|
||||
let styling = Styles::default();
|
||||
let columns = Some(columns.iter().cloned().collect());
|
||||
|
||||
ProcWidgetState::new(
|
||||
|
@ -340,7 +340,7 @@ impl DataToCell<ProcColumn> for ProcWidgetData {
|
||||
#[inline(always)]
|
||||
fn style_row<'a>(&self, row: Row<'a>, painter: &Painter) -> Row<'a> {
|
||||
if self.disabled {
|
||||
row.style(painter.colours.disabled_text_style)
|
||||
row.style(painter.styles.disabled_text_style)
|
||||
} else {
|
||||
row
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
SortDataTable, SortDataTableProps, SortOrder, SortsRow,
|
||||
},
|
||||
data_collection::temperature::TemperatureType,
|
||||
options::config::style::ColourPalette,
|
||||
options::config::style::Styles,
|
||||
utils::general::sort_partial_fn,
|
||||
};
|
||||
|
||||
@ -100,7 +100,7 @@ pub struct TempWidgetState {
|
||||
}
|
||||
|
||||
impl TempWidgetState {
|
||||
pub(crate) fn new(config: &AppConfigFields, palette: &ColourPalette) -> Self {
|
||||
pub(crate) fn new(config: &AppConfigFields, palette: &Styles) -> Self {
|
||||
let columns = [
|
||||
SortColumn::soft(TempWidgetColumn::Sensor, Some(0.8)),
|
||||
SortColumn::soft(TempWidgetColumn::Temp, None).default_descending(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user