mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-04-08 17:05:59 +02:00
Column width layout fix
This commit is contained in:
parent
f45b65e68e
commit
863ae0059a
@ -11,6 +11,7 @@ use std::borrow::Cow;
|
||||
pub struct ConvertedData<'a> {
|
||||
data: &'a DataCollection,
|
||||
temp_table: Option<Vec<Vec<Cow<'static, str>>>>,
|
||||
disk_table: Option<Vec<Vec<Cow<'static, str>>>>,
|
||||
}
|
||||
|
||||
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<Vec<Cow<'static, str>>> {
|
||||
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::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Point is of time, data
|
||||
|
@ -18,3 +18,6 @@ pub use container::Container;
|
||||
|
||||
pub mod empty;
|
||||
pub use empty::Empty;
|
||||
|
||||
pub mod padding;
|
||||
pub use padding::*;
|
0
src/tuine/component/base/padding.rs
Normal file
0
src/tuine/component/base/padding.rs
Normal file
@ -71,31 +71,36 @@ impl<Message> TextTable<Message> {
|
||||
.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;
|
||||
|
@ -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<Message> {
|
||||
inner: SimpleTable<Message>,
|
||||
}
|
||||
|
||||
impl super::AppWidget for DiskTable {
|
||||
impl<Message> DiskTable<Message> {}
|
||||
|
||||
impl<Message> AppWidget for DiskTable<Message> {
|
||||
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<Message> TmpComponent<Message> for DiskTable {
|
||||
impl<Message> TmpComponent<Message> for DiskTable<Message> {
|
||||
fn draw<Backend>(
|
||||
&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<Message>,
|
||||
) -> 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)
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ where
|
||||
BatteryTable(BatteryTable),
|
||||
CpuGraph(CpuGraph),
|
||||
CpuSimple(CpuSimple),
|
||||
DiskTable(DiskTable),
|
||||
DiskTable(DiskTable<Message>),
|
||||
MemGraph(MemGraph),
|
||||
MemSimple(MemSimple),
|
||||
NetGraph(NetGraph),
|
||||
|
Loading…
x
Reference in New Issue
Block a user