refactor: Add back scroll position and expanded

This commit is contained in:
ClementTsang 2021-09-07 21:40:47 -04:00
parent d8a6a2344e
commit 955840b412
18 changed files with 201 additions and 70 deletions

View File

@ -1140,6 +1140,7 @@ pub fn create_layout_tree(
.width(width)
.height(height)
.basic_mode(app_config_fields.use_basic_mode)
.show_scroll_index(app_config_fields.show_table_scroll_position)
.into(),
);
}
@ -1151,6 +1152,7 @@ pub fn create_layout_tree(
.width(width)
.height(height)
.basic_mode(app_config_fields.use_basic_mode)
.show_scroll_index(app_config_fields.show_table_scroll_position)
.into(),
);
}
@ -1161,6 +1163,7 @@ pub fn create_layout_tree(
.width(width)
.height(height)
.basic_mode(app_config_fields.use_basic_mode)
.show_scroll_index(app_config_fields.show_table_scroll_position)
.into(),
);
}

View File

@ -117,7 +117,7 @@ pub trait Widget {
/// Returns a [`Widget`]'s "pretty" display name.
fn get_pretty_name(&self) -> &'static str;
/// Returns a new [`BlockBuilder`], which can become a [`Block`] if [`BlockBuilder::build`] is called.
/// Returns a new [`BlockBuilder`], which can become a [`tui::widgets::Block`] if [`BlockBuilder::build`] is called.
/// The default implementation builds a [`Block`] that has all 4 borders with no selection or expansion.
fn block(&self) -> BlockBuilder {
BlockBuilder::new(self.get_pretty_name())
@ -126,6 +126,7 @@ pub trait Widget {
/// Draws a [`Widget`]. The default implementation draws nothing.
fn draw<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
expanded: bool,
) {
}

View File

@ -67,6 +67,15 @@ impl Scrollable {
self.current_index
}
/// Returns the text indicator [`String`].
pub fn text_indicator(&self) -> String {
format!(
"({} of {})",
min(self.current_index + 1, self.num_items),
self.num_items
)
}
/// Returns the start of the [`Scrollable`] when displayed.
pub fn get_list_start(&mut self, num_visible_rows: usize) -> usize {
// So it's probably confusing - what is the "window index"?

View File

@ -1,8 +1,11 @@
use crossterm::event::{KeyEvent, MouseEvent};
use tui::{backend::Backend, layout::Rect, widgets::Block, Frame};
use tui::{backend::Backend, layout::Rect, Frame};
use crate::{
app::{event::WidgetEventResult, text_table::SimpleColumn, Component, TextTable},
app::{
event::WidgetEventResult, text_table::SimpleColumn, widgets::tui_stuff::BlockBuilder,
Component, TextTable,
},
canvas::Painter,
};
@ -42,7 +45,7 @@ impl SortMenu {
/// Draws a [`tui::widgets::Table`] on screen corresponding to the sort columns of this [`SortableTextTable`].
pub fn draw_sort_menu<B: Backend, C: SortableColumn>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, columns: &[C], block: Block<'_>,
&mut self, painter: &Painter, f: &mut Frame<'_, B>, columns: &[C], block: BlockBuilder,
block_area: Rect,
) {
self.set_bounds(block_area);
@ -53,7 +56,7 @@ impl SortMenu {
.collect::<Vec<_>>();
self.table
.draw_tui_table(painter, f, &data, block, block_area, true);
.draw_tui_table(painter, f, &data, block, block_area, true, false);
}
}

View File

@ -1,11 +1,12 @@
use std::borrow::Cow;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind};
use tui::{backend::Backend, layout::Rect, widgets::Block, Frame};
use tui::{backend::Backend, layout::Rect, Frame};
use crate::{
app::{
event::{ReturnSignal, WidgetEventResult},
widgets::tui_stuff::BlockBuilder,
Component, TextTable,
},
canvas::Painter,
@ -371,10 +372,18 @@ where
/// it will only create as many columns as it can grab data from both sources from.
pub fn draw_tui_table<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, data: &TextTableDataRef,
block: Block<'_>, block_area: Rect, show_selected_entry: bool,
block: BlockBuilder, block_area: Rect, show_selected_entry: bool,
show_scroll_position: bool,
) {
self.table
.draw_tui_table(painter, f, data, block, block_area, show_selected_entry);
self.table.draw_tui_table(
painter,
f,
data,
block,
block_area,
show_selected_entry,
show_scroll_position,
);
}
}

View File

@ -9,13 +9,13 @@ use tui::{
layout::{Constraint, Rect},
style::Style,
text::Text,
widgets::{Block, Table},
widgets::Table,
Frame,
};
use unicode_segmentation::UnicodeSegmentation;
use crate::{
app::{event::WidgetEventResult, Component, Scrollable},
app::{event::WidgetEventResult, widgets::tui_stuff::BlockBuilder, Component, Scrollable},
canvas::Painter,
constants::TABLE_GAP_HEIGHT_LIMIT,
};
@ -381,10 +381,21 @@ where
/// Draws a [`Table`] on screen corresponding to the [`TextTable`].
pub fn draw_tui_table<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, data: &TextTableDataRef,
block: Block<'_>, block_area: Rect, show_selected_entry: bool,
block: BlockBuilder, block_area: Rect, show_selected_entry: bool,
show_scroll_position: bool,
) {
use tui::widgets::Row;
self.set_num_items(data.len());
let block = block
.extra_text(if show_scroll_position {
Some(self.scrollable.text_indicator())
} else {
None
})
.build(painter, block_area);
let inner_area = block.inner(block_area);
if inner_area.height < 1 {
f.render_widget(block, block_area);
@ -396,7 +407,6 @@ where
1
};
self.set_num_items(data.len());
self.set_border_bounds(block_area);
self.set_bounds(inner_area);
let table_extras = 1 + table_gap;

View File

@ -59,6 +59,7 @@ impl Widget for BasicCpu {
fn draw<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
_expanded: bool,
) {
const CONSTRAINTS: [Constraint; 2 * REQUIRED_COLUMNS - 1] = [
Constraint::Ratio(1, REQUIRED_COLUMNS as u32),

View File

@ -8,8 +8,7 @@ use tui::{
use crate::{
app::{
event::WidgetEventResult, widgets::tui_stuff::PipeGauge, Component, DataCollection,
Widget,
event::WidgetEventResult, widgets::tui_stuff::PipeGauge, Component, DataCollection, Widget,
},
canvas::Painter,
constants::SIDE_BORDERS,
@ -73,6 +72,7 @@ impl Widget for BasicMem {
fn draw<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
_expanded: bool,
) {
let block = Block::default()
.borders(*SIDE_BORDERS)

View File

@ -68,6 +68,7 @@ impl Widget for BasicNet {
fn draw<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
_expanded: bool,
) {
let block = Block::default()
.borders(*SIDE_BORDERS)

View File

@ -177,12 +177,14 @@ impl Widget for BatteryTable {
fn draw<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
expanded: bool,
) {
let block = self
.block()
.selected(selected)
.borders(self.block_border)
.build(painter);
.expanded(expanded)
.build(painter, area);
let inner_area = block.inner(area);
const CONSTRAINTS: [Constraint; 2] = [Constraint::Length(1), Constraint::Min(0)];

View File

@ -4,7 +4,6 @@ use crossterm::event::{KeyEvent, MouseEvent};
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
widgets::{Block, Borders},
Frame,
};
@ -164,6 +163,7 @@ impl Widget for CpuGraph {
fn draw<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
expanded: bool,
) {
let constraints = {
const CPU_LEGEND_MIN_WIDTH: u16 = 10;
@ -230,7 +230,8 @@ impl Widget for CpuGraph {
let graph_block = self
.block()
.selected(selected && matches!(&self.selected, CpuGraphSelection::Graph))
.build(painter);
.expanded(expanded)
.build(painter, graph_block_area);
self.graph.draw_tui_chart(
painter,
@ -243,17 +244,11 @@ impl Widget for CpuGraph {
graph_block_area,
);
let legend_block = Block::default()
.border_style(if selected {
if let CpuGraphSelection::Legend = &self.selected {
painter.colours.highlighted_border_style
} else {
painter.colours.border_style
}
} else {
painter.colours.border_style
})
.borders(Borders::ALL);
let legend_block = self
.block()
.selected(selected && matches!(&self.selected, CpuGraphSelection::Legend))
.expanded(expanded)
.hide_title(true);
let legend_data = self
.display_data
@ -294,6 +289,7 @@ impl Widget for CpuGraph {
legend_block,
legend_block_area,
true,
false,
);
}

View File

@ -48,6 +48,7 @@ pub struct DiskTable {
width: LayoutRule,
height: LayoutRule,
block_border: Borders,
show_scroll_index: bool,
}
impl Default for DiskTable {
@ -69,6 +70,7 @@ impl Default for DiskTable {
width: LayoutRule::default(),
height: LayoutRule::default(),
block_border: Borders::ALL,
show_scroll_index: false,
}
}
}
@ -94,6 +96,12 @@ impl DiskTable {
self
}
/// Sets whether to show the scroll index.
pub fn show_scroll_index(mut self, show_scroll_index: bool) -> Self {
self.show_scroll_index = show_scroll_index;
self
}
}
impl Component for DiskTable {
@ -121,15 +129,23 @@ impl Widget for DiskTable {
fn draw<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
expanded: bool,
) {
let block = self
.block()
.selected(selected)
.borders(self.block_border)
.build(painter);
.expanded(expanded);
self.table
.draw_tui_table(painter, f, &self.display_data, block, area, selected);
self.table.draw_tui_table(
painter,
f,
&self.display_data,
block,
area,
selected,
self.show_scroll_index,
);
}
fn update_data(&mut self, data_collection: &DataCollection) {

View File

@ -87,9 +87,13 @@ impl Widget for MemGraph {
fn draw<B: Backend>(
&mut self, painter: &crate::canvas::Painter, f: &mut tui::Frame<'_, B>, area: Rect,
selected: bool,
selected: bool, expanded: bool,
) {
let block = self.block().selected(selected).build(painter);
let block = self
.block()
.selected(selected)
.expanded(expanded)
.build(painter, area);
let mut chart_data = Vec::with_capacity(2);
if let Some((label_percent, label_frac)) = &self.mem_labels {

View File

@ -3,14 +3,14 @@ use std::{borrow::Cow, collections::HashMap, time::Instant};
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
widgets::{Block, Borders},
Frame,
};
use crate::{
app::{
data_farmer::DataCollection, text_table::SimpleColumn, time_graph::TimeGraphData,
AppConfigFields, AxisScaling, Component, TextTable, TimeGraph, Widget,
widgets::tui_stuff::BlockBuilder, AppConfigFields, AxisScaling, Component, TextTable,
TimeGraph, Widget,
},
canvas::Painter,
data_conversion::convert_network_data_points,
@ -517,8 +517,13 @@ impl Widget for NetGraph {
fn draw<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
expanded: bool,
) {
let block = self.block().selected(selected).build(painter);
let block = self
.block()
.selected(selected)
.expanded(expanded)
.build(painter, area);
self.set_draw_cache();
@ -660,6 +665,7 @@ impl Widget for OldNetGraph {
fn draw<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
expanded: bool,
) {
const CONSTRAINTS: [Constraint; 2] = [Constraint::Min(0), Constraint::Length(4)];
@ -671,15 +677,10 @@ impl Widget for OldNetGraph {
let graph_area = split_area[0];
let table_area = split_area[1];
self.net_graph.draw(painter, f, graph_area, selected);
self.net_graph
.draw(painter, f, graph_area, selected, expanded);
let table_block = Block::default()
.border_style(if selected {
painter.colours.highlighted_border_style
} else {
painter.colours.border_style
})
.borders(Borders::ALL);
let table_block = BlockBuilder::new("").hide_title(true);
self.table.draw_tui_table(
painter,
@ -701,6 +702,7 @@ impl Widget for OldNetGraph {
table_block,
table_area,
selected,
false,
);
}

View File

@ -789,6 +789,8 @@ pub struct ProcessManager {
width: LayoutRule,
height: LayoutRule,
show_scroll_index: bool,
}
impl ProcessManager {
@ -825,6 +827,7 @@ impl ProcessManager {
block_border: Borders::ALL,
width: LayoutRule::default(),
height: LayoutRule::default(),
show_scroll_index: false,
};
manager.set_tree_mode(process_defaults.is_tree);
@ -856,6 +859,12 @@ impl ProcessManager {
self.in_tree_mode = in_tree_mode;
}
/// Sets whether to show the scroll index.
pub fn show_scroll_index(mut self, show_scroll_index: bool) -> Self {
self.show_scroll_index = show_scroll_index;
self
}
fn open_search(&mut self) -> WidgetEventResult {
if let ProcessManagerSelection::Search = self.selected {
WidgetEventResult::NoRedraw
@ -1147,6 +1156,7 @@ impl Widget for ProcessManager {
fn draw<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
expanded: bool,
) {
let area = if self.show_search {
const SEARCH_CONSTRAINTS: [Constraint; 2] = [Constraint::Min(0), Constraint::Length(4)];
@ -1209,17 +1219,10 @@ impl Widget for ProcessManager {
.constraints(SORT_CONSTRAINTS)
.split(area);
let sort_block = Block::default()
.border_style(if selected {
if let ProcessManagerSelection::Sort = self.selected {
painter.colours.highlighted_border_style
} else {
painter.colours.border_style
}
} else {
painter.colours.border_style
})
.borders(Borders::ALL);
let sort_block = self
.block()
.selected(selected && matches!(self.selected, ProcessManagerSelection::Sort))
.hide_title(true);
self.sort_menu.draw_sort_menu(
painter,
f,
@ -1237,7 +1240,7 @@ impl Widget for ProcessManager {
.block()
.selected(selected && matches!(self.selected, ProcessManagerSelection::Processes))
.borders(self.block_border)
.build(painter);
.expanded(expanded && !self.show_sort && !self.show_search);
self.process_table.draw_tui_table(
painter,
@ -1246,6 +1249,7 @@ impl Widget for ProcessManager {
process_block,
area,
selected,
self.show_scroll_index,
);
}

View File

@ -56,6 +56,7 @@ pub struct TempTable {
width: LayoutRule,
height: LayoutRule,
block_border: Borders,
show_scroll_index: bool,
}
impl Default for TempTable {
@ -74,6 +75,7 @@ impl Default for TempTable {
width: LayoutRule::default(),
height: LayoutRule::default(),
block_border: Borders::ALL,
show_scroll_index: false,
}
}
}
@ -105,6 +107,12 @@ impl TempTable {
self
}
/// Sets whether to show the scroll index.
pub fn show_scroll_index(mut self, show_scroll_index: bool) -> Self {
self.show_scroll_index = show_scroll_index;
self
}
}
impl Component for TempTable {
@ -132,15 +140,23 @@ impl Widget for TempTable {
fn draw<B: Backend>(
&mut self, painter: &Painter, f: &mut Frame<'_, B>, area: Rect, selected: bool,
expanded: bool,
) {
let block = self
.block()
.selected(selected)
.borders(self.block_border)
.build(painter);
.expanded(expanded);
self.table
.draw_tui_table(painter, f, &self.display_data, block, area, selected);
self.table.draw_tui_table(
painter,
f,
&self.display_data,
block,
area,
selected,
self.show_scroll_index,
);
}
fn update_data(&mut self, data_collection: &DataCollection) {

View File

@ -1,4 +1,5 @@
use tui::{
layout::Rect,
text::Span,
widgets::{Block, Borders},
};
@ -11,6 +12,7 @@ pub struct BlockBuilder {
selected: bool,
expanded: bool,
name: &'static str,
hide_title: bool,
extra_text: Option<String>,
}
@ -22,6 +24,7 @@ impl BlockBuilder {
selected: false,
expanded: false,
name,
hide_title: false,
extra_text: None,
}
}
@ -39,8 +42,8 @@ impl BlockBuilder {
}
/// Indicates that this block has some extra text beyond the name.
pub fn extra_text(mut self, extra_text: String) -> Self {
self.extra_text = Some(extra_text);
pub fn extra_text(mut self, extra_text: Option<String>) -> Self {
self.extra_text = extra_text;
self
}
@ -50,9 +53,16 @@ impl BlockBuilder {
self
}
pub fn build<'a>(self, painter: &'a Painter) -> Block<'a> {
let has_top_bottom_border =
self.borders.contains(Borders::TOP) || self.borders.contains(Borders::BOTTOM);
/// Forcibly hides the title of the built [`Block`].
pub fn hide_title(mut self, hide_title: bool) -> Self {
self.hide_title = hide_title;
self
}
/// Converts the [`BlockBuilder`] into an actual [`Block`].
pub fn build(self, painter: &Painter, area: Rect) -> Block<'static> {
let has_title = !self.hide_title
&& (self.borders.contains(Borders::TOP) || self.borders.contains(Borders::BOTTOM));
let block = Block::default()
.border_style(if self.selected {
@ -62,11 +72,54 @@ impl BlockBuilder {
})
.borders(self.borders);
if has_top_bottom_border {
block.title(Span::styled(
let inner_width = block.inner(area).width as usize;
if has_title {
let name = Span::styled(
format!(" {} ", self.name),
painter.colours.widget_title_style,
))
);
let mut title_len = name.width();
let mut title = vec![name, Span::from(""), Span::from(""), Span::from("")];
if self.expanded {
const EXPAND_TEXT: &str = " Esc to go back ";
const EXPAND_TEXT_LEN: usize = EXPAND_TEXT.len();
let expand_span =
Span::styled(EXPAND_TEXT, painter.colours.highlighted_border_style);
if title_len + EXPAND_TEXT_LEN <= inner_width {
title_len += EXPAND_TEXT_LEN;
title[3] = expand_span;
}
}
if let Some(extra_text) = self.extra_text {
let extra_span = Span::styled(
format!("{} ", extra_text),
painter.colours.widget_title_style,
);
let width = extra_span.width();
if title_len + width <= inner_width {
title_len += width;
title[1] = extra_span;
}
}
if self.expanded {
let difference = inner_width.saturating_sub(title_len);
title[2] = Span::styled(
"".repeat(difference),
if self.selected {
painter.colours.highlighted_border_style
} else {
painter.colours.border_style
},
);
}
block.title(title)
} else {
block
}

View File

@ -336,7 +336,7 @@ impl Painter {
.get_mut(&app_state.selected_widget)
{
current_widget.set_bounds(draw_area);
current_widget.draw(self, f, draw_area, true);
current_widget.draw(self, f, draw_area, true, true);
}
} else {
/// A simple traversal through the `arena`, drawing all leaf elements.
@ -398,12 +398,13 @@ impl Painter {
f,
remaining_area,
selected_id == to_draw_node,
false,
);
}
}
} else {
widget.set_bounds(area);
widget.draw(painter, f, area, selected_id == node);
widget.draw(painter, f, area, selected_id == node, false);
}
}
}