mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-27 07:34:27 +02:00
refactor: don't draw header if too short
This commit is contained in:
parent
c296b8bf5a
commit
2e51590bf5
@ -117,9 +117,7 @@ impl TableComponentState {
|
|||||||
|
|
||||||
/// Calculates widths for the columns for this table.
|
/// Calculates widths for the columns for this table.
|
||||||
///
|
///
|
||||||
/// * `total_width` is the, well, total width available. **NOTE:** This function automatically
|
/// * `total_width` is the, well, total width available.
|
||||||
/// takes away 2 from the width as part of the left/right
|
|
||||||
/// bounds.
|
|
||||||
/// * `left_to_right` is a boolean whether to go from left to right if true, or right to left if
|
/// * `left_to_right` is a boolean whether to go from left to right if true, or right to left if
|
||||||
/// false.
|
/// false.
|
||||||
///
|
///
|
||||||
@ -128,71 +126,68 @@ impl TableComponentState {
|
|||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
|
|
||||||
if total_width > 2 {
|
let mut total_width_left = total_width;
|
||||||
let initial_width = total_width - 2;
|
|
||||||
let mut total_width_left = initial_width;
|
|
||||||
|
|
||||||
let column_widths = &mut self.calculated_widths;
|
let column_widths = &mut self.calculated_widths;
|
||||||
*column_widths = vec![0; self.columns.len()];
|
*column_widths = vec![0; self.columns.len()];
|
||||||
|
|
||||||
let columns = if left_to_right {
|
let columns = if left_to_right {
|
||||||
Either::Left(self.columns.iter().enumerate())
|
Either::Left(self.columns.iter().enumerate())
|
||||||
} else {
|
} else {
|
||||||
Either::Right(self.columns.iter().enumerate().rev())
|
Either::Right(self.columns.iter().enumerate().rev())
|
||||||
};
|
};
|
||||||
|
|
||||||
for (itx, column) in columns {
|
for (itx, column) in columns {
|
||||||
match &column.width_bounds {
|
match &column.width_bounds {
|
||||||
WidthBounds::Soft {
|
WidthBounds::Soft {
|
||||||
min_width,
|
min_width,
|
||||||
desired,
|
desired,
|
||||||
max_percentage,
|
max_percentage,
|
||||||
} => {
|
} => {
|
||||||
let soft_limit = max(
|
let soft_limit = max(
|
||||||
if let Some(max_percentage) = max_percentage {
|
if let Some(max_percentage) = max_percentage {
|
||||||
// Rust doesn't have an `into()` or `try_into()` for floats to integers???
|
// Rust doesn't have an `into()` or `try_into()` for floats to integers???
|
||||||
((*max_percentage * f32::from(initial_width)).ceil()) as u16
|
((*max_percentage * f32::from(total_width)).ceil()) as u16
|
||||||
} else {
|
|
||||||
*desired
|
|
||||||
},
|
|
||||||
*min_width,
|
|
||||||
);
|
|
||||||
let space_taken = min(min(soft_limit, *desired), total_width_left);
|
|
||||||
|
|
||||||
if *min_width > space_taken {
|
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
total_width_left = total_width_left.saturating_sub(space_taken + 1);
|
*desired
|
||||||
column_widths[itx] = space_taken;
|
},
|
||||||
}
|
*min_width,
|
||||||
|
);
|
||||||
|
let space_taken = min(min(soft_limit, *desired), total_width_left);
|
||||||
|
|
||||||
|
if *min_width > space_taken {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
total_width_left = total_width_left.saturating_sub(space_taken + 1);
|
||||||
|
column_widths[itx] = space_taken;
|
||||||
}
|
}
|
||||||
WidthBounds::Hard(width) => {
|
}
|
||||||
let space_taken = min(*width, total_width_left);
|
WidthBounds::Hard(width) => {
|
||||||
|
let space_taken = min(*width, total_width_left);
|
||||||
|
|
||||||
if *width > space_taken {
|
if *width > space_taken {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
total_width_left = total_width_left.saturating_sub(space_taken + 1);
|
total_width_left = total_width_left.saturating_sub(space_taken + 1);
|
||||||
column_widths[itx] = space_taken;
|
column_widths[itx] = space_taken;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while let Some(0) = column_widths.last() {
|
while let Some(0) = column_widths.last() {
|
||||||
column_widths.pop();
|
column_widths.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if !column_widths.is_empty() {
|
if !column_widths.is_empty() {
|
||||||
// Redistribute remaining.
|
// Redistribute remaining.
|
||||||
let amount_per_slot = total_width_left / column_widths.len() as u16;
|
let amount_per_slot = total_width_left / column_widths.len() as u16;
|
||||||
total_width_left %= column_widths.len() as u16;
|
total_width_left %= column_widths.len() as u16;
|
||||||
for (index, width) in column_widths.iter_mut().enumerate() {
|
for (index, width) in column_widths.iter_mut().enumerate() {
|
||||||
if index < total_width_left.into() {
|
if index < total_width_left.into() {
|
||||||
*width += amount_per_slot + 1;
|
*width += amount_per_slot + 1;
|
||||||
} else {
|
} else {
|
||||||
*width += amount_per_slot;
|
*width += amount_per_slot;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ use crate::{
|
|||||||
|
|
||||||
pub struct TextTable<'a> {
|
pub struct TextTable<'a> {
|
||||||
pub table_gap: u16,
|
pub table_gap: u16,
|
||||||
pub table_height_offset: u16,
|
|
||||||
pub is_force_redraw: bool,
|
pub is_force_redraw: bool,
|
||||||
pub recalculate_column_widths: bool,
|
pub recalculate_column_widths: bool,
|
||||||
|
|
||||||
@ -100,71 +99,23 @@ impl<'a> TextTable<'a> {
|
|||||||
&self, f: &mut Frame<'_, B>, draw_loc: Rect, state: &mut TableComponentState,
|
&self, f: &mut Frame<'_, B>, draw_loc: Rect, state: &mut TableComponentState,
|
||||||
table_data: &TableData,
|
table_data: &TableData,
|
||||||
) -> Rect {
|
) -> Rect {
|
||||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
let margined_draw_loc = Layout::default()
|
||||||
0
|
.constraints([Constraint::Percentage(100)])
|
||||||
} else {
|
.horizontal_margin(if self.is_on_widget || self.draw_border {
|
||||||
self.table_gap
|
0
|
||||||
};
|
} else {
|
||||||
|
1
|
||||||
let sliced_vec = {
|
})
|
||||||
let num_rows = usize::from(
|
.direction(Direction::Horizontal)
|
||||||
(draw_loc.height + 1 - table_gap).saturating_sub(self.table_height_offset),
|
.split(draw_loc)[0];
|
||||||
);
|
|
||||||
let start = get_start_position(
|
|
||||||
num_rows,
|
|
||||||
&state.scroll_direction,
|
|
||||||
&mut state.scroll_bar,
|
|
||||||
state.current_scroll_position,
|
|
||||||
self.is_force_redraw,
|
|
||||||
);
|
|
||||||
let end = min(table_data.data.len(), start + num_rows + 1);
|
|
||||||
state
|
|
||||||
.table_state
|
|
||||||
.select(Some(state.current_scroll_position.saturating_sub(start)));
|
|
||||||
&table_data.data[start..end]
|
|
||||||
};
|
|
||||||
|
|
||||||
// Calculate widths
|
|
||||||
if self.recalculate_column_widths {
|
|
||||||
state
|
|
||||||
.columns
|
|
||||||
.iter_mut()
|
|
||||||
.zip(&table_data.row_widths)
|
|
||||||
.for_each(|(column, data_width)| match &mut column.width_bounds {
|
|
||||||
app::WidthBounds::Soft {
|
|
||||||
min_width: _,
|
|
||||||
desired,
|
|
||||||
max_percentage: _,
|
|
||||||
} => {
|
|
||||||
*desired = std::cmp::max(column.name.len(), *data_width) as u16;
|
|
||||||
}
|
|
||||||
app::WidthBounds::Hard(_width) => {}
|
|
||||||
});
|
|
||||||
|
|
||||||
state.calculate_column_widths(draw_loc.width, self.left_to_right);
|
|
||||||
}
|
|
||||||
|
|
||||||
let columns = &state.columns;
|
|
||||||
let widths = &state.calculated_widths;
|
|
||||||
// TODO: Maybe truncate this too?
|
|
||||||
let header = Row::new(columns.iter().map(|c| Text::raw(c.name.as_ref())))
|
|
||||||
.style(self.header_style)
|
|
||||||
.bottom_margin(table_gap);
|
|
||||||
let disk_rows = sliced_vec.iter().map(|row| {
|
|
||||||
Row::new(
|
|
||||||
row.iter()
|
|
||||||
.zip(widths)
|
|
||||||
.map(|(cell, width)| truncate_text(cell, (*width).into())),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let title = self.generate_title(
|
|
||||||
draw_loc,
|
|
||||||
state.current_scroll_position.saturating_add(1),
|
|
||||||
table_data.data.len(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let disk_block = if self.draw_border {
|
let disk_block = if self.draw_border {
|
||||||
|
let title = self.generate_title(
|
||||||
|
draw_loc,
|
||||||
|
state.current_scroll_position.saturating_add(1),
|
||||||
|
table_data.data.len(),
|
||||||
|
);
|
||||||
|
|
||||||
Block::default()
|
Block::default()
|
||||||
.title(title)
|
.title(title)
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
@ -177,34 +128,99 @@ impl<'a> TextTable<'a> {
|
|||||||
Block::default().borders(Borders::NONE)
|
Block::default().borders(Borders::NONE)
|
||||||
};
|
};
|
||||||
|
|
||||||
let margined_draw_loc = Layout::default()
|
let (inner_width, inner_height) = {
|
||||||
.constraints([Constraint::Percentage(100)])
|
let inner = disk_block.inner(margined_draw_loc);
|
||||||
.horizontal_margin(if self.is_on_widget || self.draw_border {
|
(inner.width, inner.height)
|
||||||
|
};
|
||||||
|
|
||||||
|
if inner_width == 0 || inner_height == 0 {
|
||||||
|
f.render_widget(disk_block, margined_draw_loc);
|
||||||
|
margined_draw_loc
|
||||||
|
} else {
|
||||||
|
let show_header = inner_height > 1;
|
||||||
|
let header_height = if show_header { 1 } else { 0 };
|
||||||
|
let table_gap = if !show_header || draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
1
|
self.table_gap
|
||||||
})
|
};
|
||||||
.direction(Direction::Horizontal)
|
|
||||||
.split(draw_loc)[0];
|
|
||||||
|
|
||||||
// Draw!
|
let sliced_vec = {
|
||||||
f.render_stateful_widget(
|
let num_rows = usize::from(inner_height.saturating_sub(table_gap + header_height));
|
||||||
Table::new(disk_rows)
|
let start = get_start_position(
|
||||||
.block(disk_block)
|
num_rows,
|
||||||
.header(header)
|
&state.scroll_direction,
|
||||||
.highlight_style(self.highlighted_text_style)
|
&mut state.scroll_bar,
|
||||||
.style(self.text_style)
|
state.current_scroll_position,
|
||||||
.widths(
|
self.is_force_redraw,
|
||||||
|
);
|
||||||
|
let end = min(table_data.data.len(), start + num_rows + 1);
|
||||||
|
state
|
||||||
|
.table_state
|
||||||
|
.select(Some(state.current_scroll_position.saturating_sub(start)));
|
||||||
|
&table_data.data[start..end]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate widths
|
||||||
|
if self.recalculate_column_widths {
|
||||||
|
state
|
||||||
|
.columns
|
||||||
|
.iter_mut()
|
||||||
|
.zip(&table_data.row_widths)
|
||||||
|
.for_each(|(column, data_width)| match &mut column.width_bounds {
|
||||||
|
app::WidthBounds::Soft {
|
||||||
|
min_width: _,
|
||||||
|
desired,
|
||||||
|
max_percentage: _,
|
||||||
|
} => {
|
||||||
|
*desired = std::cmp::max(column.name.len(), *data_width) as u16;
|
||||||
|
}
|
||||||
|
app::WidthBounds::Hard(_width) => {}
|
||||||
|
});
|
||||||
|
|
||||||
|
state.calculate_column_widths(inner_width, self.left_to_right);
|
||||||
|
}
|
||||||
|
|
||||||
|
let columns = &state.columns;
|
||||||
|
let widths = &state.calculated_widths;
|
||||||
|
// TODO: Maybe truncate this too?
|
||||||
|
let header = Row::new(columns.iter().map(|c| Text::raw(c.name.as_ref())))
|
||||||
|
.style(self.header_style)
|
||||||
|
.bottom_margin(table_gap);
|
||||||
|
let disk_rows = sliced_vec.iter().map(|row| {
|
||||||
|
Row::new(
|
||||||
|
row.iter()
|
||||||
|
.zip(widths)
|
||||||
|
.map(|(cell, width)| truncate_text(cell, (*width).into())),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let widget = {
|
||||||
|
let mut table = Table::new(disk_rows)
|
||||||
|
.block(disk_block)
|
||||||
|
.highlight_style(self.highlighted_text_style)
|
||||||
|
.style(self.text_style);
|
||||||
|
|
||||||
|
if show_header {
|
||||||
|
table = table.header(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
table
|
||||||
|
};
|
||||||
|
|
||||||
|
f.render_stateful_widget(
|
||||||
|
widget.widths(
|
||||||
&(widths
|
&(widths
|
||||||
.iter()
|
.iter()
|
||||||
.map(|w| Constraint::Length(*w))
|
.map(|w| Constraint::Length(*w))
|
||||||
.collect::<Vec<_>>()),
|
.collect::<Vec<_>>()),
|
||||||
),
|
),
|
||||||
margined_draw_loc,
|
margined_draw_loc,
|
||||||
&mut state.table_state,
|
&mut state.table_state,
|
||||||
);
|
);
|
||||||
|
|
||||||
margined_draw_loc
|
margined_draw_loc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{layout_manager::WidgetDirection, App},
|
app::{layout_manager::WidgetDirection, App, CpuWidgetState},
|
||||||
canvas::{
|
canvas::{
|
||||||
components::{GraphData, TimeGraph},
|
components::{GraphData, TimeGraph},
|
||||||
drawing_utils::{get_column_widths, get_start_position, should_hide_x_label},
|
drawing_utils::{get_column_widths, get_start_position, should_hide_x_label},
|
||||||
@ -115,6 +115,56 @@ impl Painter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_points<'a>(
|
||||||
|
&self, cpu_widget_state: &CpuWidgetState, cpu_data: &'a [ConvertedCpuData],
|
||||||
|
show_avg_cpu: bool,
|
||||||
|
) -> Vec<GraphData<'a>> {
|
||||||
|
let show_avg_offset = if show_avg_cpu { AVG_POSITION } else { 0 };
|
||||||
|
|
||||||
|
let current_scroll_position = cpu_widget_state.scroll_state.current_scroll_position;
|
||||||
|
if current_scroll_position == ALL_POSITION {
|
||||||
|
// This case ensures the other cases cannot have the position be equal to 0.
|
||||||
|
cpu_data
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.rev()
|
||||||
|
.map(|(itx, cpu)| {
|
||||||
|
let style = if show_avg_cpu && itx == AVG_POSITION {
|
||||||
|
self.colours.avg_colour_style
|
||||||
|
} else if itx == ALL_POSITION {
|
||||||
|
self.colours.all_colour_style
|
||||||
|
} else {
|
||||||
|
let offset_position = itx - 1; // Because of the all position
|
||||||
|
self.colours.cpu_colour_styles[(offset_position - show_avg_offset)
|
||||||
|
% self.colours.cpu_colour_styles.len()]
|
||||||
|
};
|
||||||
|
|
||||||
|
GraphData {
|
||||||
|
points: &cpu.cpu_data[..],
|
||||||
|
style,
|
||||||
|
name: None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
} else if let Some(cpu) = cpu_data.get(current_scroll_position) {
|
||||||
|
let style = if show_avg_cpu && current_scroll_position == AVG_POSITION {
|
||||||
|
self.colours.avg_colour_style
|
||||||
|
} else {
|
||||||
|
let offset_position = current_scroll_position - 1; // Because of the all position
|
||||||
|
self.colours.cpu_colour_styles
|
||||||
|
[(offset_position - show_avg_offset) % self.colours.cpu_colour_styles.len()]
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![GraphData {
|
||||||
|
points: &cpu.cpu_data[..],
|
||||||
|
style,
|
||||||
|
name: None,
|
||||||
|
}]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_cpu_graph<B: Backend>(
|
fn draw_cpu_graph<B: Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||||
) {
|
) {
|
||||||
@ -131,52 +181,12 @@ impl Painter {
|
|||||||
&mut cpu_widget_state.autohide_timer,
|
&mut cpu_widget_state.autohide_timer,
|
||||||
draw_loc,
|
draw_loc,
|
||||||
);
|
);
|
||||||
let show_avg_cpu = app_state.app_config_fields.show_average_cpu;
|
|
||||||
let show_avg_offset = if show_avg_cpu { AVG_POSITION } else { 0 };
|
|
||||||
let points = {
|
|
||||||
let current_scroll_position = cpu_widget_state.scroll_state.current_scroll_position;
|
|
||||||
if current_scroll_position == ALL_POSITION {
|
|
||||||
// This case ensures the other cases cannot have the position be equal to 0.
|
|
||||||
cpu_data
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.rev()
|
|
||||||
.map(|(itx, cpu)| {
|
|
||||||
let style = if show_avg_cpu && itx == AVG_POSITION {
|
|
||||||
self.colours.avg_colour_style
|
|
||||||
} else if itx == ALL_POSITION {
|
|
||||||
self.colours.all_colour_style
|
|
||||||
} else {
|
|
||||||
let offset_position = itx - 1; // Because of the all position
|
|
||||||
self.colours.cpu_colour_styles[(offset_position - show_avg_offset)
|
|
||||||
% self.colours.cpu_colour_styles.len()]
|
|
||||||
};
|
|
||||||
|
|
||||||
GraphData {
|
let points = self.generate_points(
|
||||||
points: &cpu.cpu_data[..],
|
&cpu_widget_state,
|
||||||
style,
|
cpu_data,
|
||||||
name: None,
|
app_state.app_config_fields.show_average_cpu,
|
||||||
}
|
);
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
} else if let Some(cpu) = cpu_data.get(current_scroll_position) {
|
|
||||||
let style = if show_avg_cpu && current_scroll_position == AVG_POSITION {
|
|
||||||
self.colours.avg_colour_style
|
|
||||||
} else {
|
|
||||||
let offset_position = current_scroll_position - 1; // Because of the all position
|
|
||||||
self.colours.cpu_colour_styles[(offset_position - show_avg_offset)
|
|
||||||
% self.colours.cpu_colour_styles.len()]
|
|
||||||
};
|
|
||||||
|
|
||||||
vec![GraphData {
|
|
||||||
points: &cpu.cpu_data[..],
|
|
||||||
style,
|
|
||||||
name: None,
|
|
||||||
}]
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Maybe hide load avg if too long? Or maybe the CPU part.
|
// TODO: Maybe hide load avg if too long? Or maybe the CPU part.
|
||||||
let title = if cfg!(target_family = "unix") {
|
let title = if cfg!(target_family = "unix") {
|
||||||
|
@ -24,7 +24,6 @@ impl Painter {
|
|||||||
};
|
};
|
||||||
let margined_draw_loc = TextTable {
|
let margined_draw_loc = TextTable {
|
||||||
table_gap: app_state.app_config_fields.table_gap,
|
table_gap: app_state.app_config_fields.table_gap,
|
||||||
table_height_offset: self.table_height_offset,
|
|
||||||
is_force_redraw: app_state.is_force_redraw,
|
is_force_redraw: app_state.is_force_redraw,
|
||||||
recalculate_column_widths,
|
recalculate_column_widths,
|
||||||
header_style: self.colours.table_header_style,
|
header_style: self.colours.table_header_style,
|
||||||
|
@ -24,7 +24,6 @@ impl Painter {
|
|||||||
};
|
};
|
||||||
let margined_draw_loc = TextTable {
|
let margined_draw_loc = TextTable {
|
||||||
table_gap: app_state.app_config_fields.table_gap,
|
table_gap: app_state.app_config_fields.table_gap,
|
||||||
table_height_offset: self.table_height_offset,
|
|
||||||
is_force_redraw: app_state.is_force_redraw,
|
is_force_redraw: app_state.is_force_redraw,
|
||||||
recalculate_column_widths,
|
recalculate_column_widths,
|
||||||
header_style: self.colours.table_header_style,
|
header_style: self.colours.table_header_style,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user