diff --git a/src/app.rs b/src/app.rs index 52807a1f..ef81d54d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -29,7 +29,6 @@ use frozen_state::FrozenState; use crate::{ canvas::Painter, constants, - data_conversion::ConvertedData, tuine::{Application, Element, Status, ViewContext}, units::data_units::DataUnit, Pid, @@ -135,6 +134,7 @@ pub enum AppMessages { to_kill: Vec, signal: Option, }, + Expand, ToggleFreeze, Reset, Clean, @@ -150,23 +150,16 @@ pub struct AppState { frozen_state: FrozenState, current_screen: CurrentScreen, pub painter: Painter, + layout: WidgetLayoutNode, terminator: Arc, } impl AppState { /// Creates a new [`AppState`]. pub fn new( - app_config: AppConfig, filters: DataFilters, layout_tree_output: LayoutCreationOutput, - painter: Painter, + app_config: AppConfig, filters: DataFilters, layout: WidgetLayoutNode, + used_widgets: UsedWidgets, painter: Painter, ) -> Result { - let LayoutCreationOutput { - layout_tree: _, - root: _, - widget_lookup_map, - selected: _, - used_widgets, - } = layout_tree_output; - Ok(Self { app_config, filters, @@ -177,7 +170,7 @@ impl AppState { data_collection: Default::default(), frozen_state: Default::default(), current_screen: Default::default(), - + layout, terminator: Self::register_terminator()?, }) } @@ -221,6 +214,10 @@ impl Application for AppState { // FIXME: Handle process termination true } + AppMessages::Expand => { + // FIXME: Expand current widget + true + } AppMessages::ToggleFreeze => { self.frozen_state.toggle(&self.data_collection); true @@ -246,7 +243,27 @@ impl Application for AppState { } fn view<'b>(&mut self, ctx: &mut ViewContext<'_>) -> Element { - todo!() + match self.current_screen { + CurrentScreen::Main => { + // The main screen. + + todo!() + } + CurrentScreen::Expanded => { + // Displayed when a user "expands" a widget + todo!() + } + CurrentScreen::Help => { + // The help dialog. + + todo!() + } + CurrentScreen::Delete => { + // The delete dialog. + + todo!() + } + } } fn destructor(&mut self) { diff --git a/src/app/layout_manager.rs b/src/app/layout_manager.rs index 6f3595d6..9a260c94 100644 --- a/src/app/layout_manager.rs +++ b/src/app/layout_manager.rs @@ -1,14 +1,7 @@ use crate::{ - app::{ - BasicCpu, BasicMem, BasicNet, BatteryTable, Carousel, DiskTable, Empty, MemGraph, NetGraph, - OldNetGraph, ProcessManager, SelectableType, TempTable, - }, + app::SelectableType, error::{BottomError, Result}, - options::{ - layout_options::{LayoutRow, LayoutRowChild, LayoutRule}, - ProcessDefaults, - }, - tuine::{Element, Flex}, + options::layout_options::{FinalWidget, LayoutRow, LayoutRowChild, LayoutRule}, }; use indextree::{Arena, NodeId}; use rustc_hash::FxHashMap; @@ -17,9 +10,7 @@ use tui::layout::Rect; use crate::app::widgets::Widget; -use super::{ - event::SelectionAction, AppConfig, AppState, CpuGraph, OldBottomWidget, TimeGraph, UsedWidgets, -}; +use super::{event::SelectionAction, OldBottomWidget, UsedWidgets}; #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum BottomWidgetType { @@ -41,12 +32,6 @@ pub enum BottomWidgetType { Carousel, } -impl Default for BottomWidgetType { - fn default() -> Self { - BottomWidgetType::Empty - } -} - impl FromStr for BottomWidgetType { type Err = BottomError; @@ -122,16 +107,6 @@ pub struct RowLayout { pub bound: Rect, } -impl RowLayout { - fn new(parent_rule: LayoutRule) -> Self { - Self { - last_selected: None, - parent_rule, - bound: Rect::default(), - } - } -} - /// Represents a column in the layout tree. #[derive(Debug, PartialEq, Eq, Clone)] pub struct ColLayout { @@ -140,16 +115,6 @@ pub struct ColLayout { pub bound: Rect, } -impl ColLayout { - fn new(parent_rule: LayoutRule) -> Self { - Self { - last_selected: None, - parent_rule, - bound: Rect::default(), - } - } -} - /// Represents a widget in the layout tree. #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct WidgetLayout { @@ -178,327 +143,125 @@ pub enum MovementDirection { Down, } -pub fn initialize_widget_layout( - layout_rows: &[LayoutRow], app: &AppState, -) -> anyhow::Result> { - let mut root = Flex::column(); - - for layout_row in layout_rows { - let mut row = Flex::row(); - if let Some(children) = &layout_row.child { - for child in children { - match child { - LayoutRowChild::Widget(widget) => {} - LayoutRowChild::Carousel { - carousel_children, - default, - } => {} - LayoutRowChild::LayoutCol { - ratio, - child: children, - } => for child in children {}, - } - } - } - - root = root.with_child(row); - } - - Ok(root.into()) +/// An intermediate representation of the widget layout. +pub enum WidgetLayoutNode { + Row { + children: Vec, + parent_rule: LayoutRule, + }, + Col { + children: Vec, + parent_rule: LayoutRule, + }, + Carousel { + children: Vec, + selected: bool, + }, + Widget { + widget_type: BottomWidgetType, + selected: bool, + width_rule: LayoutRule, + height_rule: LayoutRule, + }, } -/// A wrapper struct to simplify the output of [`create_layout_tree`]. -pub struct LayoutCreationOutput { - pub layout_tree: Arena, - pub root: NodeId, - pub widget_lookup_map: FxHashMap, - pub selected: NodeId, - pub used_widgets: UsedWidgets, -} - -/// Creates a new [`Arena`] from the given config and returns it, along with the [`NodeId`] representing -/// the root of the newly created [`Arena`], a mapping from [`NodeId`]s to [`BottomWidget`]s, and optionally, a default -/// selected [`NodeId`]. -// FIXME: [AFTER REFACTOR] This is currently jury-rigged "glue" just to work with the existing config system! We are NOT keeping it like this, it's too awful to keep like this! -pub fn create_layout_tree( - rows: &[LayoutRow], process_defaults: ProcessDefaults, app_config_fields: &AppConfig, -) -> Result { - fn add_widget_to_map( - widget_lookup_map: &mut FxHashMap, widget_type: BottomWidgetType, - widget_id: NodeId, process_defaults: &ProcessDefaults, app_config_fields: &AppConfig, - width: LayoutRule, height: LayoutRule, - ) -> Result<()> { - match widget_type { - BottomWidgetType::Cpu => { - widget_lookup_map.insert( - widget_id, - CpuGraph::from_config(app_config_fields) - .width(width) - .height(height) - .into(), - ); - } - BottomWidgetType::Mem => { - let graph = TimeGraph::from_config(app_config_fields); - widget_lookup_map.insert( - widget_id, - MemGraph::new(graph).width(width).height(height).into(), - ); - } - BottomWidgetType::Net => { - if app_config_fields.use_old_network_legend { - widget_lookup_map.insert( - widget_id, - OldNetGraph::from_config(app_config_fields) - .width(width) - .height(height) - .into(), - ); - } else { - widget_lookup_map.insert( - widget_id, - NetGraph::from_config(app_config_fields) - .width(width) - .height(height) - .into(), - ); - } - } - BottomWidgetType::Proc => { - widget_lookup_map.insert( - widget_id, - ProcessManager::new(process_defaults, app_config_fields) - .width(width) - .height(height) - .basic_mode(app_config_fields.use_basic_mode) - .show_scroll_index(app_config_fields.show_table_scroll_position) - .into(), - ); - } - BottomWidgetType::Temp => { - widget_lookup_map.insert( - widget_id, - TempTable::from_config(app_config_fields) - .set_temp_type(app_config_fields.temperature_type.clone()) - .width(width) - .height(height) - .basic_mode(app_config_fields.use_basic_mode) - .show_scroll_index(app_config_fields.show_table_scroll_position) - .into(), - ); - } - BottomWidgetType::Disk => { - widget_lookup_map.insert( - widget_id, - DiskTable::from_config(app_config_fields) - .width(width) - .height(height) - .basic_mode(app_config_fields.use_basic_mode) - .show_scroll_index(app_config_fields.show_table_scroll_position) - .into(), - ); - } - BottomWidgetType::Battery => { - widget_lookup_map.insert( - widget_id, - BatteryTable::default() - .width(width) - .height(height) - .basic_mode(app_config_fields.use_basic_mode) - .into(), - ); - } - BottomWidgetType::BasicCpu => { - widget_lookup_map.insert( - widget_id, - BasicCpu::from_config(app_config_fields).width(width).into(), - ); - } - BottomWidgetType::BasicMem => { - widget_lookup_map.insert(widget_id, BasicMem::default().width(width).into()); - } - BottomWidgetType::BasicNet => { - widget_lookup_map.insert( - widget_id, - BasicNet::from_config(app_config_fields).width(width).into(), - ); - } - BottomWidgetType::Empty => { - widget_lookup_map.insert( - widget_id, - Empty::default().width(width).height(height).into(), - ); - } - _ => {} - } - - Ok(()) - } - - let mut arena = Arena::new(); - let root_id = arena.new_node(LayoutNode::Col(ColLayout::new(LayoutRule::Expand { - ratio: 1, - }))); - let mut widget_lookup_map = FxHashMap::default(); - let mut first_selected = None; - let mut first_widget_seen = None; // Backup selected widget +/// Parses the layout in the config into an intermediate representation. +pub fn parse_widget_layout( + layout_rows: &[LayoutRow], +) -> anyhow::Result<(WidgetLayoutNode, UsedWidgets)> { + let mut root_children = Vec::with_capacity(layout_rows.len()); let mut used_widgets = UsedWidgets::default(); - for row in rows { - let row_id = arena.new_node(LayoutNode::Row(RowLayout::new( - row.ratio - .map(|ratio| LayoutRule::Expand { ratio }) - .unwrap_or(LayoutRule::Child), - ))); - root_id.append(row_id, &mut arena); + for layout_row in layout_rows { + if let Some(children) = &layout_row.child { + let mut row_children = Vec::with_capacity(children.len()); - if let Some(children) = &row.child { for child in children { match child { LayoutRowChild::Widget(widget) => { - let widget_id = arena.new_node(LayoutNode::Widget(WidgetLayout::default())); - row_id.append(widget_id, &mut arena); + let FinalWidget { + rule, + widget_type, + default, + } = widget; - if let Some(true) = widget.default { - first_selected = Some(widget_id); - } - - if first_widget_seen.is_none() { - first_widget_seen = Some(widget_id); - } - - let widget_type = widget.widget_type.parse::()?; + let widget_type = widget_type.parse::()?; used_widgets.add(&widget_type); - add_widget_to_map( - &mut widget_lookup_map, + row_children.push(WidgetLayoutNode::Widget { widget_type, - widget_id, - &process_defaults, - app_config_fields, - widget.rule.unwrap_or_default(), - LayoutRule::default(), - )?; + selected: default.unwrap_or(false), + width_rule: rule.unwrap_or_default(), + height_rule: LayoutRule::default(), + }); } LayoutRowChild::Carousel { carousel_children, default, } => { - if !carousel_children.is_empty() { - let mut child_ids = Vec::with_capacity(carousel_children.len()); - let carousel_widget_id = - arena.new_node(LayoutNode::Widget(WidgetLayout::default())); - row_id.append(carousel_widget_id, &mut arena); + let mut car_children = Vec::with_capacity(carousel_children.len()); + for widget_type in carousel_children { + let widget_type = widget_type.parse::()?; + used_widgets.add(&widget_type); - if let Some(true) = default { - first_selected = Some(carousel_widget_id); - } - - if first_widget_seen.is_none() { - first_widget_seen = Some(carousel_widget_id); - } - - // Handle the rest of the children. - for child in carousel_children { - let widget_id = - arena.new_node(LayoutNode::Widget(WidgetLayout::default())); - carousel_widget_id.append(widget_id, &mut arena); - - let widget_type = child.parse::()?; - used_widgets.add(&widget_type); - - add_widget_to_map( - &mut widget_lookup_map, - widget_type, - widget_id, - &process_defaults, - app_config_fields, - LayoutRule::default(), - LayoutRule::default(), - )?; - - child_ids.push(widget_id); - } - - widget_lookup_map.insert( - carousel_widget_id, - Carousel::new( - child_ids - .into_iter() - .filter_map(|child_id| { - widget_lookup_map - .get(&child_id) - .map(|w| (child_id, w.get_pretty_name().into())) - }) - .collect(), - ) - .into(), - ); + car_children.push(widget_type); } + + row_children.push(WidgetLayoutNode::Carousel { + children: car_children, + selected: default.unwrap_or(false), + }); } LayoutRowChild::LayoutCol { ratio, - child: col_child, + child: children, } => { - let col_id = arena.new_node(LayoutNode::Col(ColLayout::new( - ratio - .map(|ratio| LayoutRule::Expand { ratio }) - .unwrap_or(LayoutRule::Child), - ))); - row_id.append(col_id, &mut arena); - - for widget in col_child { - let widget_id = - arena.new_node(LayoutNode::Widget(WidgetLayout::default())); - col_id.append(widget_id, &mut arena); - - if let Some(true) = widget.default { - first_selected = Some(widget_id); - } - - if first_widget_seen.is_none() { - first_widget_seen = Some(widget_id); - } - - let widget_type = widget.widget_type.parse::()?; + let mut col_children = Vec::with_capacity(children.len()); + for widget in children { + let FinalWidget { + rule, + widget_type, + default, + } = widget; + let widget_type = widget_type.parse::()?; used_widgets.add(&widget_type); - add_widget_to_map( - &mut widget_lookup_map, + col_children.push(WidgetLayoutNode::Widget { widget_type, - widget_id, - &process_defaults, - app_config_fields, - LayoutRule::default(), - widget.rule.unwrap_or_default(), - )?; + selected: default.unwrap_or(false), + width_rule: LayoutRule::default(), + height_rule: rule.unwrap_or_default(), + }); } + + row_children.push(WidgetLayoutNode::Col { + children: col_children, + parent_rule: match ratio { + Some(ratio) => LayoutRule::Expand { ratio: *ratio }, + None => LayoutRule::Child, + }, + }); } } } + + let row = WidgetLayoutNode::Row { + children: row_children, + parent_rule: match layout_row.ratio { + Some(ratio) => LayoutRule::Expand { ratio }, + None => LayoutRule::Child, + }, + }; + root_children.push(row); } } - let selected: NodeId; - if let Some(first_selected) = first_selected { - selected = first_selected; - } else if let Some(first_widget_seen) = first_widget_seen { - selected = first_widget_seen; - } else { - return Err(BottomError::ConfigError( - "A layout cannot contain zero widgets!".to_string(), - )); - } + let root = WidgetLayoutNode::Col { + children: root_children, + parent_rule: LayoutRule::Expand { ratio: 1 }, + }; - correct_layout_last_selections(&mut arena, selected); - - Ok(LayoutCreationOutput { - layout_tree: arena, - root: root_id, - widget_lookup_map, - selected, - used_widgets, - }) + Ok((root, used_widgets)) } /// We may have situations where we also have to make sure the correct layout indices are selected. diff --git a/src/app/widgets/base/sort_text_table.rs b/src/app/widgets/base/sort_text_table.rs index c107dae0..fc805eb0 100644 --- a/src/app/widgets/base/sort_text_table.rs +++ b/src/app/widgets/base/sort_text_table.rs @@ -119,7 +119,6 @@ pub struct SimpleSortableColumn { original_name: Cow<'static, str>, pub shortcut: Option<(KeyEvent, String)>, pub default_descending: bool, - x_bounds: Option<(u16, u16)>, pub internal: SimpleColumn, @@ -138,7 +137,6 @@ impl SimpleSortableColumn { original_name, shortcut, default_descending, - x_bounds: None, internal: SimpleColumn::new(full_name, desired_width), sorting_status: SortStatus::NotSorting, } diff --git a/src/constants.rs b/src/constants.rs index a068a199..dd27fd6d 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -225,7 +225,7 @@ pub const HELP_CONTENTS_TEXT: [&str; 8] = [ ]; pub const GENERAL_HELP_TITLE: &str = "General"; -pub const GENERAL_HELP_TEXT: [[&str; 2]; 21] = [ +pub const GENERAL_HELP_TEXT: [[&str; 2]; 23] = [ ["q, Ctrl-c", "Quit"], [ "Esc", @@ -251,6 +251,8 @@ pub const GENERAL_HELP_TEXT: [[&str; 2]; 21] = [ ["+", "Zoom in on chart (decrease time range)"], ["-", "Zoom out on chart (increase time range)"], ["=", "Reset zoom"], + ["Page Up", "Move up one page in a table"], + ["Page Down", "Move down one page in a table"], [ "Mouse scroll", "Scroll through the tables or zoom in/out of charts by scrolling up/down", diff --git a/src/options.rs b/src/options.rs index 93cca743..140589b4 100644 --- a/src/options.rs +++ b/src/options.rs @@ -241,7 +241,7 @@ pub fn build_app(matches: &clap::ArgMatches<'static>, config: &mut Config) -> Re &rows }; - let layout_tree_output = create_layout_tree(row_ref, process_defaults, &app_config_fields)?; + let (layout, used_widgets) = parse_widget_layout(row_ref)?; let disk_filter = get_ignore_list(&config.disk_filter).context("Update 'disk_filter' in your config file")?; @@ -259,7 +259,13 @@ pub fn build_app(matches: &clap::ArgMatches<'static>, config: &mut Config) -> Re }; let painter = Painter::init(&config, get_color_scheme(&matches, &config)?)?; - AppState::new(app_config_fields, data_filter, layout_tree_output, painter) + AppState::new( + app_config_fields, + data_filter, + layout, + used_widgets, + painter, + ) } fn get_update_rate_in_milliseconds( diff --git a/src/tuine/component/widget/mod.rs b/src/tuine/component/widget/mod.rs index ca788d12..0bc0c318 100644 --- a/src/tuine/component/widget/mod.rs +++ b/src/tuine/component/widget/mod.rs @@ -1,6 +1,3 @@ -use anyhow::{anyhow, Result}; -use enum_dispatch::enum_dispatch; - pub mod simple_table; pub use simple_table::*; diff --git a/src/tuine/component/widget/simple_table.rs b/src/tuine/component/widget/simple_table.rs index ebf10702..01a29d9c 100644 --- a/src/tuine/component/widget/simple_table.rs +++ b/src/tuine/component/widget/simple_table.rs @@ -1,10 +1,9 @@ -use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use tui::style::Style; use crate::tuine::{ self, block, text_table::{self, DataRow, SortType, TextTableProps}, - Block, Event, Shortcut, StatefulComponent, Status, TextTable, TmpComponent, ViewContext, + Block, Shortcut, StatefulComponent, TextTable, TmpComponent, ViewContext, }; /// A set of styles for a [`SimpleTable`]. diff --git a/src/tuine/component/widget/temp_table.rs b/src/tuine/component/widget/temp_table.rs index 3dbd1b95..da11e759 100644 --- a/src/tuine/component/widget/temp_table.rs +++ b/src/tuine/component/widget/temp_table.rs @@ -3,8 +3,8 @@ use crate::{ canvas::Painter, data_conversion::ConvertedData, tuine::{ - Bounds, DataRow, DrawContext, LayoutNode, SimpleTable, Size, StateContext, Status, - TmpComponent, ViewContext, + Bounds, DrawContext, LayoutNode, SimpleTable, Size, StateContext, Status, TmpComponent, + ViewContext, }, }; diff --git a/src/tuine/tui_rs/block_builder.rs b/src/tuine/tui_rs/block_builder.rs deleted file mode 100644 index cec835e3..00000000 --- a/src/tuine/tui_rs/block_builder.rs +++ /dev/null @@ -1,121 +0,0 @@ -use tui::{ - layout::Rect, - text::Span, - widgets::{Block, Borders}, -}; - -use crate::canvas::Painter; - -/// A factory pattern builder for a tui [`Block`]. -pub struct BlockBuilder { - borders: Borders, - selected: bool, - show_esc: bool, - name: &'static str, - hide_title: bool, - extra_text: Option, -} - -impl BlockBuilder { - /// Creates a new [`BlockBuilder`] with the name of block. - pub fn new(name: &'static str) -> Self { - Self { - borders: Borders::ALL, - selected: false, - show_esc: false, - name, - hide_title: false, - extra_text: None, - } - } - - /// Indicates that this block is currently selected, and should be drawn as such. - pub fn selected(mut self, selected: bool) -> Self { - self.selected = selected; - self - } - - /// Indicates that this block should show esc, and should be drawn as such. - pub fn show_esc(mut self, show_esc: bool) -> Self { - self.show_esc = show_esc; - self - } - - /// Indicates that this block has some extra text beyond the name. - pub fn extra_text(mut self, extra_text: Option) -> Self { - self.extra_text = extra_text; - self - } - - /// Determines the borders of the built [`Block`]. - pub fn borders(mut self, borders: Borders) -> Self { - self.borders = borders; - self - } - - /// 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 border_style = if self.selected { - painter.colours.highlighted_border_style - } else { - painter.colours.border_style - }; - - let block = Block::default() - .border_style(border_style) - .borders(self.borders); - - 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.show_esc { - const EXPAND_TEXT: &str = " Esc to go back "; - const EXPAND_TEXT_LEN: usize = EXPAND_TEXT.len(); - - let expand_span = Span::styled(EXPAND_TEXT, 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.show_esc { - let difference = inner_width.saturating_sub(title_len); - title[2] = Span::styled("─".repeat(difference), border_style); - } - - block.title(title) - } else { - block - } - } -} diff --git a/src/tuine/tui_rs/mod.rs b/src/tuine/tui_rs/mod.rs index fdaf8a85..1c0eaab3 100644 --- a/src/tuine/tui_rs/mod.rs +++ b/src/tuine/tui_rs/mod.rs @@ -3,6 +3,3 @@ pub use custom_legend_chart::TimeChart; pub mod pipe_gauge; pub use pipe_gauge::PipeGauge; - -pub mod block_builder; -pub use block_builder::BlockBuilder;