From 863ae0059adfa7b2381c97b73153350437d9bec2 Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Fri, 31 Dec 2021 01:42:11 -0500 Subject: [PATCH] Column width layout fix --- src/data_conversion.rs | 60 +++++++++++++++++++ src/tuine/component/base/mod.rs | 3 + src/tuine/component/base/padding.rs | 0 src/tuine/component/base/text_table/mod.rs | 29 +++++---- src/tuine/component/widget/disk_table.rs | 68 +++++++++++++++++----- src/tuine/element.rs | 2 +- 6 files changed, 133 insertions(+), 29 deletions(-) create mode 100644 src/tuine/component/base/padding.rs diff --git a/src/data_conversion.rs b/src/data_conversion.rs index 509a0342..20edf8c6 100644 --- a/src/data_conversion.rs +++ b/src/data_conversion.rs @@ -11,6 +11,7 @@ use std::borrow::Cow; pub struct ConvertedData<'a> { data: &'a DataCollection, temp_table: Option>>>, + disk_table: Option>>>, } impl<'a> ConvertedData<'a> { @@ -18,6 +19,7 @@ impl<'a> ConvertedData<'a> { Self { data, temp_table: None, + disk_table: None, } } @@ -52,6 +54,64 @@ impl<'a> ConvertedData<'a> { } } } + + pub fn disk_table(&mut self) -> Vec>> { + match &self.disk_table { + Some(disk_table) => disk_table.clone(), + None => { + if self.data.disk_harvest.is_empty() { + vec![vec!["No Disks Found".into(), "".into()]] + } else { + self.data + .disk_harvest + .iter() + .zip(&self.data.io_labels) + .map(|(disk, (io_read, io_write))| { + let free_space_fmt = if let Some(free_space) = disk.free_space { + let converted_free_space = get_decimal_bytes(free_space); + Cow::Owned(format!( + "{:.*}{}", + 0, converted_free_space.0, converted_free_space.1 + )) + } else { + "N/A".into() + }; + let total_space_fmt = if let Some(total_space) = disk.total_space { + let converted_total_space = get_decimal_bytes(total_space); + Cow::Owned(format!( + "{:.*}{}", + 0, converted_total_space.0, converted_total_space.1 + )) + } else { + "N/A".into() + }; + + let usage_fmt = if let (Some(used_space), Some(total_space)) = + (disk.used_space, disk.total_space) + { + Cow::Owned(format!( + "{:.0}%", + used_space as f64 / total_space as f64 * 100_f64 + )) + } else { + "N/A".into() + }; + + vec![ + disk.name.clone().into(), + disk.mount_point.clone().into(), + usage_fmt, + free_space_fmt, + total_space_fmt, + io_read.clone().into(), + io_write.clone().into(), + ] + }) + .collect::>() + } + } + } + } } /// Point is of time, data diff --git a/src/tuine/component/base/mod.rs b/src/tuine/component/base/mod.rs index 2306514d..16658b3e 100644 --- a/src/tuine/component/base/mod.rs +++ b/src/tuine/component/base/mod.rs @@ -18,3 +18,6 @@ pub use container::Container; pub mod empty; pub use empty::Empty; + +pub mod padding; +pub use padding::*; \ No newline at end of file diff --git a/src/tuine/component/base/padding.rs b/src/tuine/component/base/padding.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/tuine/component/base/text_table/mod.rs b/src/tuine/component/base/text_table/mod.rs index 860d5ebf..6f8529a2 100644 --- a/src/tuine/component/base/text_table/mod.rs +++ b/src/tuine/component/base/text_table/mod.rs @@ -71,31 +71,36 @@ impl TextTable { .columns .iter() .map(|column| { + let desired = column.name.graphemes(true).count() as u16; // FIXME: Should this be +1 if sorting is enabled? let width = match column.width_constraint { - TextColumnConstraint::Fill => { - let desired = column.name.graphemes(true).count().saturating_add(1) as u16; - min(desired, width_remaining) - } + TextColumnConstraint::Fill => min(desired, width_remaining), TextColumnConstraint::Length(length) => min(length, width_remaining), TextColumnConstraint::Percentage(percentage) => { let length = total_width * percentage / 100; min(length, width_remaining) } - TextColumnConstraint::MaxLength(length) => { - let desired = column.name.graphemes(true).count().saturating_add(1) as u16; - min(min(length, desired), width_remaining) - } + TextColumnConstraint::MaxLength(length) => min(length, width_remaining), TextColumnConstraint::MaxPercentage(percentage) => { - let desired = column.name.graphemes(true).count().saturating_add(1) as u16; let length = total_width * percentage / 100; - min(min(desired, length), width_remaining) + min(length, width_remaining) } }; - width_remaining -= width; - width + + if desired > width { + 0 + } else { + // +1 for the spacing + width_remaining -= width + 1; + width + } }) .collect(); + // Prune from the end + while let Some(0) = column_widths.last() { + column_widths.pop(); + } + if !column_widths.is_empty() { let amount_per_slot = width_remaining / column_widths.len() as u16; width_remaining %= column_widths.len() as u16; diff --git a/src/tuine/component/widget/disk_table.rs b/src/tuine/component/widget/disk_table.rs index 42927cb1..c7434d9c 100644 --- a/src/tuine/component/widget/disk_table.rs +++ b/src/tuine/component/widget/disk_table.rs @@ -1,30 +1,66 @@ -use tui::{text::Text, widgets::Paragraph, Frame}; +use crate::{ + app::AppConfig, + canvas::Painter, + data_conversion::ConvertedData, + tuine::{ + Bounds, DrawContext, LayoutNode, SimpleTable, Size, StateContext, Status, TmpComponent, + ViewContext, + }, +}; -use crate::tuine::{DrawContext, StateContext, TmpComponent}; +use super::{simple_table, AppWidget}; -/// A [`DiskTable`] is a table displaying disk data. -pub struct DiskTable {} +/// A [`DiskTable`] is a table displaying temperature data. +/// +/// It wraps a [`SimpleTable`], with set columns and manages extracting data and styling. +pub struct DiskTable { + inner: SimpleTable, +} -impl super::AppWidget for DiskTable { +impl DiskTable {} + +impl AppWidget for DiskTable { fn build( - ctx: &mut crate::tuine::ViewContext<'_>, painter: &crate::canvas::Painter, - config: &crate::app::AppConfig, data: &mut crate::data_conversion::ConvertedData<'_>, + ctx: &mut ViewContext<'_>, painter: &Painter, config: &AppConfig, + data: &mut ConvertedData<'_>, ) -> Self { - Self {} + let style = simple_table::StyleSheet { + text: painter.colours.text_style, + selected_text: painter.colours.currently_selected_text_style, + table_header: painter.colours.table_header_style, + border: painter.colours.border_style, + }; + let rows = data.disk_table(); + + Self { + inner: SimpleTable::build( + ctx, + style, + vec!["Disk", "Mount", "Used", "Free", "Total", "R/s", "W/s"], + rows, + ), + } } } -impl TmpComponent for DiskTable { +impl TmpComponent for DiskTable { fn draw( - &mut self, _state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, - frame: &mut Frame<'_, Backend>, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, + frame: &mut tui::Frame<'_, Backend>, ) where Backend: tui::backend::Backend, { - let rect = draw_ctx.global_rect(); - frame.render_widget( - Paragraph::new(Text::raw("Disk Table")).block(tui::widgets::Block::default()), - rect, - ); + self.inner.draw(state_ctx, draw_ctx, frame); + } + + fn on_event( + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, + event: crate::tuine::Event, messages: &mut Vec, + ) -> Status { + self.inner.on_event(state_ctx, draw_ctx, event, messages) + } + + fn layout(&self, bounds: Bounds, node: &mut LayoutNode) -> Size { + self.inner.layout(bounds, node) } } diff --git a/src/tuine/element.rs b/src/tuine/element.rs index a24590f7..aec0017f 100644 --- a/src/tuine/element.rs +++ b/src/tuine/element.rs @@ -19,7 +19,7 @@ where BatteryTable(BatteryTable), CpuGraph(CpuGraph), CpuSimple(CpuSimple), - DiskTable(DiskTable), + DiskTable(DiskTable), MemGraph(MemGraph), MemSimple(MemSimple), NetGraph(NetGraph),