From 3b09b9b8733f389e1ea13c9dcfb1577fad501abd Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Sat, 3 Jul 2021 22:20:12 -0400 Subject: [PATCH] Revert "refactor: Create a prototype time graph element" This reverts commit bd95ca8b73623c9df1dab03e0c51b31cb9b99c4d. --- src/app.rs | 16 +- src/app/layout_manager.rs | 20 +- src/app/widget_states/graph_state.rs | 1 + src/app/widget_states/mod.rs | 4 +- src/app/widget_states/table_state.rs | 1 + src/bin/main.rs | 78 +++--- src/{canvas/mod.rs => canvas.rs} | 34 +-- src/canvas/dialogs/dd_dialog.rs | 10 +- src/canvas/elements/element.rs | 53 ---- src/canvas/elements/node.rs | 34 --- src/canvas/elements/scroll_sort_table.rs | 35 --- src/canvas/elements/scrollable_table.rs | 20 -- src/canvas/elements/time_graph.rs | 255 ------------------ src/canvas/{elements/mod.rs => widgets.rs} | 19 +- .../basic_table_arrows.rs} | 8 +- .../{elements => widgets}/battery_display.rs | 13 +- src/canvas/{elements => widgets}/cpu_basic.rs | 6 +- src/canvas/{elements => widgets}/cpu_graph.rs | 4 +- .../{elements => widgets}/disk_table.rs | 4 +- src/canvas/{elements => widgets}/mem_basic.rs | 4 +- src/canvas/{elements => widgets}/mem_graph.rs | 0 .../{elements => widgets}/network_basic.rs | 8 +- .../{elements => widgets}/network_graph.rs | 4 +- .../{elements => widgets}/process_table.rs | 12 +- .../{elements => widgets}/temp_table.rs | 4 +- src/constants.rs | 3 +- src/lib.rs | 29 +- 27 files changed, 132 insertions(+), 547 deletions(-) create mode 100644 src/app/widget_states/graph_state.rs create mode 100644 src/app/widget_states/table_state.rs rename src/{canvas/mod.rs => canvas.rs} (97%) delete mode 100644 src/canvas/elements/element.rs delete mode 100644 src/canvas/elements/node.rs delete mode 100644 src/canvas/elements/scroll_sort_table.rs delete mode 100644 src/canvas/elements/scrollable_table.rs delete mode 100644 src/canvas/elements/time_graph.rs rename src/canvas/{elements/mod.rs => widgets.rs} (63%) rename src/canvas/{elements/element_carousel.rs => widgets/basic_table_arrows.rs} (97%) rename src/canvas/{elements => widgets}/battery_display.rs (94%) rename src/canvas/{elements => widgets}/cpu_basic.rs (98%) rename src/canvas/{elements => widgets}/cpu_graph.rs (99%) rename src/canvas/{elements => widgets}/disk_table.rs (98%) rename src/canvas/{elements => widgets}/mem_basic.rs (97%) rename src/canvas/{elements => widgets}/mem_graph.rs (100%) rename src/canvas/{elements => widgets}/network_basic.rs (93%) rename src/canvas/{elements => widgets}/network_graph.rs (99%) rename src/canvas/{elements => widgets}/process_table.rs (98%) rename src/canvas/{elements => widgets}/temp_table.rs (98%) diff --git a/src/app.rs b/src/app.rs index df2a85d68..0ecb15e4b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -68,10 +68,10 @@ pub struct AppConfigFields { #[derive(TypedBuilder)] pub struct AppState { #[builder(default = false, setter(skip))] - awaiting_second_char: bool, // TODO: Move out to input + awaiting_second_char: bool, #[builder(default, setter(skip))] - second_char: Option, // TODO: Move out to input + second_char: Option, #[builder(default, setter(skip))] pub dd_err: Option, @@ -83,7 +83,7 @@ pub struct AppState { pub is_frozen: bool, #[builder(default = Instant::now(), setter(skip))] - last_key_press: Instant, // TODO: Move out to input + last_key_press: Instant, #[builder(default, setter(skip))] pub canvas_data: canvas::DisplayableData, @@ -129,11 +129,11 @@ pub struct AppState { } #[cfg(target_os = "windows")] -const MAX_KILL_SIGNAL: usize = 1; +const MAX_SIGNAL: usize = 1; #[cfg(target_os = "linux")] -const MAX_KILL_SIGNAL: usize = 64; +const MAX_SIGNAL: usize = 64; #[cfg(target_os = "macos")] -const MAX_KILL_SIGNAL: usize = 31; +const MAX_SIGNAL: usize = 31; impl AppState { pub fn reset(&mut self) { @@ -967,7 +967,7 @@ impl AppState { if self.delete_dialog_state.is_showing_dd { let mut new_signal = match self.delete_dialog_state.selected_signal { KillSignal::Cancel => 8, - KillSignal::Kill(signal) => min(signal + 8, MAX_KILL_SIGNAL), + KillSignal::Kill(signal) => min(signal + 8, MAX_SIGNAL), }; if new_signal > 31 && new_signal < 42 { new_signal += 2; @@ -2133,7 +2133,7 @@ impl AppState { .max_scroll_index .saturating_sub(1); } else if self.delete_dialog_state.is_showing_dd { - self.delete_dialog_state.selected_signal = KillSignal::Kill(MAX_KILL_SIGNAL); + self.delete_dialog_state.selected_signal = KillSignal::Kill(MAX_SIGNAL); } } diff --git a/src/app/layout_manager.rs b/src/app/layout_manager.rs index ef6837037..d4b9272d1 100644 --- a/src/app/layout_manager.rs +++ b/src/app/layout_manager.rs @@ -947,7 +947,25 @@ impl std::str::FromStr for BottomWidgetType { "empty" => Ok(BottomWidgetType::Empty), "battery" | "batt" => Ok(BottomWidgetType::Battery), _ => Err(BottomError::ConfigError(format!( - "\"{}\" is an invalid widget name.", + "\"{}\" is an invalid widget name. + +Supported widget names: ++--------------------------+ +| cpu | ++--------------------------+ +| mem, memory | ++--------------------------+ +| net, network | ++--------------------------+ +| proc, process, processes | ++--------------------------+ +| temp, temperature | ++--------------------------+ +| disk | ++--------------------------+ +| batt, battery | ++--------------------------+ + ", s ))), } diff --git a/src/app/widget_states/graph_state.rs b/src/app/widget_states/graph_state.rs new file mode 100644 index 000000000..6f3b55d48 --- /dev/null +++ b/src/app/widget_states/graph_state.rs @@ -0,0 +1 @@ +//! States for a graph widget. diff --git a/src/app/widget_states/mod.rs b/src/app/widget_states/mod.rs index 2493da6e3..dc6c02e74 100644 --- a/src/app/widget_states/mod.rs +++ b/src/app/widget_states/mod.rs @@ -46,7 +46,7 @@ pub enum CursorDirection { } /// AppScrollWidgetState deals with fields for a scrollable app's current state. -#[derive(Debug, Default)] +#[derive(Default)] pub struct AppScrollWidgetState { pub current_scroll_position: usize, pub previous_scroll_position: usize, @@ -99,7 +99,7 @@ impl Default for AppHelpDialogState { } /// Meant for canvas operations involving table column widths. -#[derive(Debug, Default)] +#[derive(Default)] pub struct CanvasTableWidthState { pub desired_column_widths: Vec, pub calculated_column_widths: Vec, diff --git a/src/app/widget_states/table_state.rs b/src/app/widget_states/table_state.rs new file mode 100644 index 000000000..2e57e8f13 --- /dev/null +++ b/src/app/widget_states/table_state.rs @@ -0,0 +1 @@ +//! States for a table widget. diff --git a/src/bin/main.rs b/src/bin/main.rs index 4afea1739..48a757a03 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -4,7 +4,7 @@ #[macro_use] extern crate log; -use bottom::{canvas, data_conversion::*, options::*, *}; +use bottom::{canvas, constants::*, data_conversion::*, options::*, *}; use std::{ boxed::Box, @@ -61,30 +61,6 @@ fn main() -> Result<()> { get_color_scheme(&matches, &config)?, )?; - // Set up up tui and crossterm - let mut stdout_val = stdout(); - execute!(stdout_val, EnterAlternateScreen, EnableMouseCapture)?; - enable_raw_mode()?; - - let mut terminal = Terminal::new(CrosstermBackend::new(stdout_val))?; - terminal.clear()?; - terminal.hide_cursor()?; - - // Set panic hook - panic::set_hook(Box::new(|info| panic_hook(info))); - - // Set termination hook - let is_terminated = Arc::new(AtomicBool::new(false)); - { - let is_terminated = is_terminated.clone(); - ctrlc::set_handler(move || { - is_terminated.store(true, Ordering::SeqCst); - })?; - } - let mut first_pass = true; - - // ===== Start of actual thread creation and loop ===== - // Create termination mutex and cvar #[allow(clippy::mutex_atomic)] let thread_termination_lock = Arc::new(Mutex::new(false)); @@ -131,34 +107,46 @@ fn main() -> Result<()> { app.used_widgets.clone(), ); + // Set up up tui and crossterm + let mut stdout_val = stdout(); + execute!(stdout_val, EnterAlternateScreen, EnableMouseCapture)?; + enable_raw_mode()?; + + let mut terminal = Terminal::new(CrosstermBackend::new(stdout_val))?; + terminal.clear()?; + terminal.hide_cursor()?; + + // Set panic hook + panic::set_hook(Box::new(|info| panic_hook(info))); + + // Set termination hook + let is_terminated = Arc::new(AtomicBool::new(false)); + let ist_clone = is_terminated.clone(); + ctrlc::set_handler(move || { + ist_clone.store(true, Ordering::SeqCst); + })?; + let mut first_run = true; + while !is_terminated.load(Ordering::SeqCst) { - if let Ok(recv) = receiver.recv() { + if let Ok(recv) = receiver.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) { match recv { BottomEvent::KeyInput(event) => { if handle_key_event_or_break(event, &mut app, &collection_thread_ctrl_sender) { break; } handle_force_redraws(&mut app); - - if try_drawing(&mut terminal, &mut app, &mut painter).is_err() { - break; - } } BottomEvent::MouseInput(event) => { handle_mouse_event(event, &mut app); handle_force_redraws(&mut app); - - if try_drawing(&mut terminal, &mut app, &mut painter).is_err() { - break; - } } BottomEvent::Update(data) => { app.data_collection.eat_data(data); // This thing is required as otherwise, some widgets can't draw correctly w/o // some data (or they need to be re-drawn). - if first_pass { - first_pass = false; + if first_run { + first_run = false; app.is_force_redraw = true; } @@ -233,29 +221,25 @@ fn main() -> Result<()> { convert_battery_harvest(&app.data_collection); } } - - if try_drawing(&mut terminal, &mut app, &mut painter).is_err() { - break; - } } BottomEvent::Clean => { app.data_collection .clean_data(constants::STALE_MAX_MILLISECONDS); } - BottomEvent::RequestRedraw => { - if try_drawing(&mut terminal, &mut app, &mut painter).is_err() { - break; - } - } } } + + // TODO: [OPT] Should not draw if no change (ie: scroll max) + try_drawing(&mut terminal, &mut app, &mut painter)?; } + // I think doing it in this order is safe... + *thread_termination_lock.lock().unwrap() = true; + thread_termination_cvar.notify_all(); - cleanup_terminal(&mut terminal)?; - // ===== End of actual thread creation and loop ===== + cleanup_terminal(&mut terminal)?; Ok(()) } diff --git a/src/canvas/mod.rs b/src/canvas.rs similarity index 97% rename from src/canvas/mod.rs rename to src/canvas.rs index 9c145104e..d96b78d95 100644 --- a/src/canvas/mod.rs +++ b/src/canvas.rs @@ -3,15 +3,17 @@ use std::{collections::HashMap, str::FromStr}; use tui::{ backend::Backend, - layout::{Constraint, Direction, Layout as tuiLayout, Rect}, + layout::{Constraint, Direction, Layout, Rect}, text::{Span, Spans}, widgets::Paragraph, Frame, Terminal, }; +// use ordered_float::OrderedFloat; + use canvas_colours::*; use dialogs::*; -use elements::*; +use widgets::*; use crate::{ app::{ @@ -30,7 +32,7 @@ use crate::{ mod canvas_colours; mod dialogs; mod drawing_utils; -mod elements; +mod widgets; /// Point is of time, data type Point = (f64, f64); @@ -287,7 +289,7 @@ impl Painter { "Frozen, press 'f' to unfreeze", self.colours.currently_selected_text_style, )), - tuiLayout::default() + Layout::default() .horizontal_margin(1) .constraints([Constraint::Length(1)]) .split(draw_loc)[0], @@ -301,7 +303,7 @@ impl Painter { terminal.draw(|mut f| { let (terminal_size, frozen_draw_loc) = if app_state.is_frozen { - let split_loc = tuiLayout::default() + let split_loc = Layout::default() .constraints([Constraint::Min(0), Constraint::Length(1)]) .split(f.size()); (split_loc[0], Some(split_loc[1])) @@ -344,7 +346,7 @@ impl Painter { if app_state.help_dialog_state.is_showing_help { let gen_help_len = GENERAL_HELP_TEXT.len() as u16 + 3; let border_len = terminal_height.saturating_sub(gen_help_len) / 2; - let vertical_dialog_chunk = tuiLayout::default() + let vertical_dialog_chunk = Layout::default() .direction(Direction::Vertical) .constraints([ Constraint::Length(border_len), @@ -353,7 +355,7 @@ impl Painter { ]) .split(terminal_size); - let middle_dialog_chunk = tuiLayout::default() + let middle_dialog_chunk = Layout::default() .direction(Direction::Horizontal) .constraints(if terminal_width < 100 { // TODO: [REFACTOR] The point we start changing size at currently hard-coded in. @@ -427,7 +429,7 @@ impl Painter { // }; let vertical_bordering = terminal_height.saturating_sub(text_height) / 2; - let vertical_dialog_chunk = tuiLayout::default() + let vertical_dialog_chunk = Layout::default() .direction(Direction::Vertical) .constraints([ Constraint::Length(vertical_bordering), @@ -437,7 +439,7 @@ impl Painter { .split(terminal_size); let horizontal_bordering = terminal_width.saturating_sub(text_width) / 2; - let middle_dialog_chunk = tuiLayout::default() + let middle_dialog_chunk = Layout::default() .direction(Direction::Horizontal) .constraints([ Constraint::Length(horizontal_bordering), @@ -454,7 +456,7 @@ impl Painter { self.draw_frozen_indicator(&mut f, frozen_draw_loc); } - let rect = tuiLayout::default() + let rect = Layout::default() .margin(0) .constraints([Constraint::Percentage(100)]) .split(terminal_size); @@ -538,7 +540,7 @@ impl Painter { } }; - let vertical_chunks = tuiLayout::default() + let vertical_chunks = Layout::default() .direction(Direction::Vertical) .margin(0) .constraints([ @@ -549,7 +551,7 @@ impl Painter { ]) .split(terminal_size); - let middle_chunks = tuiLayout::default() + let middle_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) .split(vertical_chunks[1]); @@ -612,7 +614,7 @@ impl Painter { } if self.derived_widget_draw_locs.is_empty() || app_state.is_force_redraw { - let draw_locs = tuiLayout::default() + let draw_locs = Layout::default() .margin(0) .constraints(self.row_constraints.as_ref()) .direction(Direction::Vertical) @@ -634,7 +636,7 @@ impl Painter { cols, )| { izip!( - tuiLayout::default() + Layout::default() .constraints(col_constraint.as_ref()) .direction(Direction::Horizontal) .split(draw_loc) @@ -645,7 +647,7 @@ impl Painter { ) .map(|(split_loc, constraint, col_constraint_vec, col_rows)| { izip!( - tuiLayout::default() + Layout::default() .constraints(constraint.as_ref()) .direction(Direction::Vertical) .split(split_loc) @@ -655,7 +657,7 @@ impl Painter { ) .map(|(draw_loc, col_row_constraint_vec, widgets)| { // Note that col_row_constraint_vec CONTAINS the widget constraints - let widget_draw_locs = tuiLayout::default() + let widget_draw_locs = Layout::default() .constraints(col_row_constraint_vec.as_ref()) .direction(Direction::Horizontal) .split(draw_loc); diff --git a/src/canvas/dialogs/dd_dialog.rs b/src/canvas/dialogs/dd_dialog.rs index f65390edb..8a187e8fd 100644 --- a/src/canvas/dialogs/dd_dialog.rs +++ b/src/canvas/dialogs/dd_dialog.rs @@ -2,7 +2,7 @@ use std::cmp::min; use tui::{ backend::Backend, - layout::{Alignment, Constraint, Direction, Layout as tuiLayout, Rect}, + layout::{Alignment, Constraint, Direction, Layout, Rect}, terminal::Frame, text::{Span, Spans, Text}, widgets::{Block, Borders, Paragraph, Wrap}, @@ -83,7 +83,7 @@ impl KillDialog for Painter { ), }; - let button_layout = tuiLayout::default() + let button_layout = Layout::default() .direction(Direction::Horizontal) .constraints( [ @@ -247,7 +247,7 @@ impl KillDialog for Painter { ]; } - let button_rect = tuiLayout::default() + let button_rect = Layout::default() .direction(Direction::Horizontal) .margin(1) .constraints( @@ -269,7 +269,7 @@ impl KillDialog for Painter { selected -= 2; } - let layout = tuiLayout::default() + let layout = Layout::default() .direction(Direction::Vertical) .constraints(vec![Constraint::Min(1); button_rect.height as usize]) .split(button_rect); @@ -376,7 +376,7 @@ impl KillDialog for Painter { }; // Now draw buttons if needed... - let split_draw_loc = tuiLayout::default() + let split_draw_loc = Layout::default() .direction(Direction::Vertical) .constraints( if app_state.dd_err.is_some() { diff --git a/src/canvas/elements/element.rs b/src/canvas/elements/element.rs deleted file mode 100644 index b684698c6..000000000 --- a/src/canvas/elements/element.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![allow(dead_code)] - -use tui::{backend::Backend, layout::Rect, Frame}; - -use crate::canvas::canvas_colours::CanvasColours; - -use super::{Layout, Node}; - -/// A single point. -#[derive(Copy, Clone)] -pub struct Point { - pub x: u16, - pub y: u16, -} - -/// The top-left and bottom-right corners of a [`Element`]. -#[derive(Copy, Clone)] -pub enum ElementBounds { - Unset, - Points { - top_left_corner: Point, - bottom_right_corner: Point, - }, -} - -/// A basic [`Element`] trait, all drawn components must implement this. -pub trait Element { - /// The type of data that is expected for the [`Element`]. - - /// The main drawing function. - fn draw( - &mut self, f: &mut Frame<'_, B>, layout: Layout<'_>, style: &CanvasColours, - ) -> anyhow::Result<()>; - - /// Determines the layout. - fn layout(&mut self, bounds: Rect) -> Node; - - /// Returns the name of the [`Element`], if it exists. Default implementation returns `None`. - fn name(&self) -> Option<&str> { - None - } - - /// Returns whether an [`Element`] is selected. Default implementation - fn is_selected(&self) -> bool { - false - } - - /// Marks an [`Element`] as selected. Default implementation does nothing. - fn select(&mut self) {} - - /// Marks an [`Element`] as unselected. Default implementation does nothing. - fn unselect(&mut self) {} -} diff --git a/src/canvas/elements/node.rs b/src/canvas/elements/node.rs deleted file mode 100644 index 25d13feab..000000000 --- a/src/canvas/elements/node.rs +++ /dev/null @@ -1,34 +0,0 @@ -use tui::layout::Rect; - -pub struct Node { - bounds: Rect, - children: Vec, -} - -impl Node { - pub fn new(bounds: Rect, children: Vec) -> Node { - Self { bounds, children } - } - - pub fn bounds(&self) -> Rect { - self.bounds - } - - pub fn children(&self) -> &[Node] { - &self.children - } -} - -pub struct Layout<'a> { - node: &'a Node, -} - -impl<'a> Layout<'a> { - pub fn children(&self) -> impl Iterator> { - self.node.children().iter().map(move |node| Layout { node }) - } - - pub fn bounds(&self) -> Rect { - self.node.bounds() - } -} diff --git a/src/canvas/elements/scroll_sort_table.rs b/src/canvas/elements/scroll_sort_table.rs deleted file mode 100644 index 549bde458..000000000 --- a/src/canvas/elements/scroll_sort_table.rs +++ /dev/null @@ -1,35 +0,0 @@ -#![allow(dead_code)] - -//! Code for a generic table element with scroll and sort support. - -use crate::app::{AppScrollWidgetState, CanvasTableWidthState}; - -use super::element::ElementBounds; - -#[derive(Debug, Default)] -pub struct State { - scroll: AppScrollWidgetState, - width: CanvasTableWidthState, -} - -/// A [`ScrollSortTable`] is a stateful generic table element with scroll and sort support. -pub struct ScrollSortTable { - state: State, - bounds: ElementBounds, -} - -impl ScrollSortTable { - /// Function for incrementing the scroll. - fn increment_scroll(&mut self) {} - - /// Function for decrementing the scroll. - fn decrement_scroll(&mut self) {} - - pub fn on_down(&mut self) { - self.increment_scroll(); - } - - pub fn on_up(&mut self) { - self.decrement_scroll(); - } -} diff --git a/src/canvas/elements/scrollable_table.rs b/src/canvas/elements/scrollable_table.rs deleted file mode 100644 index 56c967b73..000000000 --- a/src/canvas/elements/scrollable_table.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![allow(dead_code)] -#![allow(unused_variables)] - -use tui::widgets::TableState; - -use super::element::{Element, ElementBounds}; - -/// The state for a [`ScrollableTable`]. -struct ScrollableTableState { - tui_state: TableState, -} - -/// A [`ScrollableTable`] is a stateful table [`Element`] with scrolling support. -pub struct ScrollableTable { - bounds: ElementBounds, - selected: bool, - state: ScrollableTableState, -} - -impl ScrollableTable {} diff --git a/src/canvas/elements/time_graph.rs b/src/canvas/elements/time_graph.rs deleted file mode 100644 index f5db11e05..000000000 --- a/src/canvas/elements/time_graph.rs +++ /dev/null @@ -1,255 +0,0 @@ -#![allow(dead_code)] - -use std::{borrow::Cow, time::Instant}; - -use tui::{ - backend::Backend, - layout::{Constraint, Rect}, - style::Style, - symbols::Marker, - text::{Span, Spans}, - widgets::{Axis, Block, Borders, Chart, Dataset}, - Frame, -}; -use unicode_segmentation::UnicodeSegmentation; - -use crate::{ - app::AppState, - canvas::{canvas_colours::CanvasColours, drawing_utils::interpolate_points}, - constants::{AUTOHIDE_TIMEOUT_MILLISECONDS, TIME_LABEL_HEIGHT_LIMIT}, -}; - -use super::{ - element::{Element, ElementBounds}, - Layout, Node, -}; - -/// Struct representing the state of a [`TimeGraph`]. -#[derive(Default)] -struct TimeGraphState { - pub current_max_time_ms: u32, - pub autohide_timer: Option, -} - -/// A stateful graph widget graphing between a time x-axis and some y-axis, supporting time zooming. -pub struct TimeGraph<'d> { - state: TimeGraphState, - bounds: ElementBounds, - name: Cow<'static, str>, - legend_constraints: (Constraint, Constraint), - selected: bool, - data: &'d [(Cow<'static, str>, Style, Vec<(f64, f64)>)], - y_axis_legend: Axis<'d>, - marker: Marker, -} - -impl<'d> TimeGraph<'d> { - /// Creates a new [`TimeGraph`]. - pub fn new( - data: &'d [(Cow<'static, str>, Style, Vec<(f64, f64)>)], legend_bounds: &[f64; 2], - labels: Vec>, - ) -> Self { - Self { - state: TimeGraphState::default(), - bounds: ElementBounds::Unset, - name: Cow::default(), - legend_constraints: (Constraint::Ratio(1, 1), Constraint::Ratio(3, 4)), - selected: false, - data, - y_axis_legend: Axis::default().bounds(*legend_bounds).labels(labels), - marker: Marker::Braille, - } - } - - /// Sets the legend status for a [`TimeGraph`]. - pub fn enable_legend(mut self, enable_legend: bool) -> Self { - if enable_legend { - self.legend_constraints = (Constraint::Ratio(1, 1), Constraint::Ratio(3, 4)); - } else { - self.legend_constraints = (Constraint::Ratio(0, 1), Constraint::Ratio(0, 1)); - } - - self - } - - /// Sets the marker type for a [`TimeGraph`]. - pub fn marker(mut self, use_dot: bool) -> Self { - self.marker = if use_dot { - Marker::Dot - } else { - Marker::Braille - }; - - self - } -} - -impl<'d> Element for TimeGraph<'d> { - fn draw( - &mut self, f: &mut Frame<'_, B>, layout: Layout<'_>, style: &CanvasColours, - ) -> anyhow::Result<()> { - let time_start = -(f64::from(self.state.current_max_time_ms)); - let display_time_labels = vec![ - Span::styled( - format!("{}s", self.state.current_max_time_ms / 1000), - style.graph_style, - ), - Span::styled("0s".to_string(), style.graph_style), - ]; - - let x_axis = if app_state.app_config_fields.hide_time - || (app_state.app_config_fields.autohide_time && self.state.autohide_timer.is_none()) - { - Axis::default().bounds([time_start, 0.0]) - } else if let Some(time) = self.state.autohide_timer { - if std::time::Instant::now().duration_since(time).as_millis() - < AUTOHIDE_TIMEOUT_MILLISECONDS as u128 - { - Axis::default() - .bounds([time_start, 0.0]) - .style(style.graph_style) - .labels(display_time_labels) - } else { - self.state.autohide_timer = None; - Axis::default().bounds([time_start, 0.0]) - } - } else if draw_loc.height < TIME_LABEL_HEIGHT_LIMIT { - Axis::default().bounds([time_start, 0.0]) - } else { - Axis::default() - .bounds([time_start, 0.0]) - .style(style.graph_style) - .labels(display_time_labels) - }; - - let y_axis = self.y_axis_legend.clone(); // Not sure how else to do this right now. - let border_style = if self.selected { - style.highlighted_border_style - } else { - style.border_style - }; - - let title = if app_state.is_expanded { - Spans::from(vec![ - Span::styled(format!(" {} ", self.name), style.widget_title_style), - Span::styled( - format!( - "─{}─ Esc to go back ", - "─".repeat( - usize::from(draw_loc.width).saturating_sub( - UnicodeSegmentation::graphemes( - format!(" {} ── Esc to go back", self.name).as_str(), - true - ) - .count() - + 2 - ) - ) - ), - border_style, - ), - ]) - } else { - Spans::from(Span::styled( - format!(" {} ", self.name), - style.widget_title_style, - )) - }; - - // We unfortunately must store the data at least once, otherwise we get issues with local referencing. - let processed_data = self - .data - .iter() - .map(|(name, style, dataset)| { - // Match time + interpolate; we assume all the datasets are sorted. - - if let Some(end_pos) = dataset.iter().position(|(time, _data)| *time >= time_start) - { - if end_pos > 0 { - // We can interpolate. - - let old = dataset[end_pos - 1]; - let new = dataset[end_pos]; - - let interpolated_point = interpolate_points(&old, &new, time_start); - - ( - (name, *style, &dataset[end_pos..]), - Some([(time_start, interpolated_point), new]), - ) - } else { - ((name, *style, &dataset[end_pos..]), None) - } - } else { - // No need to interpolate, just return the entire thing. - ((name, *style, &dataset[..]), None) - } - }) - .collect::>(); - - let datasets = processed_data - .iter() - .map(|((name, style, cut_data), interpolated_data)| { - if let Some(interpolated_data) = interpolated_data { - vec![ - Dataset::default() - .data(cut_data) - .style(*style) - .name(name.as_ref()) - .marker(self.marker) - .graph_type(tui::widgets::GraphType::Line), - Dataset::default() - .data(interpolated_data.as_ref()) - .style(*style) - .marker(self.marker) - .graph_type(tui::widgets::GraphType::Line), - ] - } else { - vec![Dataset::default() - .data(cut_data) - .style(*style) - .name(name.as_ref()) - .marker(self.marker) - .graph_type(tui::widgets::GraphType::Line)] - } - }) - .flatten() - .collect(); - - f.render_widget( - Chart::new(datasets) - .block( - Block::default() - .title(title) - .borders(Borders::ALL) - .border_style(if self.selected { - style.highlighted_border_style - } else { - style.border_style - }), - ) - .x_axis(x_axis) - .y_axis(y_axis) - .hidden_legend_constraints(self.legend_constraints), - draw_loc, - ); - - Ok(()) - } - - fn layout(&mut self, bounds: Rect) -> Node { - Node::new(bounds, vec![]) - } - - fn is_selected(&self) -> bool { - self.selected - } - - fn select(&mut self) { - self.selected = true; - } - - fn unselect(&mut self) { - self.selected = false; - } -} diff --git a/src/canvas/elements/mod.rs b/src/canvas/widgets.rs similarity index 63% rename from src/canvas/elements/mod.rs rename to src/canvas/widgets.rs index 9d1a85954..a76b45912 100644 --- a/src/canvas/elements/mod.rs +++ b/src/canvas/widgets.rs @@ -1,8 +1,8 @@ +pub mod basic_table_arrows; pub mod battery_display; pub mod cpu_basic; pub mod cpu_graph; pub mod disk_table; -pub mod element_carousel; pub mod mem_basic; pub mod mem_graph; pub mod network_basic; @@ -10,29 +10,14 @@ pub mod network_graph; pub mod process_table; pub mod temp_table; +pub use basic_table_arrows::BasicTableArrows; pub use battery_display::BatteryDisplayWidget; pub use cpu_basic::CpuBasicWidget; pub use cpu_graph::CpuGraphWidget; pub use disk_table::DiskTableWidget; -pub use element_carousel::ElementCarousel; pub use mem_basic::MemBasicWidget; pub use mem_graph::MemGraphWidget; pub use network_basic::NetworkBasicWidget; pub use network_graph::NetworkGraphWidget; pub use process_table::ProcessTableWidget; pub use temp_table::TempTableWidget; - -pub mod element; -pub use element::Element; - -pub mod scrollable_table; -pub use scrollable_table::ScrollableTable; - -pub mod scroll_sort_table; -pub use scroll_sort_table::ScrollSortTable; - -pub mod time_graph; -pub use time_graph::TimeGraph; - -pub mod node; -pub use node::*; diff --git a/src/canvas/elements/element_carousel.rs b/src/canvas/widgets/basic_table_arrows.rs similarity index 97% rename from src/canvas/elements/element_carousel.rs rename to src/canvas/widgets/basic_table_arrows.rs index 6b46f1467..82c232453 100644 --- a/src/canvas/elements/element_carousel.rs +++ b/src/canvas/widgets/basic_table_arrows.rs @@ -5,20 +5,20 @@ use crate::{ use tui::{ backend::Backend, - layout::{Alignment, Constraint, Direction, Layout as tuiLayout, Rect}, + layout::{Alignment, Constraint, Direction, Layout, Rect}, terminal::Frame, text::Span, text::Spans, widgets::{Block, Paragraph}, }; -pub trait ElementCarousel { +pub trait BasicTableArrows { fn draw_basic_table_arrows( &self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64, ); } -impl ElementCarousel for Painter { +impl BasicTableArrows for Painter { fn draw_basic_table_arrows( &self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64, ) { @@ -114,7 +114,7 @@ impl ElementCarousel for Painter { )), ]; - let margined_draw_loc = tuiLayout::default() + let margined_draw_loc = Layout::default() .direction(Direction::Horizontal) .constraints([ Constraint::Length(2 + left_name.len() as u16), diff --git a/src/canvas/elements/battery_display.rs b/src/canvas/widgets/battery_display.rs similarity index 94% rename from src/canvas/elements/battery_display.rs rename to src/canvas/widgets/battery_display.rs index 9acce6edf..34d9783cc 100644 --- a/src/canvas/elements/battery_display.rs +++ b/src/canvas/widgets/battery_display.rs @@ -6,7 +6,7 @@ use crate::{ use tui::{ backend::Backend, - layout::{Constraint, Direction, Layout as tuiLayout, Rect}, + layout::{Constraint, Direction, Layout, Rect}, terminal::Frame, text::{Span, Spans}, widgets::{Block, Borders, Cell, Paragraph, Row, Table, Tabs}, @@ -82,7 +82,7 @@ impl BatteryDisplayWidget for Painter { .map(|battery| &battery.battery_name) .collect::>(); - let tab_draw_loc = tuiLayout::default() + let tab_draw_loc = Layout::default() .constraints([ Constraint::Length(1), Constraint::Length(2), @@ -106,7 +106,7 @@ impl BatteryDisplayWidget for Painter { tab_draw_loc, ); - let margined_draw_loc = tuiLayout::default() + let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)]) .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) .direction(Direction::Horizontal) @@ -186,14 +186,13 @@ impl BatteryDisplayWidget for Painter { let mut tab_click_locs: Vec<((u16, u16), (u16, u16))> = vec![]; for battery in battery_names { // +1 because there's a space after the tab label. - let width = - unicode_width::UnicodeWidthStr::width(battery.as_str()) as u16 + 1; + let width = unicode_width::UnicodeWidthStr::width(battery.as_str()) as u16; tab_click_locs .push(((current_x, current_y), (current_x + width, current_y))); - // +2 because we want to go one space past to get to the '|', then one MORE + // +4 because we want to go one space, then one space past to get to the '|', then 2 more // to start at the blank space before the tab label. - current_x = current_x + width + 2; + current_x += width + 4; } battery_widget_state.tab_click_locs = Some(tab_click_locs); } diff --git a/src/canvas/elements/cpu_basic.rs b/src/canvas/widgets/cpu_basic.rs similarity index 98% rename from src/canvas/elements/cpu_basic.rs rename to src/canvas/widgets/cpu_basic.rs index 22220c92b..84f12f07a 100644 --- a/src/canvas/elements/cpu_basic.rs +++ b/src/canvas/widgets/cpu_basic.rs @@ -9,7 +9,7 @@ use crate::{ use tui::{ backend::Backend, - layout::{Constraint, Direction, Layout as tuiLayout, Rect}, + layout::{Constraint, Direction, Layout, Rect}, terminal::Frame, text::{Span, Spans}, widgets::{Block, Paragraph}, @@ -57,7 +57,7 @@ impl CpuBasicWidget for Painter { let chunk_vec = vec![Constraint::Percentage((100 / REQUIRED_COLUMNS) as u16); REQUIRED_COLUMNS]; - let chunks = tuiLayout::default() + let chunks = Layout::default() .constraints(chunk_vec) .direction(Direction::Horizontal) .split(draw_loc); @@ -179,7 +179,7 @@ impl CpuBasicWidget for Painter { start_index += how_many_cpus; - let margined_loc = tuiLayout::default() + let margined_loc = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(100)]) .horizontal_margin(1) diff --git a/src/canvas/elements/cpu_graph.rs b/src/canvas/widgets/cpu_graph.rs similarity index 99% rename from src/canvas/elements/cpu_graph.rs rename to src/canvas/widgets/cpu_graph.rs index e7bab838c..8e07ec28d 100644 --- a/src/canvas/elements/cpu_graph.rs +++ b/src/canvas/widgets/cpu_graph.rs @@ -13,7 +13,7 @@ use crate::{ use tui::{ backend::Backend, - layout::{Constraint, Direction, Layout as tuiLayout, Rect}, + layout::{Constraint, Direction, Layout, Rect}, symbols::Marker, terminal::Frame, text::Span, @@ -86,7 +86,7 @@ impl CpuGraphWidget for Painter { ) }; - let partitioned_draw_loc = tuiLayout::default() + let partitioned_draw_loc = Layout::default() .margin(0) .direction(Direction::Horizontal) .constraints(constraints) diff --git a/src/canvas/elements/disk_table.rs b/src/canvas/widgets/disk_table.rs similarity index 98% rename from src/canvas/elements/disk_table.rs rename to src/canvas/widgets/disk_table.rs index 8f891ed5b..6a79c5b69 100644 --- a/src/canvas/elements/disk_table.rs +++ b/src/canvas/widgets/disk_table.rs @@ -1,7 +1,7 @@ use once_cell::sync::Lazy; use tui::{ backend::Backend, - layout::{Constraint, Direction, Layout as tuiLayout, Rect}, + layout::{Constraint, Direction, Layout, Rect}, terminal::Frame, text::Span, text::{Spans, Text}, @@ -232,7 +232,7 @@ impl DiskTableWidget for Painter { Block::default().borders(Borders::NONE) }; - let margined_draw_loc = tuiLayout::default() + let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)]) .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) .direction(Direction::Horizontal) diff --git a/src/canvas/elements/mem_basic.rs b/src/canvas/widgets/mem_basic.rs similarity index 97% rename from src/canvas/elements/mem_basic.rs rename to src/canvas/widgets/mem_basic.rs index 9f273f810..afb3da537 100644 --- a/src/canvas/elements/mem_basic.rs +++ b/src/canvas/widgets/mem_basic.rs @@ -6,7 +6,7 @@ use crate::{ use tui::{ backend::Backend, - layout::{Constraint, Layout as tuiLayout, Rect}, + layout::{Constraint, Layout, Rect}, terminal::Frame, text::Span, text::Spans, @@ -26,7 +26,7 @@ impl MemBasicWidget for Painter { let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data; let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data; - let margined_loc = tuiLayout::default() + let margined_loc = Layout::default() .constraints([Constraint::Percentage(100)]) .horizontal_margin(1) .split(draw_loc); diff --git a/src/canvas/elements/mem_graph.rs b/src/canvas/widgets/mem_graph.rs similarity index 100% rename from src/canvas/elements/mem_graph.rs rename to src/canvas/widgets/mem_graph.rs diff --git a/src/canvas/elements/network_basic.rs b/src/canvas/widgets/network_basic.rs similarity index 93% rename from src/canvas/elements/network_basic.rs rename to src/canvas/widgets/network_basic.rs index 64e6be5da..42ebd73b5 100644 --- a/src/canvas/elements/network_basic.rs +++ b/src/canvas/widgets/network_basic.rs @@ -2,7 +2,7 @@ use crate::{app::AppState, canvas::Painter, constants::*}; use tui::{ backend::Backend, - layout::{Constraint, Direction, Layout as tuiLayout, Rect}, + layout::{Constraint, Direction, Layout, Rect}, terminal::Frame, text::{Span, Spans}, widgets::{Block, Paragraph}, @@ -18,18 +18,18 @@ impl NetworkBasicWidget for Painter { fn draw_basic_network( &self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64, ) { - let divided_loc = tuiLayout::default() + let divided_loc = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) .split(draw_loc); - let net_loc = tuiLayout::default() + let net_loc = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(100)]) .horizontal_margin(1) .split(divided_loc[0]); - let total_loc = tuiLayout::default() + let total_loc = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(100)]) .horizontal_margin(1) diff --git a/src/canvas/elements/network_graph.rs b/src/canvas/widgets/network_graph.rs similarity index 99% rename from src/canvas/elements/network_graph.rs rename to src/canvas/widgets/network_graph.rs index 52f78b6f2..be33264aa 100644 --- a/src/canvas/elements/network_graph.rs +++ b/src/canvas/widgets/network_graph.rs @@ -15,7 +15,7 @@ use crate::{ use tui::{ backend::Backend, - layout::{Constraint, Direction, Layout as tuiLayout, Rect}, + layout::{Constraint, Direction, Layout, Rect}, symbols::Marker, terminal::Frame, text::Span, @@ -52,7 +52,7 @@ impl NetworkGraphWidget for Painter { &self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64, ) { if app_state.app_config_fields.use_old_network_legend { - let network_chunk = tuiLayout::default() + let network_chunk = Layout::default() .direction(Direction::Vertical) .margin(0) .constraints([ diff --git a/src/canvas/elements/process_table.rs b/src/canvas/widgets/process_table.rs similarity index 98% rename from src/canvas/elements/process_table.rs rename to src/canvas/widgets/process_table.rs index 239d3519d..190a216e0 100644 --- a/src/canvas/elements/process_table.rs +++ b/src/canvas/widgets/process_table.rs @@ -9,7 +9,7 @@ use crate::{ use tui::{ backend::Backend, - layout::{Alignment, Constraint, Direction, Layout as tuiLayout, Rect}, + layout::{Alignment, Constraint, Direction, Layout, Rect}, terminal::Frame, text::{Span, Spans, Text}, widgets::{Block, Borders, Paragraph, Row, Table}, @@ -149,7 +149,7 @@ impl ProcessTableWidget for Painter { let mut proc_draw_loc = draw_loc; if process_widget_state.is_search_enabled() { - let processes_chunk = tuiLayout::default() + let processes_chunk = Layout::default() .direction(Direction::Vertical) .constraints([Constraint::Min(0), Constraint::Length(search_height)]) .split(draw_loc); @@ -165,7 +165,7 @@ impl ProcessTableWidget for Painter { } if is_sort_open { - let processes_chunk = tuiLayout::default() + let processes_chunk = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Length(header_len + 4), Constraint::Min(0)]) .split(proc_draw_loc); @@ -197,7 +197,7 @@ impl ProcessTableWidget for Painter { } let is_on_widget = widget_id == app_state.current_widget.widget_id; - let margined_draw_loc = tuiLayout::default() + let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)]) .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) .direction(Direction::Horizontal) @@ -758,7 +758,7 @@ impl ProcessTableWidget for Painter { Block::default().borders(Borders::NONE) }; - let margined_draw_loc = tuiLayout::default() + let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)]) .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) .direction(Direction::Horizontal) @@ -875,7 +875,7 @@ impl ProcessTableWidget for Painter { self.colours.text_style }; - let margined_draw_loc = tuiLayout::default() + let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)]) .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) .direction(Direction::Horizontal) diff --git a/src/canvas/elements/temp_table.rs b/src/canvas/widgets/temp_table.rs similarity index 98% rename from src/canvas/elements/temp_table.rs rename to src/canvas/widgets/temp_table.rs index 8c06a7a67..eaa9ea2a1 100644 --- a/src/canvas/elements/temp_table.rs +++ b/src/canvas/widgets/temp_table.rs @@ -1,7 +1,7 @@ use once_cell::sync::Lazy; use tui::{ backend::Backend, - layout::{Constraint, Direction, Layout as tuiLayout, Rect}, + layout::{Constraint, Direction, Layout, Rect}, terminal::Frame, text::Span, text::{Spans, Text}, @@ -221,7 +221,7 @@ impl TempTableWidget for Painter { Block::default().borders(Borders::NONE) }; - let margined_draw_loc = tuiLayout::default() + let margined_draw_loc = Layout::default() .constraints([Constraint::Percentage(100)]) .horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 }) .direction(Direction::Horizontal) diff --git a/src/constants.rs b/src/constants.rs index 3c76cfaf2..c810ccb9e 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -11,8 +11,9 @@ pub const STALE_MAX_MILLISECONDS: u64 = 600 * 1000; // Keep 10 minutes of data. pub const DEFAULT_TIME_MILLISECONDS: u64 = 60 * 1000; // Defaults to 1 min. pub const STALE_MIN_MILLISECONDS: u64 = 30 * 1000; // Lowest is 30 seconds pub const TIME_CHANGE_MILLISECONDS: u64 = 15 * 1000; // How much to increment each time -pub const AUTOHIDE_TIMEOUT_MILLISECONDS: u128 = 5000; // 5 seconds to autohide +pub const AUTOHIDE_TIMEOUT_MILLISECONDS: u64 = 5000; // 5 seconds to autohide +pub const TICK_RATE_IN_MILLISECONDS: u64 = 200; // How fast the screen refreshes pub const DEFAULT_REFRESH_RATE_IN_MILLISECONDS: u64 = 1000; pub const MAX_KEY_TIMEOUT_IN_MILLISECONDS: u64 = 1000; diff --git a/src/lib.rs b/src/lib.rs index cb1dd129b..c0e59ce7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,7 +61,6 @@ pub enum BottomEvent { MouseInput(J), Update(Box), Clean, - RequestRedraw, } #[derive(Debug)] @@ -611,27 +610,19 @@ pub fn create_input_thread( if let Ok(poll) = poll(Duration::from_millis(20)) { if poll { if let Ok(event) = read() { - match event { - Event::Key(key) => { - if Instant::now().duration_since(keyboard_timer).as_millis() >= 20 { - if sender.send(BottomEvent::KeyInput(key)).is_err() { - break; - } - keyboard_timer = Instant::now(); + if let Event::Key(key) = event { + if Instant::now().duration_since(keyboard_timer).as_millis() >= 20 { + if sender.send(BottomEvent::KeyInput(key)).is_err() { + break; } + keyboard_timer = Instant::now(); } - Event::Mouse(mouse) => { - if Instant::now().duration_since(mouse_timer).as_millis() >= 20 { - if sender.send(BottomEvent::MouseInput(mouse)).is_err() { - break; - } - mouse_timer = Instant::now(); + } else if let Event::Mouse(mouse) = event { + if Instant::now().duration_since(mouse_timer).as_millis() >= 20 { + if sender.send(BottomEvent::MouseInput(mouse)).is_err() { + break; } - } - Event::Resize(_, _) => { - // if sender.send(BottomEvent::RequestRedraw).is_err() { - // break; - // } + mouse_timer = Instant::now(); } } }