Skip to content

Commit

Permalink
feature: support custom widget borders (#1642)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ClementTsang authored Dec 5, 2024
1 parent 1fe17dd commit 0d182e4
Show file tree
Hide file tree
Showing 32 changed files with 498 additions and 495 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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]
Expand Down
20 changes: 20 additions & 0 deletions schema/nightly/bottom.json
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,15 @@
}
]
},
"WidgetBorderType": {
"type": "string",
"enum": [
"Default",
"Rounded",
"Double",
"Thick"
]
},
"WidgetStyle": {
"description": "General styling for generic widgets.",
"type": "object",
Expand Down Expand Up @@ -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": [
Expand Down
42 changes: 17 additions & 25 deletions src/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand All @@ -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.
Expand Down Expand Up @@ -131,7 +131,7 @@ impl Painter {
});

let painter = Painter {
colours: styling,
styles: styling,
previous_height: 0,
previous_width: 0,
row_constraints,
Expand All @@ -149,17 +149,17 @@ 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
}
}

fn draw_frozen_indicator(&self, f: &mut Frame<'_>, draw_loc: Rect) {
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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
_ => {}
}
}
Expand Down Expand Up @@ -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),
_ => {}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/canvas/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pub mod data_table;
pub mod time_graph;
mod tui_widget;

pub mod widget_carousel;

pub use tui_widget::*;
83 changes: 37 additions & 46 deletions src/canvas/components/data_table/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,18 @@ 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,
SortType,
};
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,
};

Expand Down Expand Up @@ -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);
let border_style = if is_selected {
self.styling.highlighted_border_style
} else {
self.styling.border_style
};

if let Some(title) = self.generate_title(draw_info, data_len) {
block.title(title)
} else {
block
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);
}
} else if draw_info.is_on_widget() {
// Implies it is basic mode but selected.
Block::default()
.borders(SIDE_BORDERS)
.border_style(border_style)
} else {
Block::default().borders(Borders::NONE)
}

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();
Expand All @@ -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)
})
}

Expand Down Expand Up @@ -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);
}
Expand Down
20 changes: 11 additions & 9 deletions src/canvas/components/data_table/styling.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
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,
pub title_style: Style,
}

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,
}
}
}
Loading

0 comments on commit 0d182e4

Please sign in to comment.