mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-27 07:34:27 +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> {
|
pub struct ConvertedData<'a> {
|
||||||
data: &'a DataCollection,
|
data: &'a DataCollection,
|
||||||
temp_table: Option<Vec<Vec<Cow<'static, str>>>>,
|
temp_table: Option<Vec<Vec<Cow<'static, str>>>>,
|
||||||
|
disk_table: Option<Vec<Vec<Cow<'static, str>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ConvertedData<'a> {
|
impl<'a> ConvertedData<'a> {
|
||||||
@ -18,6 +19,7 @@ impl<'a> ConvertedData<'a> {
|
|||||||
Self {
|
Self {
|
||||||
data,
|
data,
|
||||||
temp_table: None,
|
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
|
/// Point is of time, data
|
||||||
|
@ -18,3 +18,6 @@ pub use container::Container;
|
|||||||
|
|
||||||
pub mod empty;
|
pub mod empty;
|
||||||
pub use empty::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
|
.columns
|
||||||
.iter()
|
.iter()
|
||||||
.map(|column| {
|
.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 {
|
let width = match column.width_constraint {
|
||||||
TextColumnConstraint::Fill => {
|
TextColumnConstraint::Fill => min(desired, width_remaining),
|
||||||
let desired = column.name.graphemes(true).count().saturating_add(1) as u16;
|
|
||||||
min(desired, width_remaining)
|
|
||||||
}
|
|
||||||
TextColumnConstraint::Length(length) => min(length, width_remaining),
|
TextColumnConstraint::Length(length) => min(length, width_remaining),
|
||||||
TextColumnConstraint::Percentage(percentage) => {
|
TextColumnConstraint::Percentage(percentage) => {
|
||||||
let length = total_width * percentage / 100;
|
let length = total_width * percentage / 100;
|
||||||
min(length, width_remaining)
|
min(length, width_remaining)
|
||||||
}
|
}
|
||||||
TextColumnConstraint::MaxLength(length) => {
|
TextColumnConstraint::MaxLength(length) => min(length, width_remaining),
|
||||||
let desired = column.name.graphemes(true).count().saturating_add(1) as u16;
|
|
||||||
min(min(length, desired), width_remaining)
|
|
||||||
}
|
|
||||||
TextColumnConstraint::MaxPercentage(percentage) => {
|
TextColumnConstraint::MaxPercentage(percentage) => {
|
||||||
let desired = column.name.graphemes(true).count().saturating_add(1) as u16;
|
|
||||||
let length = total_width * percentage / 100;
|
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();
|
.collect();
|
||||||
|
|
||||||
|
// Prune from the end
|
||||||
|
while let Some(0) = column_widths.last() {
|
||||||
|
column_widths.pop();
|
||||||
|
}
|
||||||
|
|
||||||
if !column_widths.is_empty() {
|
if !column_widths.is_empty() {
|
||||||
let amount_per_slot = width_remaining / column_widths.len() as u16;
|
let amount_per_slot = width_remaining / column_widths.len() as u16;
|
||||||
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.
|
/// A [`DiskTable`] is a table displaying temperature data.
|
||||||
pub struct DiskTable {}
|
///
|
||||||
|
/// 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(
|
fn build(
|
||||||
ctx: &mut crate::tuine::ViewContext<'_>, painter: &crate::canvas::Painter,
|
ctx: &mut ViewContext<'_>, painter: &Painter, config: &AppConfig,
|
||||||
config: &crate::app::AppConfig, data: &mut crate::data_conversion::ConvertedData<'_>,
|
data: &mut ConvertedData<'_>,
|
||||||
) -> Self {
|
) -> 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>(
|
fn draw<Backend>(
|
||||||
&mut self, _state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
&mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||||
frame: &mut Frame<'_, Backend>,
|
frame: &mut tui::Frame<'_, Backend>,
|
||||||
) where
|
) where
|
||||||
Backend: tui::backend::Backend,
|
Backend: tui::backend::Backend,
|
||||||
{
|
{
|
||||||
let rect = draw_ctx.global_rect();
|
self.inner.draw(state_ctx, draw_ctx, frame);
|
||||||
frame.render_widget(
|
}
|
||||||
Paragraph::new(Text::raw("Disk Table")).block(tui::widgets::Block::default()),
|
|
||||||
rect,
|
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),
|
BatteryTable(BatteryTable),
|
||||||
CpuGraph(CpuGraph),
|
CpuGraph(CpuGraph),
|
||||||
CpuSimple(CpuSimple),
|
CpuSimple(CpuSimple),
|
||||||
DiskTable(DiskTable),
|
DiskTable(DiskTable<Message>),
|
||||||
MemGraph(MemGraph),
|
MemGraph(MemGraph),
|
||||||
MemSimple(MemSimple),
|
MemSimple(MemSimple),
|
||||||
NetGraph(NetGraph),
|
NetGraph(NetGraph),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user