mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-26 23:24:20 +02:00
refactor: rip out trait system for drawing widgets
This rips out this weird trait system I previously used for drawing widgets, where I implemented a trait onto the Painter struct that did the drawing. I have no idea what I was thinking back then.
This commit is contained in:
parent
189be96622
commit
dd7e183ec8
@ -270,6 +270,10 @@ impl AppState {
|
|||||||
EventResult::NoRedraw
|
EventResult::NoRedraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BottomEvent::Resize {
|
||||||
|
width: _,
|
||||||
|
height: _,
|
||||||
|
} => EventResult::Redraw,
|
||||||
BottomEvent::Clean => {
|
BottomEvent::Clean => {
|
||||||
self.data_collection
|
self.data_collection
|
||||||
.clean_data(constants::STALE_MAX_MILLISECONDS);
|
.clean_data(constants::STALE_MAX_MILLISECONDS);
|
||||||
|
@ -233,6 +233,12 @@ fn main() -> Result<()> {
|
|||||||
try_drawing(&mut terminal, &mut app, &mut painter)?;
|
try_drawing(&mut terminal, &mut app, &mut painter)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BottomEvent::Resize {
|
||||||
|
width: _,
|
||||||
|
height: _,
|
||||||
|
} => {
|
||||||
|
try_drawing(&mut terminal, &mut app, &mut painter)?;
|
||||||
|
}
|
||||||
BottomEvent::Clean => {
|
BottomEvent::Clean => {
|
||||||
app.data_collection
|
app.data_collection
|
||||||
.clean_data(constants::STALE_MAX_MILLISECONDS);
|
.clean_data(constants::STALE_MAX_MILLISECONDS);
|
||||||
|
@ -95,8 +95,6 @@ impl FromStr for ColourScheme {
|
|||||||
/// Handles the canvas' state. TODO: [OPT] implement this.
|
/// Handles the canvas' state. TODO: [OPT] implement this.
|
||||||
pub struct Painter {
|
pub struct Painter {
|
||||||
pub colours: CanvasColours,
|
pub colours: CanvasColours,
|
||||||
height: u16,
|
|
||||||
width: u16,
|
|
||||||
styled_help_text: Vec<Spans<'static>>,
|
styled_help_text: Vec<Spans<'static>>,
|
||||||
is_mac_os: bool, // FIXME: This feels out of place...
|
is_mac_os: bool, // FIXME: This feels out of place...
|
||||||
row_constraints: Vec<Constraint>,
|
row_constraints: Vec<Constraint>,
|
||||||
@ -182,8 +180,6 @@ impl Painter {
|
|||||||
|
|
||||||
let mut painter = Painter {
|
let mut painter = Painter {
|
||||||
colours: CanvasColours::default(),
|
colours: CanvasColours::default(),
|
||||||
height: 0,
|
|
||||||
width: 0,
|
|
||||||
styled_help_text: Vec::default(),
|
styled_help_text: Vec::default(),
|
||||||
is_mac_os: cfg!(target_os = "macos"),
|
is_mac_os: cfg!(target_os = "macos"),
|
||||||
row_constraints,
|
row_constraints,
|
||||||
@ -313,36 +309,6 @@ impl Painter {
|
|||||||
let terminal_height = terminal_size.height;
|
let terminal_height = terminal_size.height;
|
||||||
let terminal_width = terminal_size.width;
|
let terminal_width = terminal_size.width;
|
||||||
|
|
||||||
if (self.height == 0 && self.width == 0)
|
|
||||||
|| (self.height != terminal_height || self.width != terminal_width)
|
|
||||||
{
|
|
||||||
app_state.is_force_redraw = true;
|
|
||||||
self.height = terminal_height;
|
|
||||||
self.width = terminal_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
if app_state.should_get_widget_bounds() {
|
|
||||||
// If we're force drawing, reset ALL mouse boundaries.
|
|
||||||
for widget in app_state.widget_map.values_mut() {
|
|
||||||
widget.top_left_corner = None;
|
|
||||||
widget.bottom_right_corner = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset dd_dialog...
|
|
||||||
app_state.delete_dialog_state.button_positions = vec![];
|
|
||||||
|
|
||||||
// Reset battery dialog...
|
|
||||||
for battery_widget in app_state.battery_state.widget_states.values_mut() {
|
|
||||||
battery_widget.tab_click_locs = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset column headers for sorting in process widget...
|
|
||||||
for proc_widget in app_state.proc_state.widget_states.values_mut() {
|
|
||||||
proc_widget.columns.column_header_y_loc = None;
|
|
||||||
proc_widget.columns.column_header_x_locs = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if app_state.help_dialog_state.is_showing_help {
|
if app_state.help_dialog_state.is_showing_help {
|
||||||
let gen_help_len = GENERAL_HELP_TEXT.len() as u16 + 3;
|
let gen_help_len = GENERAL_HELP_TEXT.len() as u16 + 3;
|
||||||
let border_len = terminal_height.saturating_sub(gen_help_len) / 2;
|
let border_len = terminal_height.saturating_sub(gen_help_len) / 2;
|
||||||
@ -461,39 +427,45 @@ impl Painter {
|
|||||||
.constraints([Constraint::Percentage(100)])
|
.constraints([Constraint::Percentage(100)])
|
||||||
.split(terminal_size);
|
.split(terminal_size);
|
||||||
match &app_state.current_widget.widget_type {
|
match &app_state.current_widget.widget_type {
|
||||||
Cpu => self.draw_cpu(
|
Cpu => draw_cpu(
|
||||||
|
self,
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
rect[0],
|
rect[0],
|
||||||
app_state.current_widget.widget_id,
|
app_state.current_widget.widget_id,
|
||||||
),
|
),
|
||||||
CpuLegend => self.draw_cpu(
|
CpuLegend => draw_cpu(
|
||||||
|
self,
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
rect[0],
|
rect[0],
|
||||||
app_state.current_widget.widget_id - 1,
|
app_state.current_widget.widget_id - 1,
|
||||||
),
|
),
|
||||||
Mem | BasicMem => self.draw_memory_graph(
|
Mem | BasicMem => draw_memory_graph(
|
||||||
|
self,
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
rect[0],
|
rect[0],
|
||||||
app_state.current_widget.widget_id,
|
app_state.current_widget.widget_id,
|
||||||
),
|
),
|
||||||
Disk => self.draw_disk_table(
|
Disk => draw_disk_table(
|
||||||
|
self,
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
rect[0],
|
rect[0],
|
||||||
true,
|
true,
|
||||||
app_state.current_widget.widget_id,
|
app_state.current_widget.widget_id,
|
||||||
),
|
),
|
||||||
Temp => self.draw_temp_table(
|
Temp => draw_temp_table(
|
||||||
|
self,
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
rect[0],
|
rect[0],
|
||||||
true,
|
true,
|
||||||
app_state.current_widget.widget_id,
|
app_state.current_widget.widget_id,
|
||||||
),
|
),
|
||||||
Net => self.draw_network_graph(
|
Net => draw_network_graph(
|
||||||
|
self,
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
rect[0],
|
rect[0],
|
||||||
@ -508,9 +480,10 @@ impl Painter {
|
|||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.draw_process_features(&mut f, app_state, rect[0], true, widget_id);
|
draw_process_features(self, &mut f, app_state, rect[0], true, widget_id);
|
||||||
}
|
}
|
||||||
Battery => self.draw_battery_display(
|
Battery => draw_battery_display(
|
||||||
|
self,
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
rect[0],
|
rect[0],
|
||||||
@ -555,16 +528,17 @@ impl Painter {
|
|||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||||
.split(vertical_chunks[1]);
|
.split(vertical_chunks[1]);
|
||||||
self.draw_basic_cpu(&mut f, app_state, vertical_chunks[0], 1);
|
draw_basic_cpu(self, &mut f, app_state, vertical_chunks[0], 1);
|
||||||
self.draw_basic_memory(&mut f, app_state, middle_chunks[0], 2);
|
draw_basic_memory(self, &mut f, app_state, middle_chunks[0], 2);
|
||||||
self.draw_basic_network(&mut f, app_state, middle_chunks[1], 3);
|
draw_basic_network(self, &mut f, app_state, middle_chunks[1], 3);
|
||||||
|
|
||||||
let mut later_widget_id: Option<u64> = None;
|
let mut later_widget_id: Option<u64> = None;
|
||||||
if let Some(basic_table_widget_state) = &app_state.basic_table_widget_state {
|
if let Some(basic_table_widget_state) = &app_state.basic_table_widget_state {
|
||||||
let widget_id = basic_table_widget_state.currently_displayed_widget_id;
|
let widget_id = basic_table_widget_state.currently_displayed_widget_id;
|
||||||
later_widget_id = Some(widget_id);
|
later_widget_id = Some(widget_id);
|
||||||
match basic_table_widget_state.currently_displayed_widget_type {
|
match basic_table_widget_state.currently_displayed_widget_type {
|
||||||
Disk => self.draw_disk_table(
|
Disk => draw_disk_table(
|
||||||
|
self,
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
vertical_chunks[3],
|
vertical_chunks[3],
|
||||||
@ -578,7 +552,8 @@ impl Painter {
|
|||||||
ProcSort => 2,
|
ProcSort => 2,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
self.draw_process_features(
|
draw_process_features(
|
||||||
|
self,
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
vertical_chunks[3],
|
vertical_chunks[3],
|
||||||
@ -586,14 +561,16 @@ impl Painter {
|
|||||||
wid,
|
wid,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Temp => self.draw_temp_table(
|
Temp => draw_temp_table(
|
||||||
|
self,
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
vertical_chunks[3],
|
vertical_chunks[3],
|
||||||
false,
|
false,
|
||||||
widget_id,
|
widget_id,
|
||||||
),
|
),
|
||||||
Battery => self.draw_battery_display(
|
Battery => draw_battery_display(
|
||||||
|
self,
|
||||||
&mut f,
|
&mut f,
|
||||||
app_state,
|
app_state,
|
||||||
vertical_chunks[3],
|
vertical_chunks[3],
|
||||||
@ -605,7 +582,7 @@ impl Painter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(widget_id) = later_widget_id {
|
if let Some(widget_id) = later_widget_id {
|
||||||
self.draw_basic_table_arrows(&mut f, app_state, vertical_chunks[2], widget_id);
|
draw_basic_table_arrows(self, &mut f, app_state, vertical_chunks[2], widget_id);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Draws using the passed in (or default) layout.
|
// Draws using the passed in (or default) layout.
|
||||||
@ -713,23 +690,25 @@ impl Painter {
|
|||||||
for (widget, widget_draw_loc) in widgets.children.iter().zip(widget_draw_locs) {
|
for (widget, widget_draw_loc) in widgets.children.iter().zip(widget_draw_locs) {
|
||||||
match &widget.widget_type {
|
match &widget.widget_type {
|
||||||
Empty => {}
|
Empty => {}
|
||||||
Cpu => self.draw_cpu(f, app_state, *widget_draw_loc, widget.widget_id),
|
Cpu => draw_cpu(self, f, app_state, *widget_draw_loc, widget.widget_id),
|
||||||
Mem => self.draw_memory_graph(f, app_state, *widget_draw_loc, widget.widget_id),
|
Mem => draw_memory_graph(self, f, app_state, *widget_draw_loc, widget.widget_id),
|
||||||
Net => self.draw_network(f, app_state, *widget_draw_loc, widget.widget_id),
|
Net => draw_network(self, f, app_state, *widget_draw_loc, widget.widget_id),
|
||||||
Temp => {
|
Temp => {
|
||||||
self.draw_temp_table(f, app_state, *widget_draw_loc, true, widget.widget_id)
|
draw_temp_table(self, f, app_state, *widget_draw_loc, true, widget.widget_id)
|
||||||
}
|
}
|
||||||
Disk => {
|
Disk => {
|
||||||
self.draw_disk_table(f, app_state, *widget_draw_loc, true, widget.widget_id)
|
draw_disk_table(self, f, app_state, *widget_draw_loc, true, widget.widget_id)
|
||||||
}
|
}
|
||||||
Proc => self.draw_process_features(
|
Proc => draw_process_features(
|
||||||
|
self,
|
||||||
f,
|
f,
|
||||||
app_state,
|
app_state,
|
||||||
*widget_draw_loc,
|
*widget_draw_loc,
|
||||||
true,
|
true,
|
||||||
widget.widget_id,
|
widget.widget_id,
|
||||||
),
|
),
|
||||||
Battery => self.draw_battery_display(
|
Battery => draw_battery_display(
|
||||||
|
self,
|
||||||
f,
|
f,
|
||||||
app_state,
|
app_state,
|
||||||
*widget_draw_loc,
|
*widget_draw_loc,
|
||||||
|
@ -10,14 +10,14 @@ pub mod network_graph;
|
|||||||
pub mod process_table;
|
pub mod process_table;
|
||||||
pub mod temp_table;
|
pub mod temp_table;
|
||||||
|
|
||||||
pub use basic_table_arrows::BasicTableArrows;
|
pub use basic_table_arrows::*;
|
||||||
pub use battery_display::BatteryDisplayWidget;
|
pub use battery_display::*;
|
||||||
pub use cpu_basic::CpuBasicWidget;
|
pub use cpu_basic::*;
|
||||||
pub use cpu_graph::CpuGraphWidget;
|
pub use cpu_graph::*;
|
||||||
pub use disk_table::DiskTableWidget;
|
pub use disk_table::*;
|
||||||
pub use mem_basic::MemBasicWidget;
|
pub use mem_basic::*;
|
||||||
pub use mem_graph::MemGraphWidget;
|
pub use mem_graph::*;
|
||||||
pub use network_basic::NetworkBasicWidget;
|
pub use network_basic::*;
|
||||||
pub use network_graph::NetworkGraphWidget;
|
pub use network_graph::*;
|
||||||
pub use process_table::ProcessTableWidget;
|
pub use process_table::*;
|
||||||
pub use temp_table::TempTableWidget;
|
pub use temp_table::*;
|
||||||
|
@ -12,154 +12,142 @@ use tui::{
|
|||||||
widgets::{Block, Paragraph},
|
widgets::{Block, Paragraph},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait BasicTableArrows {
|
pub fn draw_basic_table_arrows<B: Backend>(
|
||||||
fn draw_basic_table_arrows<B: Backend>(
|
painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect,
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
widget_id: u64,
|
||||||
);
|
) {
|
||||||
}
|
if let Some(current_table) = app_state.widget_map.get(&widget_id) {
|
||||||
|
let current_table = if let BottomWidgetType::ProcSort = current_table.widget_type {
|
||||||
|
current_table
|
||||||
|
.right_neighbour
|
||||||
|
.map(|id| app_state.widget_map.get(&id).unwrap())
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
current_table
|
||||||
|
};
|
||||||
|
|
||||||
impl BasicTableArrows for Painter {
|
let (left_table, right_table) = (
|
||||||
fn draw_basic_table_arrows<B: Backend>(
|
{
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
current_table
|
||||||
) {
|
.left_neighbour
|
||||||
if let Some(current_table) = app_state.widget_map.get(&widget_id) {
|
.map(|left_widget_id| {
|
||||||
let current_table = if let BottomWidgetType::ProcSort = current_table.widget_type {
|
app_state
|
||||||
|
.widget_map
|
||||||
|
.get(&left_widget_id)
|
||||||
|
.map(|left_widget| {
|
||||||
|
if left_widget.widget_type == BottomWidgetType::ProcSort {
|
||||||
|
left_widget
|
||||||
|
.left_neighbour
|
||||||
|
.map(|left_left_widget_id| {
|
||||||
|
app_state.widget_map.get(&left_left_widget_id).map(
|
||||||
|
|left_left_widget| &left_left_widget.widget_type,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or(Some(&BottomWidgetType::Temp))
|
||||||
|
.unwrap_or(&BottomWidgetType::Temp)
|
||||||
|
} else {
|
||||||
|
&left_widget.widget_type
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(&BottomWidgetType::Temp)
|
||||||
|
})
|
||||||
|
.unwrap_or(&BottomWidgetType::Temp)
|
||||||
|
},
|
||||||
|
{
|
||||||
current_table
|
current_table
|
||||||
.right_neighbour
|
.right_neighbour
|
||||||
.map(|id| app_state.widget_map.get(&id).unwrap())
|
.map(|right_widget_id| {
|
||||||
.unwrap()
|
app_state
|
||||||
} else {
|
.widget_map
|
||||||
current_table
|
.get(&right_widget_id)
|
||||||
};
|
.map(|right_widget| {
|
||||||
|
if right_widget.widget_type == BottomWidgetType::ProcSort {
|
||||||
|
right_widget
|
||||||
|
.right_neighbour
|
||||||
|
.map(|right_right_widget_id| {
|
||||||
|
app_state.widget_map.get(&right_right_widget_id).map(
|
||||||
|
|right_right_widget| {
|
||||||
|
&right_right_widget.widget_type
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or(Some(&BottomWidgetType::Disk))
|
||||||
|
.unwrap_or(&BottomWidgetType::Disk)
|
||||||
|
} else {
|
||||||
|
&right_widget.widget_type
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(&BottomWidgetType::Disk)
|
||||||
|
})
|
||||||
|
.unwrap_or(&BottomWidgetType::Disk)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let (left_table, right_table) = (
|
let left_name = left_table.get_pretty_name();
|
||||||
{
|
let right_name = right_table.get_pretty_name();
|
||||||
current_table
|
|
||||||
.left_neighbour
|
|
||||||
.map(|left_widget_id| {
|
|
||||||
app_state
|
|
||||||
.widget_map
|
|
||||||
.get(&left_widget_id)
|
|
||||||
.map(|left_widget| {
|
|
||||||
if left_widget.widget_type == BottomWidgetType::ProcSort {
|
|
||||||
left_widget
|
|
||||||
.left_neighbour
|
|
||||||
.map(|left_left_widget_id| {
|
|
||||||
app_state.widget_map.get(&left_left_widget_id).map(
|
|
||||||
|left_left_widget| {
|
|
||||||
&left_left_widget.widget_type
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or(Some(&BottomWidgetType::Temp))
|
|
||||||
.unwrap_or(&BottomWidgetType::Temp)
|
|
||||||
} else {
|
|
||||||
&left_widget.widget_type
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or(&BottomWidgetType::Temp)
|
|
||||||
})
|
|
||||||
.unwrap_or(&BottomWidgetType::Temp)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
current_table
|
|
||||||
.right_neighbour
|
|
||||||
.map(|right_widget_id| {
|
|
||||||
app_state
|
|
||||||
.widget_map
|
|
||||||
.get(&right_widget_id)
|
|
||||||
.map(|right_widget| {
|
|
||||||
if right_widget.widget_type == BottomWidgetType::ProcSort {
|
|
||||||
right_widget
|
|
||||||
.right_neighbour
|
|
||||||
.map(|right_right_widget_id| {
|
|
||||||
app_state
|
|
||||||
.widget_map
|
|
||||||
.get(&right_right_widget_id)
|
|
||||||
.map(|right_right_widget| {
|
|
||||||
&right_right_widget.widget_type
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or(Some(&BottomWidgetType::Disk))
|
|
||||||
.unwrap_or(&BottomWidgetType::Disk)
|
|
||||||
} else {
|
|
||||||
&right_widget.widget_type
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or(&BottomWidgetType::Disk)
|
|
||||||
})
|
|
||||||
.unwrap_or(&BottomWidgetType::Disk)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let left_name = left_table.get_pretty_name();
|
let num_spaces =
|
||||||
let right_name = right_table.get_pretty_name();
|
usize::from(draw_loc.width).saturating_sub(6 + left_name.len() + right_name.len());
|
||||||
|
|
||||||
let num_spaces =
|
let left_arrow_text = vec![
|
||||||
usize::from(draw_loc.width).saturating_sub(6 + left_name.len() + right_name.len());
|
Spans::default(),
|
||||||
|
Spans::from(Span::styled(
|
||||||
|
format!("◄ {}", left_name),
|
||||||
|
painter.colours.text_style,
|
||||||
|
)),
|
||||||
|
];
|
||||||
|
|
||||||
let left_arrow_text = vec![
|
let right_arrow_text = vec![
|
||||||
Spans::default(),
|
Spans::default(),
|
||||||
Spans::from(Span::styled(
|
Spans::from(Span::styled(
|
||||||
format!("◄ {}", left_name),
|
format!("{} ►", right_name),
|
||||||
self.colours.text_style,
|
painter.colours.text_style,
|
||||||
)),
|
)),
|
||||||
];
|
];
|
||||||
|
|
||||||
let right_arrow_text = vec![
|
let margined_draw_loc = Layout::default()
|
||||||
Spans::default(),
|
.direction(Direction::Horizontal)
|
||||||
Spans::from(Span::styled(
|
.constraints([
|
||||||
format!("{} ►", right_name),
|
Constraint::Length(2 + left_name.len() as u16),
|
||||||
self.colours.text_style,
|
Constraint::Length(num_spaces as u16),
|
||||||
)),
|
Constraint::Length(2 + right_name.len() as u16),
|
||||||
];
|
])
|
||||||
|
.horizontal_margin(1)
|
||||||
|
.split(draw_loc);
|
||||||
|
|
||||||
let margined_draw_loc = Layout::default()
|
f.render_widget(
|
||||||
.direction(Direction::Horizontal)
|
Paragraph::new(left_arrow_text).block(Block::default()),
|
||||||
.constraints([
|
margined_draw_loc[0],
|
||||||
Constraint::Length(2 + left_name.len() as u16),
|
);
|
||||||
Constraint::Length(num_spaces as u16),
|
f.render_widget(
|
||||||
Constraint::Length(2 + right_name.len() as u16),
|
Paragraph::new(right_arrow_text)
|
||||||
])
|
.block(Block::default())
|
||||||
.horizontal_margin(1)
|
.alignment(Alignment::Right),
|
||||||
.split(draw_loc);
|
margined_draw_loc[2],
|
||||||
|
);
|
||||||
|
|
||||||
f.render_widget(
|
if app_state.should_get_widget_bounds() {
|
||||||
Paragraph::new(left_arrow_text).block(Block::default()),
|
// Some explanations for future readers:
|
||||||
margined_draw_loc[0],
|
// - The "height" as of writing of this entire widget is 2. If it's 1, it occasionally doesn't draw.
|
||||||
);
|
// - As such, the buttons are only on the lower part of this 2-high widget.
|
||||||
f.render_widget(
|
// - So, we want to only check at one location, the `draw_loc.y + 1`, and that's it.
|
||||||
Paragraph::new(right_arrow_text)
|
// - But why is it "+2" then? Well, it's because I have a REALLY ugly hack
|
||||||
.block(Block::default())
|
// for mouse button checking, since most button checks are of the form `(draw_loc.y + draw_loc.height)`,
|
||||||
.alignment(Alignment::Right),
|
// and the same for the x and width. Unfortunately, if you check using >= and <=, the outer bound is
|
||||||
margined_draw_loc[2],
|
// actually too large - so, we assume all of them are one too big and check via < (see
|
||||||
);
|
// https://github.com/ClementTsang/bottom/pull/459 for details).
|
||||||
|
// - So in other words, to make it simple, we keep this to a standard and overshoot by one here.
|
||||||
if app_state.should_get_widget_bounds() {
|
if let Some(basic_table) = &mut app_state.basic_table_widget_state {
|
||||||
// Some explanations for future readers:
|
basic_table.left_tlc = Some((margined_draw_loc[0].x, margined_draw_loc[0].y + 1));
|
||||||
// - The "height" as of writing of this entire widget is 2. If it's 1, it occasionally doesn't draw.
|
basic_table.left_brc = Some((
|
||||||
// - As such, the buttons are only on the lower part of this 2-high widget.
|
margined_draw_loc[0].x + margined_draw_loc[0].width,
|
||||||
// - So, we want to only check at one location, the `draw_loc.y + 1`, and that's it.
|
margined_draw_loc[0].y + 2,
|
||||||
// - But why is it "+2" then? Well, it's because I have a REALLY ugly hack
|
));
|
||||||
// for mouse button checking, since most button checks are of the form `(draw_loc.y + draw_loc.height)`,
|
basic_table.right_tlc = Some((margined_draw_loc[2].x, margined_draw_loc[2].y + 1));
|
||||||
// and the same for the x and width. Unfortunately, if you check using >= and <=, the outer bound is
|
basic_table.right_brc = Some((
|
||||||
// actually too large - so, we assume all of them are one too big and check via < (see
|
margined_draw_loc[2].x + margined_draw_loc[2].width,
|
||||||
// https://github.com/ClementTsang/bottom/pull/459 for details).
|
margined_draw_loc[2].y + 2,
|
||||||
// - So in other words, to make it simple, we keep this to a standard and overshoot by one here.
|
));
|
||||||
if let Some(basic_table) = &mut app_state.basic_table_widget_state {
|
|
||||||
basic_table.left_tlc =
|
|
||||||
Some((margined_draw_loc[0].x, margined_draw_loc[0].y + 1));
|
|
||||||
basic_table.left_brc = Some((
|
|
||||||
margined_draw_loc[0].x + margined_draw_loc[0].width,
|
|
||||||
margined_draw_loc[0].y + 2,
|
|
||||||
));
|
|
||||||
basic_table.right_tlc =
|
|
||||||
Some((margined_draw_loc[2].x, margined_draw_loc[2].y + 1));
|
|
||||||
basic_table.right_brc = Some((
|
|
||||||
margined_draw_loc[2].x + margined_draw_loc[2].width,
|
|
||||||
margined_draw_loc[2].y + 2,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,198 +13,184 @@ use tui::{
|
|||||||
};
|
};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
pub trait BatteryDisplayWidget {
|
pub fn draw_battery_display<B: Backend>(
|
||||||
fn draw_battery_display<B: Backend>(
|
painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect,
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, draw_border: bool,
|
draw_border: bool, widget_id: u64,
|
||||||
widget_id: u64,
|
) {
|
||||||
);
|
let should_get_widget_bounds = app_state.should_get_widget_bounds();
|
||||||
}
|
if let Some(battery_widget_state) = app_state.battery_state.widget_states.get_mut(&widget_id) {
|
||||||
|
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||||
|
let border_style = if is_on_widget {
|
||||||
|
painter.colours.highlighted_border_style
|
||||||
|
} else {
|
||||||
|
painter.colours.border_style
|
||||||
|
};
|
||||||
|
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
app_state.app_config_fields.table_gap
|
||||||
|
};
|
||||||
|
|
||||||
impl BatteryDisplayWidget for Painter {
|
let title = if app_state.is_expanded {
|
||||||
fn draw_battery_display<B: Backend>(
|
const TITLE_BASE: &str = " Battery ── Esc to go back ";
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, draw_border: bool,
|
Spans::from(vec![
|
||||||
widget_id: u64,
|
Span::styled(" Battery ".to_string(), painter.colours.widget_title_style),
|
||||||
) {
|
Span::styled(
|
||||||
let should_get_widget_bounds = app_state.should_get_widget_bounds();
|
format!(
|
||||||
if let Some(battery_widget_state) =
|
"─{}─ Esc to go back ",
|
||||||
app_state.battery_state.widget_states.get_mut(&widget_id)
|
"─".repeat(usize::from(draw_loc.width).saturating_sub(
|
||||||
{
|
UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
|
||||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
))
|
||||||
let border_style = if is_on_widget {
|
|
||||||
self.colours.highlighted_border_style
|
|
||||||
} else {
|
|
||||||
self.colours.border_style
|
|
||||||
};
|
|
||||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
app_state.app_config_fields.table_gap
|
|
||||||
};
|
|
||||||
|
|
||||||
let title = if app_state.is_expanded {
|
|
||||||
const TITLE_BASE: &str = " Battery ── Esc to go back ";
|
|
||||||
Spans::from(vec![
|
|
||||||
Span::styled(" Battery ".to_string(), self.colours.widget_title_style),
|
|
||||||
Span::styled(
|
|
||||||
format!(
|
|
||||||
"─{}─ Esc to go back ",
|
|
||||||
"─".repeat(usize::from(draw_loc.width).saturating_sub(
|
|
||||||
UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
|
|
||||||
))
|
|
||||||
),
|
|
||||||
border_style,
|
|
||||||
),
|
),
|
||||||
])
|
border_style,
|
||||||
} else {
|
),
|
||||||
Spans::from(Span::styled(
|
])
|
||||||
" Battery ".to_string(),
|
} else {
|
||||||
self.colours.widget_title_style,
|
Spans::from(Span::styled(
|
||||||
))
|
" Battery ".to_string(),
|
||||||
};
|
painter.colours.widget_title_style,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
let battery_block = if draw_border {
|
let battery_block = if draw_border {
|
||||||
Block::default()
|
Block::default()
|
||||||
.title(title)
|
.title(title)
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(border_style)
|
.border_style(border_style)
|
||||||
} else if is_on_widget {
|
} else if is_on_widget {
|
||||||
Block::default()
|
Block::default()
|
||||||
.borders(*SIDE_BORDERS)
|
.borders(*SIDE_BORDERS)
|
||||||
.border_style(self.colours.highlighted_border_style)
|
.border_style(painter.colours.highlighted_border_style)
|
||||||
} else {
|
} else {
|
||||||
Block::default().borders(Borders::NONE)
|
Block::default().borders(Borders::NONE)
|
||||||
};
|
};
|
||||||
|
|
||||||
let battery_names = app_state
|
let battery_names = app_state
|
||||||
.canvas_data
|
.canvas_data
|
||||||
.battery_data
|
.battery_data
|
||||||
.iter()
|
.iter()
|
||||||
.map(|battery| &battery.battery_name)
|
.map(|battery| &battery.battery_name)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let tab_draw_loc = Layout::default()
|
let tab_draw_loc = Layout::default()
|
||||||
.constraints([
|
.constraints([
|
||||||
Constraint::Length(1),
|
Constraint::Length(1),
|
||||||
Constraint::Length(2),
|
Constraint::Length(2),
|
||||||
Constraint::Min(0),
|
Constraint::Min(0),
|
||||||
])
|
])
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.split(draw_loc)[1];
|
.split(draw_loc)[1];
|
||||||
|
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Tabs::new(
|
Tabs::new(
|
||||||
battery_names
|
battery_names
|
||||||
.iter()
|
.iter()
|
||||||
.map(|name| Spans::from((*name).clone()))
|
.map(|name| Spans::from((*name).clone()))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
)
|
)
|
||||||
.block(Block::default())
|
.block(Block::default())
|
||||||
.divider(tui::symbols::line::VERTICAL)
|
.divider(tui::symbols::line::VERTICAL)
|
||||||
.style(self.colours.text_style)
|
.style(painter.colours.text_style)
|
||||||
.highlight_style(self.colours.currently_selected_text_style)
|
.highlight_style(painter.colours.currently_selected_text_style)
|
||||||
.select(battery_widget_state.currently_selected_battery_index),
|
.select(battery_widget_state.currently_selected_battery_index),
|
||||||
tab_draw_loc,
|
tab_draw_loc,
|
||||||
|
);
|
||||||
|
|
||||||
|
let margined_draw_loc = Layout::default()
|
||||||
|
.constraints([Constraint::Percentage(100)])
|
||||||
|
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.split(draw_loc)[0];
|
||||||
|
|
||||||
|
if let Some(battery_details) = app_state
|
||||||
|
.canvas_data
|
||||||
|
.battery_data
|
||||||
|
.get(battery_widget_state.currently_selected_battery_index)
|
||||||
|
{
|
||||||
|
// Assuming a 50/50 split in width
|
||||||
|
let bar_length = usize::from((draw_loc.width.saturating_sub(2) / 2).saturating_sub(8));
|
||||||
|
let charge_percentage = battery_details.charge_percentage;
|
||||||
|
let num_bars = calculate_basic_use_bars(charge_percentage, bar_length);
|
||||||
|
let bars = format!(
|
||||||
|
"[{}{}{:3.0}%]",
|
||||||
|
"|".repeat(num_bars),
|
||||||
|
" ".repeat(bar_length - num_bars),
|
||||||
|
charge_percentage,
|
||||||
);
|
);
|
||||||
|
|
||||||
let margined_draw_loc = Layout::default()
|
let battery_rows = vec![
|
||||||
.constraints([Constraint::Percentage(100)])
|
Row::new(vec![
|
||||||
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
Cell::from("Charge %").style(painter.colours.text_style),
|
||||||
.direction(Direction::Horizontal)
|
Cell::from(bars).style(if charge_percentage < 10.0 {
|
||||||
.split(draw_loc)[0];
|
painter.colours.low_battery_colour
|
||||||
|
} else if charge_percentage < 50.0 {
|
||||||
if let Some(battery_details) = app_state
|
painter.colours.medium_battery_colour
|
||||||
.canvas_data
|
|
||||||
.battery_data
|
|
||||||
.get(battery_widget_state.currently_selected_battery_index)
|
|
||||||
{
|
|
||||||
// Assuming a 50/50 split in width
|
|
||||||
let bar_length =
|
|
||||||
usize::from((draw_loc.width.saturating_sub(2) / 2).saturating_sub(8));
|
|
||||||
let charge_percentage = battery_details.charge_percentage;
|
|
||||||
let num_bars = calculate_basic_use_bars(charge_percentage, bar_length);
|
|
||||||
let bars = format!(
|
|
||||||
"[{}{}{:3.0}%]",
|
|
||||||
"|".repeat(num_bars),
|
|
||||||
" ".repeat(bar_length - num_bars),
|
|
||||||
charge_percentage,
|
|
||||||
);
|
|
||||||
|
|
||||||
let battery_rows = vec![
|
|
||||||
Row::new(vec![
|
|
||||||
Cell::from("Charge %").style(self.colours.text_style),
|
|
||||||
Cell::from(bars).style(if charge_percentage < 10.0 {
|
|
||||||
self.colours.low_battery_colour
|
|
||||||
} else if charge_percentage < 50.0 {
|
|
||||||
self.colours.medium_battery_colour
|
|
||||||
} else {
|
|
||||||
self.colours.high_battery_colour
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
Row::new(vec!["Consumption", &battery_details.watt_consumption])
|
|
||||||
.style(self.colours.text_style),
|
|
||||||
if let Some(duration_until_full) = &battery_details.duration_until_full {
|
|
||||||
Row::new(vec!["Time to full", duration_until_full])
|
|
||||||
.style(self.colours.text_style)
|
|
||||||
} else if let Some(duration_until_empty) = &battery_details.duration_until_empty
|
|
||||||
{
|
|
||||||
Row::new(vec!["Time to empty", duration_until_empty])
|
|
||||||
.style(self.colours.text_style)
|
|
||||||
} else {
|
} else {
|
||||||
Row::new(vec!["Time to full/empty", "N/A"]).style(self.colours.text_style)
|
painter.colours.high_battery_colour
|
||||||
},
|
}),
|
||||||
Row::new(vec!["Health %", &battery_details.health])
|
]),
|
||||||
.style(self.colours.text_style),
|
Row::new(vec!["Consumption", &battery_details.watt_consumption])
|
||||||
];
|
.style(painter.colours.text_style),
|
||||||
|
if let Some(duration_until_full) = &battery_details.duration_until_full {
|
||||||
|
Row::new(vec!["Time to full", duration_until_full])
|
||||||
|
.style(painter.colours.text_style)
|
||||||
|
} else if let Some(duration_until_empty) = &battery_details.duration_until_empty {
|
||||||
|
Row::new(vec!["Time to empty", duration_until_empty])
|
||||||
|
.style(painter.colours.text_style)
|
||||||
|
} else {
|
||||||
|
Row::new(vec!["Time to full/empty", "N/A"]).style(painter.colours.text_style)
|
||||||
|
},
|
||||||
|
Row::new(vec!["Health %", &battery_details.health])
|
||||||
|
.style(painter.colours.text_style),
|
||||||
|
];
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Table::new(battery_rows)
|
Table::new(battery_rows)
|
||||||
.block(battery_block)
|
.block(battery_block)
|
||||||
.header(Row::new(vec![""]).bottom_margin(table_gap))
|
.header(Row::new(vec![""]).bottom_margin(table_gap))
|
||||||
.widths(&[Constraint::Percentage(50), Constraint::Percentage(50)]),
|
.widths(&[Constraint::Percentage(50), Constraint::Percentage(50)]),
|
||||||
margined_draw_loc,
|
margined_draw_loc,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let mut contents = vec![Spans::default(); table_gap as usize];
|
let mut contents = vec![Spans::default(); table_gap as usize];
|
||||||
|
|
||||||
contents.push(Spans::from(Span::styled(
|
contents.push(Spans::from(Span::styled(
|
||||||
"No data found for this battery",
|
"No data found for this battery",
|
||||||
self.colours.text_style,
|
painter.colours.text_style,
|
||||||
)));
|
)));
|
||||||
|
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Paragraph::new(contents).block(battery_block),
|
Paragraph::new(contents).block(battery_block),
|
||||||
margined_draw_loc,
|
margined_draw_loc,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if should_get_widget_bounds {
|
||||||
|
// Tab wizardry
|
||||||
|
if !battery_names.is_empty() {
|
||||||
|
let mut current_x = tab_draw_loc.x;
|
||||||
|
let current_y = tab_draw_loc.y;
|
||||||
|
let mut tab_click_locs: Vec<((u16, u16), (u16, u16))> = vec![];
|
||||||
|
for battery in battery_names {
|
||||||
|
// +1 because there's a space after the tab label.
|
||||||
|
let width = unicode_width::UnicodeWidthStr::width(battery.as_str()) as u16;
|
||||||
|
tab_click_locs.push(((current_x, current_y), (current_x + width, current_y)));
|
||||||
|
|
||||||
|
// +4 because we want to go one space, then one space past to get to the '|', then 2 more
|
||||||
|
// to start at the blank space before the tab label.
|
||||||
|
current_x += width + 4;
|
||||||
|
}
|
||||||
|
battery_widget_state.tab_click_locs = Some(tab_click_locs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if should_get_widget_bounds {
|
// Update draw loc in widget map
|
||||||
// Tab wizardry
|
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||||
if !battery_names.is_empty() {
|
widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y));
|
||||||
let mut current_x = tab_draw_loc.x;
|
widget.bottom_right_corner = Some((
|
||||||
let current_y = tab_draw_loc.y;
|
margined_draw_loc.x + margined_draw_loc.width,
|
||||||
let mut tab_click_locs: Vec<((u16, u16), (u16, u16))> = vec![];
|
margined_draw_loc.y + margined_draw_loc.height,
|
||||||
for battery in battery_names {
|
));
|
||||||
// +1 because there's a space after the tab label.
|
|
||||||
let width = unicode_width::UnicodeWidthStr::width(battery.as_str()) as u16;
|
|
||||||
tab_click_locs
|
|
||||||
.push(((current_x, current_y), (current_x + width, current_y)));
|
|
||||||
|
|
||||||
// +4 because we want to go one space, then one space past to get to the '|', then 2 more
|
|
||||||
// to start at the blank space before the tab label.
|
|
||||||
current_x += width + 4;
|
|
||||||
}
|
|
||||||
battery_widget_state.tab_click_locs = Some(tab_click_locs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update draw loc in widget map
|
|
||||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
|
||||||
widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y));
|
|
||||||
widget.bottom_right_corner = Some((
|
|
||||||
margined_draw_loc.x + margined_draw_loc.width,
|
|
||||||
margined_draw_loc.y + margined_draw_loc.height,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,192 +15,185 @@ use tui::{
|
|||||||
widgets::{Block, Paragraph},
|
widgets::{Block, Paragraph},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait CpuBasicWidget {
|
pub fn draw_basic_cpu<B: Backend>(
|
||||||
fn draw_basic_cpu<B: Backend>(
|
painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect,
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
widget_id: u64,
|
||||||
);
|
) {
|
||||||
}
|
// Skip the first element, it's the "all" element
|
||||||
|
if app_state.canvas_data.cpu_data.len() > 1 {
|
||||||
|
let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data[1..];
|
||||||
|
|
||||||
impl CpuBasicWidget for Painter {
|
// This is a bit complicated, but basically, we want to draw SOME number
|
||||||
fn draw_basic_cpu<B: Backend>(
|
// of columns to draw all CPUs. Ideally, as well, we want to not have
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
// to ever scroll.
|
||||||
) {
|
// **General logic** - count number of elements in cpu_data. Then see how
|
||||||
// Skip the first element, it's the "all" element
|
// many rows and columns we have in draw_loc (-2 on both sides for border?).
|
||||||
if app_state.canvas_data.cpu_data.len() > 1 {
|
// I think what we can do is try to fit in as many in one column as possible.
|
||||||
let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data[1..];
|
// If not, then add a new column.
|
||||||
|
// Then, from this, split the row space across ALL columns. From there, generate
|
||||||
|
// the desired lengths.
|
||||||
|
|
||||||
// This is a bit complicated, but basically, we want to draw SOME number
|
if app_state.current_widget.widget_id == widget_id {
|
||||||
// of columns to draw all CPUs. Ideally, as well, we want to not have
|
f.render_widget(
|
||||||
// to ever scroll.
|
Block::default()
|
||||||
// **General logic** - count number of elements in cpu_data. Then see how
|
.borders(*SIDE_BORDERS)
|
||||||
// many rows and columns we have in draw_loc (-2 on both sides for border?).
|
.border_style(painter.colours.highlighted_border_style),
|
||||||
// I think what we can do is try to fit in as many in one column as possible.
|
draw_loc,
|
||||||
// If not, then add a new column.
|
);
|
||||||
// Then, from this, split the row space across ALL columns. From there, generate
|
}
|
||||||
// the desired lengths.
|
|
||||||
|
|
||||||
if app_state.current_widget.widget_id == widget_id {
|
let num_cpus = cpu_data.len();
|
||||||
f.render_widget(
|
let show_avg_cpu = app_state.app_config_fields.show_average_cpu;
|
||||||
Block::default()
|
|
||||||
.borders(*SIDE_BORDERS)
|
|
||||||
.border_style(self.colours.highlighted_border_style),
|
|
||||||
draw_loc,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let num_cpus = cpu_data.len();
|
if draw_loc.height > 0 {
|
||||||
let show_avg_cpu = app_state.app_config_fields.show_average_cpu;
|
let remaining_height = usize::from(draw_loc.height);
|
||||||
|
const REQUIRED_COLUMNS: usize = 4;
|
||||||
|
|
||||||
if draw_loc.height > 0 {
|
let chunk_vec =
|
||||||
let remaining_height = usize::from(draw_loc.height);
|
vec![Constraint::Percentage((100 / REQUIRED_COLUMNS) as u16); REQUIRED_COLUMNS];
|
||||||
const REQUIRED_COLUMNS: usize = 4;
|
let chunks = Layout::default()
|
||||||
|
.constraints(chunk_vec)
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.split(draw_loc);
|
||||||
|
|
||||||
let chunk_vec =
|
const CPU_NAME_SPACE: usize = 3;
|
||||||
vec![Constraint::Percentage((100 / REQUIRED_COLUMNS) as u16); REQUIRED_COLUMNS];
|
const BAR_BOUND_SPACE: usize = 2;
|
||||||
let chunks = Layout::default()
|
const PERCENTAGE_SPACE: usize = 4;
|
||||||
.constraints(chunk_vec)
|
const MARGIN_SPACE: usize = 2;
|
||||||
.direction(Direction::Horizontal)
|
|
||||||
.split(draw_loc);
|
|
||||||
|
|
||||||
const CPU_NAME_SPACE: usize = 3;
|
const COMBINED_SPACING: usize =
|
||||||
const BAR_BOUND_SPACE: usize = 2;
|
CPU_NAME_SPACE + BAR_BOUND_SPACE + PERCENTAGE_SPACE + MARGIN_SPACE;
|
||||||
const PERCENTAGE_SPACE: usize = 4;
|
const REDUCED_SPACING: usize = CPU_NAME_SPACE + PERCENTAGE_SPACE + MARGIN_SPACE;
|
||||||
const MARGIN_SPACE: usize = 2;
|
let chunk_width = chunks[0].width as usize;
|
||||||
|
|
||||||
const COMBINED_SPACING: usize =
|
// Inspired by htop.
|
||||||
CPU_NAME_SPACE + BAR_BOUND_SPACE + PERCENTAGE_SPACE + MARGIN_SPACE;
|
// We do +4 as if it's too few bars in the bar length, it's kinda pointless.
|
||||||
const REDUCED_SPACING: usize = CPU_NAME_SPACE + PERCENTAGE_SPACE + MARGIN_SPACE;
|
let cpu_bars = if chunk_width >= COMBINED_SPACING + 4 {
|
||||||
let chunk_width = chunks[0].width as usize;
|
let bar_length = chunk_width - COMBINED_SPACING;
|
||||||
|
(0..num_cpus)
|
||||||
|
.map(|cpu_index| {
|
||||||
|
let use_percentage =
|
||||||
|
if let Some(cpu_usage) = cpu_data[cpu_index].cpu_data.last() {
|
||||||
|
cpu_usage.1
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
// Inspired by htop.
|
let num_bars = calculate_basic_use_bars(use_percentage, bar_length);
|
||||||
// We do +4 as if it's too few bars in the bar length, it's kinda pointless.
|
format!(
|
||||||
let cpu_bars = if chunk_width >= COMBINED_SPACING + 4 {
|
"{:3}[{}{}{:3.0}%]",
|
||||||
let bar_length = chunk_width - COMBINED_SPACING;
|
if app_state.app_config_fields.show_average_cpu {
|
||||||
(0..num_cpus)
|
if cpu_index == 0 {
|
||||||
.map(|cpu_index| {
|
"AVG".to_string()
|
||||||
let use_percentage =
|
|
||||||
if let Some(cpu_usage) = cpu_data[cpu_index].cpu_data.last() {
|
|
||||||
cpu_usage.1
|
|
||||||
} else {
|
} else {
|
||||||
0.0
|
(cpu_index - 1).to_string()
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
|
cpu_index.to_string()
|
||||||
|
},
|
||||||
|
"|".repeat(num_bars),
|
||||||
|
" ".repeat(bar_length - num_bars),
|
||||||
|
use_percentage.round(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
} else if chunk_width >= REDUCED_SPACING {
|
||||||
|
(0..num_cpus)
|
||||||
|
.map(|cpu_index| {
|
||||||
|
let use_percentage =
|
||||||
|
if let Some(cpu_usage) = cpu_data[cpu_index].cpu_data.last() {
|
||||||
|
cpu_usage.1
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
let num_bars = calculate_basic_use_bars(use_percentage, bar_length);
|
format!(
|
||||||
format!(
|
"{:3} {:3.0}%",
|
||||||
"{:3}[{}{}{:3.0}%]",
|
if app_state.app_config_fields.show_average_cpu {
|
||||||
if app_state.app_config_fields.show_average_cpu {
|
if cpu_index == 0 {
|
||||||
if cpu_index == 0 {
|
"AVG".to_string()
|
||||||
"AVG".to_string()
|
} else {
|
||||||
|
(cpu_index - 1).to_string()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cpu_index.to_string()
|
||||||
|
},
|
||||||
|
use_percentage.round(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
} else {
|
||||||
|
(0..num_cpus)
|
||||||
|
.map(|cpu_index| {
|
||||||
|
let use_percentage =
|
||||||
|
if let Some(cpu_usage) = cpu_data[cpu_index].cpu_data.last() {
|
||||||
|
cpu_usage.1
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
|
format!("{:3.0}%", use_percentage.round(),)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut row_counter = num_cpus;
|
||||||
|
let mut start_index = 0;
|
||||||
|
for (itx, chunk) in chunks.iter().enumerate() {
|
||||||
|
// Explicitly check... don't want an accidental DBZ or underflow, this ensures
|
||||||
|
// to_divide is > 0
|
||||||
|
if REQUIRED_COLUMNS > itx {
|
||||||
|
let to_divide = REQUIRED_COLUMNS - itx;
|
||||||
|
let how_many_cpus = min(
|
||||||
|
remaining_height,
|
||||||
|
(row_counter / to_divide)
|
||||||
|
+ (if row_counter % to_divide == 0 { 0 } else { 1 }),
|
||||||
|
);
|
||||||
|
row_counter -= how_many_cpus;
|
||||||
|
let end_index = min(start_index + how_many_cpus, num_cpus);
|
||||||
|
|
||||||
|
let cpu_column = (start_index..end_index)
|
||||||
|
.map(|itx| {
|
||||||
|
Spans::from(Span {
|
||||||
|
content: (&cpu_bars[itx]).into(),
|
||||||
|
style: if show_avg_cpu {
|
||||||
|
if itx == 0 {
|
||||||
|
painter.colours.avg_colour_style
|
||||||
} else {
|
} else {
|
||||||
(cpu_index - 1).to_string()
|
painter.colours.cpu_colour_styles
|
||||||
|
[(itx - 1) % painter.colours.cpu_colour_styles.len()]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cpu_index.to_string()
|
painter.colours.cpu_colour_styles
|
||||||
|
[itx % painter.colours.cpu_colour_styles.len()]
|
||||||
},
|
},
|
||||||
"|".repeat(num_bars),
|
|
||||||
" ".repeat(bar_length - num_bars),
|
|
||||||
use_percentage.round(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
} else if chunk_width >= REDUCED_SPACING {
|
|
||||||
(0..num_cpus)
|
|
||||||
.map(|cpu_index| {
|
|
||||||
let use_percentage =
|
|
||||||
if let Some(cpu_usage) = cpu_data[cpu_index].cpu_data.last() {
|
|
||||||
cpu_usage.1
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
|
|
||||||
format!(
|
|
||||||
"{:3} {:3.0}%",
|
|
||||||
if app_state.app_config_fields.show_average_cpu {
|
|
||||||
if cpu_index == 0 {
|
|
||||||
"AVG".to_string()
|
|
||||||
} else {
|
|
||||||
(cpu_index - 1).to_string()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cpu_index.to_string()
|
|
||||||
},
|
|
||||||
use_percentage.round(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
} else {
|
|
||||||
(0..num_cpus)
|
|
||||||
.map(|cpu_index| {
|
|
||||||
let use_percentage =
|
|
||||||
if let Some(cpu_usage) = cpu_data[cpu_index].cpu_data.last() {
|
|
||||||
cpu_usage.1
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
|
|
||||||
format!("{:3.0}%", use_percentage.round(),)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut row_counter = num_cpus;
|
|
||||||
let mut start_index = 0;
|
|
||||||
for (itx, chunk) in chunks.iter().enumerate() {
|
|
||||||
// Explicitly check... don't want an accidental DBZ or underflow, this ensures
|
|
||||||
// to_divide is > 0
|
|
||||||
if REQUIRED_COLUMNS > itx {
|
|
||||||
let to_divide = REQUIRED_COLUMNS - itx;
|
|
||||||
let how_many_cpus = min(
|
|
||||||
remaining_height,
|
|
||||||
(row_counter / to_divide)
|
|
||||||
+ (if row_counter % to_divide == 0 { 0 } else { 1 }),
|
|
||||||
);
|
|
||||||
row_counter -= how_many_cpus;
|
|
||||||
let end_index = min(start_index + how_many_cpus, num_cpus);
|
|
||||||
|
|
||||||
let cpu_column = (start_index..end_index)
|
|
||||||
.map(|itx| {
|
|
||||||
Spans::from(Span {
|
|
||||||
content: (&cpu_bars[itx]).into(),
|
|
||||||
style: if show_avg_cpu {
|
|
||||||
if itx == 0 {
|
|
||||||
self.colours.avg_colour_style
|
|
||||||
} else {
|
|
||||||
self.colours.cpu_colour_styles
|
|
||||||
[(itx - 1) % self.colours.cpu_colour_styles.len()]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.colours.cpu_colour_styles
|
|
||||||
[itx % self.colours.cpu_colour_styles.len()]
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
start_index += how_many_cpus;
|
start_index += how_many_cpus;
|
||||||
|
|
||||||
let margined_loc = Layout::default()
|
let margined_loc = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.constraints([Constraint::Percentage(100)])
|
.constraints([Constraint::Percentage(100)])
|
||||||
.horizontal_margin(1)
|
.horizontal_margin(1)
|
||||||
.split(*chunk)[0];
|
.split(*chunk)[0];
|
||||||
|
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Paragraph::new(cpu_column).block(Block::default()),
|
Paragraph::new(cpu_column).block(Block::default()),
|
||||||
margined_loc,
|
margined_loc,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if app_state.should_get_widget_bounds() {
|
if app_state.should_get_widget_bounds() {
|
||||||
// Update draw loc in widget map
|
// Update draw loc in widget map
|
||||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||||
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
||||||
widget.bottom_right_corner =
|
widget.bottom_right_corner =
|
||||||
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,253 +32,245 @@ static CPU_LEGEND_HEADER_LENS: Lazy<Vec<u16>> = Lazy::new(|| {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
});
|
});
|
||||||
|
|
||||||
pub trait CpuGraphWidget {
|
pub fn draw_cpu<B: Backend>(
|
||||||
fn draw_cpu<B: Backend>(
|
painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect,
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
widget_id: u64,
|
||||||
);
|
) {
|
||||||
fn draw_cpu_graph<B: Backend>(
|
if draw_loc.width as f64 * 0.15 <= 6.0 {
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
// Skip drawing legend
|
||||||
);
|
if app_state.current_widget.widget_id == (widget_id + 1) {
|
||||||
fn draw_cpu_legend<B: Backend>(
|
if app_state.app_config_fields.left_legend {
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
app_state.move_widget_selection(&WidgetDirection::Right);
|
||||||
);
|
} else {
|
||||||
}
|
app_state.move_widget_selection(&WidgetDirection::Left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
draw_cpu_graph(painter, f, app_state, draw_loc, widget_id);
|
||||||
|
if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&widget_id) {
|
||||||
|
cpu_widget_state.is_legend_hidden = true;
|
||||||
|
}
|
||||||
|
|
||||||
impl CpuGraphWidget for Painter {
|
// Update draw loc in widget map
|
||||||
fn draw_cpu<B: Backend>(
|
if app_state.should_get_widget_bounds() {
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
if let Some(bottom_widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||||
) {
|
bottom_widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
||||||
if draw_loc.width as f64 * 0.15 <= 6.0 {
|
bottom_widget.bottom_right_corner =
|
||||||
// Skip drawing legend
|
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
||||||
if app_state.current_widget.widget_id == (widget_id + 1) {
|
|
||||||
if app_state.app_config_fields.left_legend {
|
|
||||||
app_state.move_widget_selection(&WidgetDirection::Right);
|
|
||||||
} else {
|
|
||||||
app_state.move_widget_selection(&WidgetDirection::Left);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.draw_cpu_graph(f, app_state, draw_loc, widget_id);
|
|
||||||
if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&widget_id) {
|
|
||||||
cpu_widget_state.is_legend_hidden = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update draw loc in widget map
|
|
||||||
if app_state.should_get_widget_bounds() {
|
|
||||||
if let Some(bottom_widget) = app_state.widget_map.get_mut(&widget_id) {
|
|
||||||
bottom_widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
|
||||||
bottom_widget.bottom_right_corner =
|
|
||||||
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let (graph_index, legend_index, constraints) = if app_state.app_config_fields.left_legend {
|
||||||
|
(
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
[Constraint::Percentage(15), Constraint::Percentage(85)],
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let (graph_index, legend_index, constraints) =
|
(
|
||||||
if app_state.app_config_fields.left_legend {
|
0,
|
||||||
(
|
1,
|
||||||
1,
|
[Constraint::Percentage(85), Constraint::Percentage(15)],
|
||||||
0,
|
)
|
||||||
[Constraint::Percentage(15), Constraint::Percentage(85)],
|
};
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
[Constraint::Percentage(85), Constraint::Percentage(15)],
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let partitioned_draw_loc = Layout::default()
|
let partitioned_draw_loc = Layout::default()
|
||||||
.margin(0)
|
.margin(0)
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.constraints(constraints)
|
.constraints(constraints)
|
||||||
.split(draw_loc);
|
.split(draw_loc);
|
||||||
|
|
||||||
self.draw_cpu_graph(f, app_state, partitioned_draw_loc[graph_index], widget_id);
|
draw_cpu_graph(
|
||||||
self.draw_cpu_legend(
|
painter,
|
||||||
f,
|
f,
|
||||||
app_state,
|
app_state,
|
||||||
partitioned_draw_loc[legend_index],
|
partitioned_draw_loc[graph_index],
|
||||||
widget_id + 1,
|
widget_id,
|
||||||
);
|
);
|
||||||
|
draw_cpu_legend(
|
||||||
|
painter,
|
||||||
|
f,
|
||||||
|
app_state,
|
||||||
|
partitioned_draw_loc[legend_index],
|
||||||
|
widget_id + 1,
|
||||||
|
);
|
||||||
|
|
||||||
if app_state.should_get_widget_bounds() {
|
if app_state.should_get_widget_bounds() {
|
||||||
// Update draw loc in widget map
|
// Update draw loc in widget map
|
||||||
if let Some(cpu_widget) = app_state.widget_map.get_mut(&widget_id) {
|
if let Some(cpu_widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||||
cpu_widget.top_left_corner = Some((
|
cpu_widget.top_left_corner = Some((
|
||||||
partitioned_draw_loc[graph_index].x,
|
partitioned_draw_loc[graph_index].x,
|
||||||
partitioned_draw_loc[graph_index].y,
|
partitioned_draw_loc[graph_index].y,
|
||||||
));
|
));
|
||||||
cpu_widget.bottom_right_corner = Some((
|
cpu_widget.bottom_right_corner = Some((
|
||||||
partitioned_draw_loc[graph_index].x
|
partitioned_draw_loc[graph_index].x + partitioned_draw_loc[graph_index].width,
|
||||||
+ partitioned_draw_loc[graph_index].width,
|
partitioned_draw_loc[graph_index].y + partitioned_draw_loc[graph_index].height,
|
||||||
partitioned_draw_loc[graph_index].y
|
));
|
||||||
+ partitioned_draw_loc[graph_index].height,
|
}
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(legend_widget) = app_state.widget_map.get_mut(&(widget_id + 1)) {
|
if let Some(legend_widget) = app_state.widget_map.get_mut(&(widget_id + 1)) {
|
||||||
legend_widget.top_left_corner = Some((
|
legend_widget.top_left_corner = Some((
|
||||||
partitioned_draw_loc[legend_index].x,
|
partitioned_draw_loc[legend_index].x,
|
||||||
partitioned_draw_loc[legend_index].y,
|
partitioned_draw_loc[legend_index].y,
|
||||||
));
|
));
|
||||||
legend_widget.bottom_right_corner = Some((
|
legend_widget.bottom_right_corner = Some((
|
||||||
partitioned_draw_loc[legend_index].x
|
partitioned_draw_loc[legend_index].x + partitioned_draw_loc[legend_index].width,
|
||||||
+ partitioned_draw_loc[legend_index].width,
|
partitioned_draw_loc[legend_index].y
|
||||||
partitioned_draw_loc[legend_index].y
|
+ partitioned_draw_loc[legend_index].height,
|
||||||
+ partitioned_draw_loc[legend_index].height,
|
));
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_cpu_graph<B: Backend>(
|
fn draw_cpu_graph<B: Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect,
|
||||||
) {
|
widget_id: u64,
|
||||||
if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&widget_id) {
|
) {
|
||||||
let cpu_data: &mut [ConvertedCpuData] = &mut app_state.canvas_data.cpu_data;
|
if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&widget_id) {
|
||||||
|
let cpu_data: &mut [ConvertedCpuData] = &mut app_state.canvas_data.cpu_data;
|
||||||
|
|
||||||
let display_time_labels = vec![
|
let display_time_labels = vec![
|
||||||
Span::styled(
|
Span::styled(
|
||||||
format!("{}s", cpu_widget_state.current_display_time / 1000),
|
format!("{}s", cpu_widget_state.current_display_time / 1000),
|
||||||
self.colours.graph_style,
|
painter.colours.graph_style,
|
||||||
),
|
),
|
||||||
Span::styled("0s".to_string(), self.colours.graph_style),
|
Span::styled("0s".to_string(), painter.colours.graph_style),
|
||||||
];
|
];
|
||||||
|
|
||||||
let y_axis_labels = vec![
|
let y_axis_labels = vec![
|
||||||
Span::styled(" 0%", self.colours.graph_style),
|
Span::styled(" 0%", painter.colours.graph_style),
|
||||||
Span::styled("100%", self.colours.graph_style),
|
Span::styled("100%", painter.colours.graph_style),
|
||||||
];
|
];
|
||||||
|
|
||||||
let time_start = -(cpu_widget_state.current_display_time as f64);
|
let time_start = -(cpu_widget_state.current_display_time as f64);
|
||||||
|
|
||||||
let x_axis = if app_state.app_config_fields.hide_time
|
let x_axis = if app_state.app_config_fields.hide_time
|
||||||
|| (app_state.app_config_fields.autohide_time
|
|| (app_state.app_config_fields.autohide_time
|
||||||
&& cpu_widget_state.autohide_timer.is_none())
|
&& cpu_widget_state.autohide_timer.is_none())
|
||||||
|
{
|
||||||
|
Axis::default().bounds([time_start, 0.0])
|
||||||
|
} else if let Some(time) = cpu_widget_state.autohide_timer {
|
||||||
|
if std::time::Instant::now().duration_since(time).as_millis()
|
||||||
|
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
|
||||||
{
|
{
|
||||||
Axis::default().bounds([time_start, 0.0])
|
|
||||||
} else if let Some(time) = cpu_widget_state.autohide_timer {
|
|
||||||
if std::time::Instant::now().duration_since(time).as_millis()
|
|
||||||
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
|
|
||||||
{
|
|
||||||
Axis::default()
|
|
||||||
.bounds([time_start, 0.0])
|
|
||||||
.style(self.colours.graph_style)
|
|
||||||
.labels(display_time_labels)
|
|
||||||
} else {
|
|
||||||
cpu_widget_state.autohide_timer = None;
|
|
||||||
Axis::default().bounds([time_start, 0.0])
|
|
||||||
}
|
|
||||||
} else if draw_loc.height < TIME_LABEL_HEIGHT_LIMIT {
|
|
||||||
Axis::default().bounds([time_start, 0.0])
|
|
||||||
} else {
|
|
||||||
Axis::default()
|
Axis::default()
|
||||||
.bounds([time_start, 0.0])
|
.bounds([time_start, 0.0])
|
||||||
.style(self.colours.graph_style)
|
.style(painter.colours.graph_style)
|
||||||
.labels(display_time_labels)
|
.labels(display_time_labels)
|
||||||
};
|
} else {
|
||||||
|
cpu_widget_state.autohide_timer = None;
|
||||||
|
Axis::default().bounds([time_start, 0.0])
|
||||||
|
}
|
||||||
|
} else if draw_loc.height < TIME_LABEL_HEIGHT_LIMIT {
|
||||||
|
Axis::default().bounds([time_start, 0.0])
|
||||||
|
} else {
|
||||||
|
Axis::default()
|
||||||
|
.bounds([time_start, 0.0])
|
||||||
|
.style(painter.colours.graph_style)
|
||||||
|
.labels(display_time_labels)
|
||||||
|
};
|
||||||
|
|
||||||
let y_axis = Axis::default()
|
let y_axis = Axis::default()
|
||||||
.style(self.colours.graph_style)
|
.style(painter.colours.graph_style)
|
||||||
.bounds([0.0, 100.5])
|
.bounds([0.0, 100.5])
|
||||||
.labels(y_axis_labels);
|
.labels(y_axis_labels);
|
||||||
|
|
||||||
let use_dot = app_state.app_config_fields.use_dot;
|
let use_dot = app_state.app_config_fields.use_dot;
|
||||||
let show_avg_cpu = app_state.app_config_fields.show_average_cpu;
|
let show_avg_cpu = app_state.app_config_fields.show_average_cpu;
|
||||||
let current_scroll_position = cpu_widget_state.scroll_state.current_scroll_position;
|
let current_scroll_position = cpu_widget_state.scroll_state.current_scroll_position;
|
||||||
|
|
||||||
let interpolated_cpu_points = cpu_data
|
let interpolated_cpu_points = cpu_data
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(itx, cpu)| {
|
.map(|(itx, cpu)| {
|
||||||
let to_show = if current_scroll_position == ALL_POSITION {
|
let to_show = if current_scroll_position == ALL_POSITION {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
itx == current_scroll_position
|
itx == current_scroll_position
|
||||||
};
|
};
|
||||||
|
|
||||||
if to_show {
|
if to_show {
|
||||||
if let Some(end_pos) = cpu
|
if let Some(end_pos) = cpu
|
||||||
.cpu_data
|
.cpu_data
|
||||||
.iter()
|
.iter()
|
||||||
.position(|(time, _data)| *time >= time_start)
|
.position(|(time, _data)| *time >= time_start)
|
||||||
{
|
{
|
||||||
if end_pos > 1 {
|
if end_pos > 1 {
|
||||||
let start_pos = end_pos - 1;
|
let start_pos = end_pos - 1;
|
||||||
let outside_point = cpu.cpu_data.get(start_pos);
|
let outside_point = cpu.cpu_data.get(start_pos);
|
||||||
let inside_point = cpu.cpu_data.get(end_pos);
|
let inside_point = cpu.cpu_data.get(end_pos);
|
||||||
|
|
||||||
if let (Some(outside_point), Some(inside_point)) =
|
if let (Some(outside_point), Some(inside_point)) =
|
||||||
(outside_point, inside_point)
|
(outside_point, inside_point)
|
||||||
{
|
{
|
||||||
let old = *outside_point;
|
let old = *outside_point;
|
||||||
|
|
||||||
let new_point = (
|
let new_point = (
|
||||||
time_start,
|
time_start,
|
||||||
interpolate_points(outside_point, inside_point, time_start),
|
interpolate_points(outside_point, inside_point, time_start),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(to_replace) = cpu.cpu_data.get_mut(start_pos) {
|
if let Some(to_replace) = cpu.cpu_data.get_mut(start_pos) {
|
||||||
*to_replace = new_point;
|
*to_replace = new_point;
|
||||||
Some((start_pos, old))
|
Some((start_pos, old))
|
||||||
} else {
|
|
||||||
None // Failed to get mutable reference.
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None // Point somehow doesn't exist in our data
|
None // Failed to get mutable reference.
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None // Point is already "leftmost", no need to interpolate.
|
None // Point somehow doesn't exist in our data
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None // There is no point.
|
None // Point is already "leftmost", no need to interpolate.
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None // There is no point.
|
||||||
}
|
}
|
||||||
})
|
} else {
|
||||||
.collect::<Vec<_>>();
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let dataset_vector: Vec<Dataset<'_>> = if current_scroll_position == ALL_POSITION {
|
let dataset_vector: Vec<Dataset<'_>> = if current_scroll_position == ALL_POSITION {
|
||||||
cpu_data
|
cpu_data
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|(itx, cpu)| {
|
.map(|(itx, cpu)| {
|
||||||
Dataset::default()
|
Dataset::default()
|
||||||
.marker(if use_dot {
|
.marker(if use_dot {
|
||||||
Marker::Dot
|
Marker::Dot
|
||||||
} else {
|
} else {
|
||||||
Marker::Braille
|
Marker::Braille
|
||||||
})
|
})
|
||||||
.style(if show_avg_cpu && itx == AVG_POSITION {
|
.style(if show_avg_cpu && itx == AVG_POSITION {
|
||||||
self.colours.avg_colour_style
|
painter.colours.avg_colour_style
|
||||||
} else if itx == ALL_POSITION {
|
} else if itx == ALL_POSITION {
|
||||||
self.colours.all_colour_style
|
painter.colours.all_colour_style
|
||||||
} else {
|
} else {
|
||||||
self.colours.cpu_colour_styles[(itx - 1 // Because of the all position
|
painter.colours.cpu_colour_styles[(itx - 1 // Because of the all position
|
||||||
- (if show_avg_cpu {
|
- (if show_avg_cpu {
|
||||||
AVG_POSITION
|
AVG_POSITION
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}))
|
}))
|
||||||
% self.colours.cpu_colour_styles.len()]
|
% painter.colours.cpu_colour_styles.len()]
|
||||||
})
|
})
|
||||||
.data(&cpu.cpu_data[..])
|
.data(&cpu.cpu_data[..])
|
||||||
.graph_type(tui::widgets::GraphType::Line)
|
.graph_type(tui::widgets::GraphType::Line)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
} else if let Some(cpu) = cpu_data.get(current_scroll_position) {
|
} else if let Some(cpu) = cpu_data.get(current_scroll_position) {
|
||||||
vec![Dataset::default()
|
vec![Dataset::default()
|
||||||
.marker(if use_dot {
|
.marker(if use_dot {
|
||||||
Marker::Dot
|
Marker::Dot
|
||||||
} else {
|
} else {
|
||||||
Marker::Braille
|
Marker::Braille
|
||||||
})
|
})
|
||||||
.style(if show_avg_cpu && current_scroll_position == AVG_POSITION {
|
.style(if show_avg_cpu && current_scroll_position == AVG_POSITION {
|
||||||
self.colours.avg_colour_style
|
painter.colours.avg_colour_style
|
||||||
} else {
|
} else {
|
||||||
self.colours.cpu_colour_styles[(cpu_widget_state
|
painter.colours.cpu_colour_styles[(cpu_widget_state
|
||||||
.scroll_state
|
.scroll_state
|
||||||
.current_scroll_position
|
.current_scroll_position
|
||||||
- 1 // Because of the all position
|
- 1 // Because of the all position
|
||||||
@ -287,249 +279,249 @@ impl CpuGraphWidget for Painter {
|
|||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}))
|
}))
|
||||||
% self.colours.cpu_colour_styles.len()]
|
% painter.colours.cpu_colour_styles.len()]
|
||||||
})
|
})
|
||||||
.data(&cpu.cpu_data[..])
|
.data(&cpu.cpu_data[..])
|
||||||
.graph_type(tui::widgets::GraphType::Line)]
|
.graph_type(tui::widgets::GraphType::Line)]
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||||
let border_style = if is_on_widget {
|
let border_style = if is_on_widget {
|
||||||
self.colours.highlighted_border_style
|
painter.colours.highlighted_border_style
|
||||||
} else {
|
} else {
|
||||||
self.colours.border_style
|
painter.colours.border_style
|
||||||
};
|
};
|
||||||
|
|
||||||
let title = if cfg!(target_family = "unix") {
|
let title = if cfg!(target_family = "unix") {
|
||||||
let load_avg = app_state.canvas_data.load_avg_data;
|
let load_avg = app_state.canvas_data.load_avg_data;
|
||||||
let load_avg_str = format!(
|
let load_avg_str = format!(
|
||||||
"─ {:.2} {:.2} {:.2} ",
|
"─ {:.2} {:.2} {:.2} ",
|
||||||
load_avg[0], load_avg[1], load_avg[2]
|
load_avg[0], load_avg[1], load_avg[2]
|
||||||
);
|
);
|
||||||
let load_avg_str_size =
|
let load_avg_str_size =
|
||||||
UnicodeSegmentation::graphemes(load_avg_str.as_str(), true).count();
|
UnicodeSegmentation::graphemes(load_avg_str.as_str(), true).count();
|
||||||
|
|
||||||
if app_state.is_expanded {
|
if app_state.is_expanded {
|
||||||
const TITLE_BASE: &str = " CPU ── Esc to go back ";
|
|
||||||
|
|
||||||
Spans::from(vec![
|
|
||||||
Span::styled(" CPU ", self.colours.widget_title_style),
|
|
||||||
Span::styled(load_avg_str, self.colours.widget_title_style),
|
|
||||||
Span::styled(
|
|
||||||
format!(
|
|
||||||
"─{}─ Esc to go back ",
|
|
||||||
"─".repeat(usize::from(draw_loc.width).saturating_sub(
|
|
||||||
load_avg_str_size
|
|
||||||
+ UnicodeSegmentation::graphemes(TITLE_BASE, true).count()
|
|
||||||
+ 2
|
|
||||||
))
|
|
||||||
),
|
|
||||||
border_style,
|
|
||||||
),
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
Spans::from(vec![
|
|
||||||
Span::styled(" CPU ", self.colours.widget_title_style),
|
|
||||||
Span::styled(load_avg_str, self.colours.widget_title_style),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
} else if app_state.is_expanded {
|
|
||||||
const TITLE_BASE: &str = " CPU ── Esc to go back ";
|
const TITLE_BASE: &str = " CPU ── Esc to go back ";
|
||||||
|
|
||||||
Spans::from(vec![
|
Spans::from(vec![
|
||||||
Span::styled(" CPU ", self.colours.widget_title_style),
|
Span::styled(" CPU ", painter.colours.widget_title_style),
|
||||||
|
Span::styled(load_avg_str, painter.colours.widget_title_style),
|
||||||
Span::styled(
|
Span::styled(
|
||||||
format!(
|
format!(
|
||||||
"─{}─ Esc to go back ",
|
"─{}─ Esc to go back ",
|
||||||
"─".repeat(usize::from(draw_loc.width).saturating_sub(
|
"─".repeat(usize::from(draw_loc.width).saturating_sub(
|
||||||
UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
|
load_avg_str_size
|
||||||
|
+ UnicodeSegmentation::graphemes(TITLE_BASE, true).count()
|
||||||
|
+ 2
|
||||||
))
|
))
|
||||||
),
|
),
|
||||||
border_style,
|
border_style,
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
} else {
|
} else {
|
||||||
Spans::from(vec![Span::styled(" CPU ", self.colours.widget_title_style)])
|
Spans::from(vec![
|
||||||
};
|
Span::styled(" CPU ", painter.colours.widget_title_style),
|
||||||
|
Span::styled(load_avg_str, painter.colours.widget_title_style),
|
||||||
f.render_widget(
|
])
|
||||||
Chart::new(dataset_vector)
|
|
||||||
.block(
|
|
||||||
Block::default()
|
|
||||||
.title(title)
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.border_style(border_style),
|
|
||||||
)
|
|
||||||
.x_axis(x_axis)
|
|
||||||
.y_axis(y_axis),
|
|
||||||
draw_loc,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Reset interpolated points
|
|
||||||
cpu_data
|
|
||||||
.iter_mut()
|
|
||||||
.zip(interpolated_cpu_points)
|
|
||||||
.for_each(|(cpu, interpolation)| {
|
|
||||||
if let Some((index, old_value)) = interpolation {
|
|
||||||
if let Some(to_replace) = cpu.cpu_data.get_mut(index) {
|
|
||||||
*to_replace = old_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_cpu_legend<B: Backend>(
|
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
|
||||||
) {
|
|
||||||
let recalculate_column_widths = app_state.should_get_widget_bounds();
|
|
||||||
if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&(widget_id - 1))
|
|
||||||
{
|
|
||||||
cpu_widget_state.is_legend_hidden = false;
|
|
||||||
let cpu_data: &mut [ConvertedCpuData] = &mut app_state.canvas_data.cpu_data;
|
|
||||||
let cpu_table_state = &mut cpu_widget_state.scroll_state.table_state;
|
|
||||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
|
||||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
app_state.app_config_fields.table_gap
|
|
||||||
};
|
|
||||||
let start_position = get_start_position(
|
|
||||||
usize::from(
|
|
||||||
(draw_loc.height + (1 - table_gap)).saturating_sub(self.table_height_offset),
|
|
||||||
),
|
|
||||||
&cpu_widget_state.scroll_state.scroll_direction,
|
|
||||||
&mut cpu_widget_state.scroll_state.previous_scroll_position,
|
|
||||||
cpu_widget_state.scroll_state.current_scroll_position,
|
|
||||||
app_state.is_force_redraw,
|
|
||||||
);
|
|
||||||
cpu_table_state.select(Some(
|
|
||||||
cpu_widget_state
|
|
||||||
.scroll_state
|
|
||||||
.current_scroll_position
|
|
||||||
.saturating_sub(start_position),
|
|
||||||
));
|
|
||||||
|
|
||||||
let sliced_cpu_data = &cpu_data[start_position..];
|
|
||||||
|
|
||||||
let offset_scroll_index = cpu_widget_state
|
|
||||||
.scroll_state
|
|
||||||
.current_scroll_position
|
|
||||||
.saturating_sub(start_position);
|
|
||||||
let show_avg_cpu = app_state.app_config_fields.show_average_cpu;
|
|
||||||
|
|
||||||
// Calculate widths
|
|
||||||
if recalculate_column_widths {
|
|
||||||
cpu_widget_state.table_width_state.desired_column_widths = vec![6, 4];
|
|
||||||
cpu_widget_state.table_width_state.calculated_column_widths = get_column_widths(
|
|
||||||
draw_loc.width,
|
|
||||||
&[None, None],
|
|
||||||
&(CPU_LEGEND_HEADER_LENS
|
|
||||||
.iter()
|
|
||||||
.map(|width| Some(*width))
|
|
||||||
.collect::<Vec<_>>()),
|
|
||||||
&[Some(0.5), Some(0.5)],
|
|
||||||
&(cpu_widget_state
|
|
||||||
.table_width_state
|
|
||||||
.desired_column_widths
|
|
||||||
.iter()
|
|
||||||
.map(|width| Some(*width))
|
|
||||||
.collect::<Vec<_>>()),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
} else if app_state.is_expanded {
|
||||||
|
const TITLE_BASE: &str = " CPU ── Esc to go back ";
|
||||||
|
|
||||||
let dcw = &cpu_widget_state.table_width_state.desired_column_widths;
|
Spans::from(vec![
|
||||||
let ccw = &cpu_widget_state.table_width_state.calculated_column_widths;
|
Span::styled(" CPU ", painter.colours.widget_title_style),
|
||||||
let cpu_rows = sliced_cpu_data.iter().enumerate().map(|(itx, cpu)| {
|
Span::styled(
|
||||||
let mut truncated_name =
|
format!(
|
||||||
if let (Some(desired_column_width), Some(calculated_column_width)) =
|
"─{}─ Esc to go back ",
|
||||||
(dcw.get(0), ccw.get(0))
|
"─".repeat(usize::from(draw_loc.width).saturating_sub(
|
||||||
{
|
UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
|
||||||
if *desired_column_width > *calculated_column_width {
|
))
|
||||||
Text::raw(&cpu.short_cpu_name)
|
),
|
||||||
} else {
|
border_style,
|
||||||
Text::raw(&cpu.cpu_name)
|
),
|
||||||
}
|
])
|
||||||
} else {
|
} else {
|
||||||
Text::raw(&cpu.cpu_name)
|
Spans::from(vec![Span::styled(
|
||||||
};
|
" CPU ",
|
||||||
|
painter.colours.widget_title_style,
|
||||||
|
)])
|
||||||
|
};
|
||||||
|
|
||||||
let is_first_column_hidden = if let Some(calculated_column_width) = ccw.get(0) {
|
f.render_widget(
|
||||||
*calculated_column_width == 0
|
Chart::new(dataset_vector)
|
||||||
} else {
|
.block(
|
||||||
false
|
Block::default()
|
||||||
};
|
.title(title)
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(border_style),
|
||||||
|
)
|
||||||
|
.x_axis(x_axis)
|
||||||
|
.y_axis(y_axis),
|
||||||
|
draw_loc,
|
||||||
|
);
|
||||||
|
|
||||||
let truncated_legend = if is_first_column_hidden && cpu.legend_value.is_empty() {
|
// Reset interpolated points
|
||||||
// For the case where we only have room for one column, display "All" in the normally blank area.
|
cpu_data
|
||||||
Text::raw("All")
|
.iter_mut()
|
||||||
} else {
|
.zip(interpolated_cpu_points)
|
||||||
Text::raw(&cpu.legend_value)
|
.for_each(|(cpu, interpolation)| {
|
||||||
};
|
if let Some((index, old_value)) = interpolation {
|
||||||
|
if let Some(to_replace) = cpu.cpu_data.get_mut(index) {
|
||||||
if !is_first_column_hidden
|
*to_replace = old_value;
|
||||||
&& itx == offset_scroll_index
|
}
|
||||||
&& itx + start_position == ALL_POSITION
|
|
||||||
{
|
|
||||||
truncated_name.patch_style(self.colours.currently_selected_text_style);
|
|
||||||
Row::new(vec![truncated_name, truncated_legend])
|
|
||||||
} else {
|
|
||||||
let cpu_string_row = vec![truncated_name, truncated_legend];
|
|
||||||
|
|
||||||
Row::new(cpu_string_row).style(if itx == offset_scroll_index {
|
|
||||||
self.colours.currently_selected_text_style
|
|
||||||
} else if itx + start_position == ALL_POSITION {
|
|
||||||
self.colours.all_colour_style
|
|
||||||
} else if show_avg_cpu {
|
|
||||||
if itx + start_position == AVG_POSITION {
|
|
||||||
self.colours.avg_colour_style
|
|
||||||
} else {
|
|
||||||
self.colours.cpu_colour_styles[(itx + start_position
|
|
||||||
- AVG_POSITION
|
|
||||||
- 1)
|
|
||||||
% self.colours.cpu_colour_styles.len()]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.colours.cpu_colour_styles[(itx + start_position - ALL_POSITION - 1)
|
|
||||||
% self.colours.cpu_colour_styles.len()]
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
// Note we don't set highlight_style, as it should always be shown for this widget.
|
}
|
||||||
let border_and_title_style = if is_on_widget {
|
|
||||||
self.colours.highlighted_border_style
|
fn draw_cpu_legend<B: Backend>(
|
||||||
} else {
|
painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect,
|
||||||
self.colours.border_style
|
widget_id: u64,
|
||||||
};
|
) {
|
||||||
|
let recalculate_column_widths = app_state.should_get_widget_bounds();
|
||||||
// Draw
|
if let Some(cpu_widget_state) = app_state.cpu_state.widget_states.get_mut(&(widget_id - 1)) {
|
||||||
f.render_stateful_widget(
|
cpu_widget_state.is_legend_hidden = false;
|
||||||
Table::new(cpu_rows)
|
let cpu_data: &mut [ConvertedCpuData] = &mut app_state.canvas_data.cpu_data;
|
||||||
.block(
|
let cpu_table_state = &mut cpu_widget_state.scroll_state.table_state;
|
||||||
Block::default()
|
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||||
.borders(Borders::ALL)
|
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||||
.border_style(border_and_title_style),
|
0
|
||||||
)
|
} else {
|
||||||
.header(
|
app_state.app_config_fields.table_gap
|
||||||
Row::new(CPU_LEGEND_HEADER.to_vec())
|
};
|
||||||
.style(self.colours.table_header_style)
|
let start_position = get_start_position(
|
||||||
.bottom_margin(table_gap),
|
usize::from(
|
||||||
)
|
(draw_loc.height + (1 - table_gap)).saturating_sub(painter.table_height_offset),
|
||||||
.widths(
|
),
|
||||||
&(cpu_widget_state
|
&cpu_widget_state.scroll_state.scroll_direction,
|
||||||
.table_width_state
|
&mut cpu_widget_state.scroll_state.previous_scroll_position,
|
||||||
.calculated_column_widths
|
cpu_widget_state.scroll_state.current_scroll_position,
|
||||||
.iter()
|
app_state.is_force_redraw,
|
||||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
);
|
||||||
.collect::<Vec<_>>()),
|
cpu_table_state.select(Some(
|
||||||
),
|
cpu_widget_state
|
||||||
draw_loc,
|
.scroll_state
|
||||||
cpu_table_state,
|
.current_scroll_position
|
||||||
);
|
.saturating_sub(start_position),
|
||||||
}
|
));
|
||||||
|
|
||||||
|
let sliced_cpu_data = &cpu_data[start_position..];
|
||||||
|
|
||||||
|
let offset_scroll_index = cpu_widget_state
|
||||||
|
.scroll_state
|
||||||
|
.current_scroll_position
|
||||||
|
.saturating_sub(start_position);
|
||||||
|
let show_avg_cpu = app_state.app_config_fields.show_average_cpu;
|
||||||
|
|
||||||
|
// Calculate widths
|
||||||
|
if recalculate_column_widths {
|
||||||
|
cpu_widget_state.table_width_state.desired_column_widths = vec![6, 4];
|
||||||
|
cpu_widget_state.table_width_state.calculated_column_widths = get_column_widths(
|
||||||
|
draw_loc.width,
|
||||||
|
&[None, None],
|
||||||
|
&(CPU_LEGEND_HEADER_LENS
|
||||||
|
.iter()
|
||||||
|
.map(|width| Some(*width))
|
||||||
|
.collect::<Vec<_>>()),
|
||||||
|
&[Some(0.5), Some(0.5)],
|
||||||
|
&(cpu_widget_state
|
||||||
|
.table_width_state
|
||||||
|
.desired_column_widths
|
||||||
|
.iter()
|
||||||
|
.map(|width| Some(*width))
|
||||||
|
.collect::<Vec<_>>()),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dcw = &cpu_widget_state.table_width_state.desired_column_widths;
|
||||||
|
let ccw = &cpu_widget_state.table_width_state.calculated_column_widths;
|
||||||
|
let cpu_rows = sliced_cpu_data.iter().enumerate().map(|(itx, cpu)| {
|
||||||
|
let mut truncated_name =
|
||||||
|
if let (Some(desired_column_width), Some(calculated_column_width)) =
|
||||||
|
(dcw.get(0), ccw.get(0))
|
||||||
|
{
|
||||||
|
if *desired_column_width > *calculated_column_width {
|
||||||
|
Text::raw(&cpu.short_cpu_name)
|
||||||
|
} else {
|
||||||
|
Text::raw(&cpu.cpu_name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text::raw(&cpu.cpu_name)
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_first_column_hidden = if let Some(calculated_column_width) = ccw.get(0) {
|
||||||
|
*calculated_column_width == 0
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let truncated_legend = if is_first_column_hidden && cpu.legend_value.is_empty() {
|
||||||
|
// For the case where we only have room for one column, display "All" in the normally blank area.
|
||||||
|
Text::raw("All")
|
||||||
|
} else {
|
||||||
|
Text::raw(&cpu.legend_value)
|
||||||
|
};
|
||||||
|
|
||||||
|
if !is_first_column_hidden
|
||||||
|
&& itx == offset_scroll_index
|
||||||
|
&& itx + start_position == ALL_POSITION
|
||||||
|
{
|
||||||
|
truncated_name.patch_style(painter.colours.currently_selected_text_style);
|
||||||
|
Row::new(vec![truncated_name, truncated_legend])
|
||||||
|
} else {
|
||||||
|
let cpu_string_row = vec![truncated_name, truncated_legend];
|
||||||
|
|
||||||
|
Row::new(cpu_string_row).style(if itx == offset_scroll_index {
|
||||||
|
painter.colours.currently_selected_text_style
|
||||||
|
} else if itx + start_position == ALL_POSITION {
|
||||||
|
painter.colours.all_colour_style
|
||||||
|
} else if show_avg_cpu {
|
||||||
|
if itx + start_position == AVG_POSITION {
|
||||||
|
painter.colours.avg_colour_style
|
||||||
|
} else {
|
||||||
|
painter.colours.cpu_colour_styles[(itx + start_position - AVG_POSITION - 1)
|
||||||
|
% painter.colours.cpu_colour_styles.len()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
painter.colours.cpu_colour_styles[(itx + start_position - ALL_POSITION - 1)
|
||||||
|
% painter.colours.cpu_colour_styles.len()]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Note we don't set highlight_style, as it should always be shown for this widget.
|
||||||
|
let border_and_title_style = if is_on_widget {
|
||||||
|
painter.colours.highlighted_border_style
|
||||||
|
} else {
|
||||||
|
painter.colours.border_style
|
||||||
|
};
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
f.render_stateful_widget(
|
||||||
|
Table::new(cpu_rows)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(border_and_title_style),
|
||||||
|
)
|
||||||
|
.header(
|
||||||
|
Row::new(CPU_LEGEND_HEADER.to_vec())
|
||||||
|
.style(painter.colours.table_header_style)
|
||||||
|
.bottom_margin(table_gap),
|
||||||
|
)
|
||||||
|
.widths(
|
||||||
|
&(cpu_widget_state
|
||||||
|
.table_width_state
|
||||||
|
.calculated_column_widths
|
||||||
|
.iter()
|
||||||
|
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||||
|
.collect::<Vec<_>>()),
|
||||||
|
),
|
||||||
|
draw_loc,
|
||||||
|
cpu_table_state,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,249 +27,239 @@ static DISK_HEADERS_LENS: Lazy<Vec<u16>> = Lazy::new(|| {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
});
|
});
|
||||||
|
|
||||||
pub trait DiskTableWidget {
|
pub fn draw_disk_table<B: Backend>(
|
||||||
fn draw_disk_table<B: Backend>(
|
painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut app::AppState, draw_loc: Rect,
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::AppState, draw_loc: Rect,
|
draw_border: bool, widget_id: u64,
|
||||||
draw_border: bool, widget_id: u64,
|
) {
|
||||||
);
|
let recalculate_column_widths = app_state.should_get_widget_bounds();
|
||||||
}
|
if let Some(disk_widget_state) = app_state.disk_state.widget_states.get_mut(&widget_id) {
|
||||||
|
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
app_state.app_config_fields.table_gap
|
||||||
|
};
|
||||||
|
let start_position = get_start_position(
|
||||||
|
usize::from(
|
||||||
|
(draw_loc.height + (1 - table_gap)).saturating_sub(painter.table_height_offset),
|
||||||
|
),
|
||||||
|
&disk_widget_state.scroll_state.scroll_direction,
|
||||||
|
&mut disk_widget_state.scroll_state.previous_scroll_position,
|
||||||
|
disk_widget_state.scroll_state.current_scroll_position,
|
||||||
|
app_state.is_force_redraw,
|
||||||
|
);
|
||||||
|
let is_on_widget = app_state.current_widget.widget_id == widget_id;
|
||||||
|
let disk_table_state = &mut disk_widget_state.scroll_state.table_state;
|
||||||
|
disk_table_state.select(Some(
|
||||||
|
disk_widget_state
|
||||||
|
.scroll_state
|
||||||
|
.current_scroll_position
|
||||||
|
.saturating_sub(start_position),
|
||||||
|
));
|
||||||
|
let sliced_vec = &app_state.canvas_data.disk_data[start_position..];
|
||||||
|
|
||||||
impl DiskTableWidget for Painter {
|
// Calculate widths
|
||||||
fn draw_disk_table<B: Backend>(
|
let hard_widths = [None, None, Some(4), Some(6), Some(6), Some(7), Some(7)];
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::AppState, draw_loc: Rect,
|
if recalculate_column_widths {
|
||||||
draw_border: bool, widget_id: u64,
|
disk_widget_state.table_width_state.desired_column_widths = {
|
||||||
) {
|
let mut column_widths = DISK_HEADERS_LENS.clone();
|
||||||
let recalculate_column_widths = app_state.should_get_widget_bounds();
|
for row in sliced_vec {
|
||||||
if let Some(disk_widget_state) = app_state.disk_state.widget_states.get_mut(&widget_id) {
|
for (col, entry) in row.iter().enumerate() {
|
||||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
if entry.len() as u16 > column_widths[col] {
|
||||||
0
|
column_widths[col] = entry.len() as u16;
|
||||||
} else {
|
|
||||||
app_state.app_config_fields.table_gap
|
|
||||||
};
|
|
||||||
let start_position = get_start_position(
|
|
||||||
usize::from(
|
|
||||||
(draw_loc.height + (1 - table_gap)).saturating_sub(self.table_height_offset),
|
|
||||||
),
|
|
||||||
&disk_widget_state.scroll_state.scroll_direction,
|
|
||||||
&mut disk_widget_state.scroll_state.previous_scroll_position,
|
|
||||||
disk_widget_state.scroll_state.current_scroll_position,
|
|
||||||
app_state.is_force_redraw,
|
|
||||||
);
|
|
||||||
let is_on_widget = app_state.current_widget.widget_id == widget_id;
|
|
||||||
let disk_table_state = &mut disk_widget_state.scroll_state.table_state;
|
|
||||||
disk_table_state.select(Some(
|
|
||||||
disk_widget_state
|
|
||||||
.scroll_state
|
|
||||||
.current_scroll_position
|
|
||||||
.saturating_sub(start_position),
|
|
||||||
));
|
|
||||||
let sliced_vec = &app_state.canvas_data.disk_data[start_position..];
|
|
||||||
|
|
||||||
// Calculate widths
|
|
||||||
let hard_widths = [None, None, Some(4), Some(6), Some(6), Some(7), Some(7)];
|
|
||||||
if recalculate_column_widths {
|
|
||||||
disk_widget_state.table_width_state.desired_column_widths = {
|
|
||||||
let mut column_widths = DISK_HEADERS_LENS.clone();
|
|
||||||
for row in sliced_vec {
|
|
||||||
for (col, entry) in row.iter().enumerate() {
|
|
||||||
if entry.len() as u16 > column_widths[col] {
|
|
||||||
column_widths[col] = entry.len() as u16;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
column_widths
|
}
|
||||||
};
|
column_widths
|
||||||
disk_widget_state.table_width_state.desired_column_widths = disk_widget_state
|
};
|
||||||
.table_width_state
|
disk_widget_state.table_width_state.desired_column_widths = disk_widget_state
|
||||||
.desired_column_widths
|
.table_width_state
|
||||||
.iter()
|
.desired_column_widths
|
||||||
.zip(&hard_widths)
|
.iter()
|
||||||
.map(|(current, hard)| {
|
.zip(&hard_widths)
|
||||||
if let Some(hard) = hard {
|
.map(|(current, hard)| {
|
||||||
if *hard > *current {
|
if let Some(hard) = hard {
|
||||||
*hard
|
if *hard > *current {
|
||||||
} else {
|
*hard
|
||||||
*current
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
*current
|
*current
|
||||||
}
|
}
|
||||||
})
|
} else {
|
||||||
.collect::<Vec<_>>();
|
*current
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
disk_widget_state.table_width_state.calculated_column_widths = get_column_widths(
|
disk_widget_state.table_width_state.calculated_column_widths = get_column_widths(
|
||||||
draw_loc.width,
|
draw_loc.width,
|
||||||
&hard_widths,
|
&hard_widths,
|
||||||
&(DISK_HEADERS_LENS
|
&(DISK_HEADERS_LENS
|
||||||
.iter()
|
.iter()
|
||||||
.map(|w| Some(*w))
|
.map(|w| Some(*w))
|
||||||
.collect::<Vec<_>>()),
|
.collect::<Vec<_>>()),
|
||||||
&[Some(0.2), Some(0.2), None, None, None, None, None],
|
&[Some(0.2), Some(0.2), None, None, None, None, None],
|
||||||
&(disk_widget_state
|
&(disk_widget_state
|
||||||
.table_width_state
|
.table_width_state
|
||||||
.desired_column_widths
|
.desired_column_widths
|
||||||
.iter()
|
.iter()
|
||||||
.map(|w| Some(*w))
|
.map(|w| Some(*w))
|
||||||
.collect::<Vec<_>>()),
|
.collect::<Vec<_>>()),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let dcw = &disk_widget_state.table_width_state.desired_column_widths;
|
let dcw = &disk_widget_state.table_width_state.desired_column_widths;
|
||||||
let ccw = &disk_widget_state.table_width_state.calculated_column_widths;
|
let ccw = &disk_widget_state.table_width_state.calculated_column_widths;
|
||||||
let disk_rows =
|
let disk_rows = sliced_vec.iter().map(|disk_row| {
|
||||||
sliced_vec.iter().map(|disk_row| {
|
let truncated_data =
|
||||||
let truncated_data = disk_row.iter().zip(&hard_widths).enumerate().map(
|
disk_row
|
||||||
|(itx, (entry, width))| {
|
.iter()
|
||||||
if width.is_none() {
|
.zip(&hard_widths)
|
||||||
if let (Some(desired_col_width), Some(calculated_col_width)) =
|
.enumerate()
|
||||||
(dcw.get(itx), ccw.get(itx))
|
.map(|(itx, (entry, width))| {
|
||||||
|
if width.is_none() {
|
||||||
|
if let (Some(desired_col_width), Some(calculated_col_width)) =
|
||||||
|
(dcw.get(itx), ccw.get(itx))
|
||||||
|
{
|
||||||
|
if *desired_col_width > *calculated_col_width
|
||||||
|
&& *calculated_col_width > 0
|
||||||
{
|
{
|
||||||
if *desired_col_width > *calculated_col_width
|
let graphemes =
|
||||||
&& *calculated_col_width > 0
|
UnicodeSegmentation::graphemes(entry.as_str(), true)
|
||||||
{
|
.collect::<Vec<&str>>();
|
||||||
let graphemes =
|
|
||||||
UnicodeSegmentation::graphemes(entry.as_str(), true)
|
|
||||||
.collect::<Vec<&str>>();
|
|
||||||
|
|
||||||
if graphemes.len() > *calculated_col_width as usize
|
if graphemes.len() > *calculated_col_width as usize
|
||||||
&& *calculated_col_width > 1
|
&& *calculated_col_width > 1
|
||||||
{
|
{
|
||||||
// Truncate with ellipsis
|
// Truncate with ellipsis
|
||||||
let first_n = graphemes
|
let first_n = graphemes
|
||||||
[..(*calculated_col_width as usize - 1)]
|
[..(*calculated_col_width as usize - 1)]
|
||||||
.concat();
|
.concat();
|
||||||
return Text::raw(format!("{}…", first_n));
|
return Text::raw(format!("{}…", first_n));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Text::raw(entry)
|
Text::raw(entry)
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
Row::new(truncated_data)
|
Row::new(truncated_data)
|
||||||
});
|
});
|
||||||
|
|
||||||
let (border_style, highlight_style) = if is_on_widget {
|
let (border_style, highlight_style) = if is_on_widget {
|
||||||
(
|
(
|
||||||
self.colours.highlighted_border_style,
|
painter.colours.highlighted_border_style,
|
||||||
self.colours.currently_selected_text_style,
|
painter.colours.currently_selected_text_style,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(self.colours.border_style, self.colours.text_style)
|
(painter.colours.border_style, painter.colours.text_style)
|
||||||
};
|
};
|
||||||
|
|
||||||
let title_base = if app_state.app_config_fields.show_table_scroll_position {
|
let title_base = if app_state.app_config_fields.show_table_scroll_position {
|
||||||
let title_string = format!(
|
let title_string = format!(
|
||||||
" Disk ({} of {}) ",
|
" Disk ({} of {}) ",
|
||||||
disk_widget_state
|
disk_widget_state
|
||||||
.scroll_state
|
.scroll_state
|
||||||
.current_scroll_position
|
.current_scroll_position
|
||||||
.saturating_add(1),
|
.saturating_add(1),
|
||||||
app_state.canvas_data.disk_data.len()
|
app_state.canvas_data.disk_data.len()
|
||||||
);
|
|
||||||
|
|
||||||
if title_string.len() <= draw_loc.width as usize {
|
|
||||||
title_string
|
|
||||||
} else {
|
|
||||||
" Disk ".to_string()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
" Disk ".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let title = if app_state.is_expanded {
|
|
||||||
const ESCAPE_ENDING: &str = "── Esc to go back ";
|
|
||||||
|
|
||||||
let (chosen_title_base, expanded_title_base) = {
|
|
||||||
let temp_title_base = format!("{}{}", title_base, ESCAPE_ENDING);
|
|
||||||
|
|
||||||
if temp_title_base.len() > draw_loc.width as usize {
|
|
||||||
(
|
|
||||||
" Disk ".to_string(),
|
|
||||||
format!("{}{}", " Disk ".to_string(), ESCAPE_ENDING),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(title_base, temp_title_base)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spans::from(vec![
|
|
||||||
Span::styled(chosen_title_base, self.colours.widget_title_style),
|
|
||||||
Span::styled(
|
|
||||||
format!(
|
|
||||||
"─{}─ Esc to go back ",
|
|
||||||
"─".repeat(
|
|
||||||
usize::from(draw_loc.width).saturating_sub(
|
|
||||||
UnicodeSegmentation::graphemes(
|
|
||||||
expanded_title_base.as_str(),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
.count()
|
|
||||||
+ 2
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
border_style,
|
|
||||||
),
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
Spans::from(Span::styled(title_base, self.colours.widget_title_style))
|
|
||||||
};
|
|
||||||
|
|
||||||
let disk_block = if draw_border {
|
|
||||||
Block::default()
|
|
||||||
.title(title)
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.border_style(border_style)
|
|
||||||
} else if is_on_widget {
|
|
||||||
Block::default()
|
|
||||||
.borders(*SIDE_BORDERS)
|
|
||||||
.border_style(self.colours.highlighted_border_style)
|
|
||||||
} else {
|
|
||||||
Block::default().borders(Borders::NONE)
|
|
||||||
};
|
|
||||||
|
|
||||||
let margined_draw_loc = Layout::default()
|
|
||||||
.constraints([Constraint::Percentage(100)])
|
|
||||||
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
|
||||||
.direction(Direction::Horizontal)
|
|
||||||
.split(draw_loc)[0];
|
|
||||||
|
|
||||||
// Draw!
|
|
||||||
f.render_stateful_widget(
|
|
||||||
Table::new(disk_rows)
|
|
||||||
.block(disk_block)
|
|
||||||
.header(
|
|
||||||
Row::new(DISK_HEADERS.to_vec())
|
|
||||||
.style(self.colours.table_header_style)
|
|
||||||
.bottom_margin(table_gap),
|
|
||||||
)
|
|
||||||
.highlight_style(highlight_style)
|
|
||||||
.style(self.colours.text_style)
|
|
||||||
.widths(
|
|
||||||
&(disk_widget_state
|
|
||||||
.table_width_state
|
|
||||||
.calculated_column_widths
|
|
||||||
.iter()
|
|
||||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
|
||||||
.collect::<Vec<_>>()),
|
|
||||||
),
|
|
||||||
margined_draw_loc,
|
|
||||||
disk_table_state,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if app_state.should_get_widget_bounds() {
|
if title_string.len() <= draw_loc.width as usize {
|
||||||
// Update draw loc in widget map
|
title_string
|
||||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
} else {
|
||||||
widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y));
|
" Disk ".to_string()
|
||||||
widget.bottom_right_corner = Some((
|
}
|
||||||
margined_draw_loc.x + margined_draw_loc.width,
|
} else {
|
||||||
margined_draw_loc.y + margined_draw_loc.height,
|
" Disk ".to_string()
|
||||||
));
|
};
|
||||||
|
|
||||||
|
let title = if app_state.is_expanded {
|
||||||
|
const ESCAPE_ENDING: &str = "── Esc to go back ";
|
||||||
|
|
||||||
|
let (chosen_title_base, expanded_title_base) = {
|
||||||
|
let temp_title_base = format!("{}{}", title_base, ESCAPE_ENDING);
|
||||||
|
|
||||||
|
if temp_title_base.len() > draw_loc.width as usize {
|
||||||
|
(
|
||||||
|
" Disk ".to_string(),
|
||||||
|
format!("{}{}", " Disk ".to_string(), ESCAPE_ENDING),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(title_base, temp_title_base)
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Spans::from(vec![
|
||||||
|
Span::styled(chosen_title_base, painter.colours.widget_title_style),
|
||||||
|
Span::styled(
|
||||||
|
format!(
|
||||||
|
"─{}─ Esc to go back ",
|
||||||
|
"─".repeat(
|
||||||
|
usize::from(draw_loc.width).saturating_sub(
|
||||||
|
UnicodeSegmentation::graphemes(expanded_title_base.as_str(), true)
|
||||||
|
.count()
|
||||||
|
+ 2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
border_style,
|
||||||
|
),
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
Spans::from(Span::styled(title_base, painter.colours.widget_title_style))
|
||||||
|
};
|
||||||
|
|
||||||
|
let disk_block = if draw_border {
|
||||||
|
Block::default()
|
||||||
|
.title(title)
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(border_style)
|
||||||
|
} else if is_on_widget {
|
||||||
|
Block::default()
|
||||||
|
.borders(*SIDE_BORDERS)
|
||||||
|
.border_style(painter.colours.highlighted_border_style)
|
||||||
|
} else {
|
||||||
|
Block::default().borders(Borders::NONE)
|
||||||
|
};
|
||||||
|
|
||||||
|
let margined_draw_loc = Layout::default()
|
||||||
|
.constraints([Constraint::Percentage(100)])
|
||||||
|
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.split(draw_loc)[0];
|
||||||
|
|
||||||
|
// Draw!
|
||||||
|
f.render_stateful_widget(
|
||||||
|
Table::new(disk_rows)
|
||||||
|
.block(disk_block)
|
||||||
|
.header(
|
||||||
|
Row::new(DISK_HEADERS.to_vec())
|
||||||
|
.style(painter.colours.table_header_style)
|
||||||
|
.bottom_margin(table_gap),
|
||||||
|
)
|
||||||
|
.highlight_style(highlight_style)
|
||||||
|
.style(painter.colours.text_style)
|
||||||
|
.widths(
|
||||||
|
&(disk_widget_state
|
||||||
|
.table_width_state
|
||||||
|
.calculated_column_widths
|
||||||
|
.iter()
|
||||||
|
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||||
|
.collect::<Vec<_>>()),
|
||||||
|
),
|
||||||
|
margined_draw_loc,
|
||||||
|
disk_table_state,
|
||||||
|
);
|
||||||
|
|
||||||
|
if app_state.should_get_widget_bounds() {
|
||||||
|
// Update draw loc in widget map
|
||||||
|
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||||
|
widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y));
|
||||||
|
widget.bottom_right_corner = Some((
|
||||||
|
margined_draw_loc.x + margined_draw_loc.width,
|
||||||
|
margined_draw_loc.y + margined_draw_loc.height,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,118 +13,111 @@ use tui::{
|
|||||||
widgets::{Block, Paragraph},
|
widgets::{Block, Paragraph},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait MemBasicWidget {
|
pub fn draw_basic_memory<B: Backend>(
|
||||||
fn draw_basic_memory<B: Backend>(
|
painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect,
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
widget_id: u64,
|
||||||
);
|
) {
|
||||||
}
|
let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data;
|
||||||
|
let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data;
|
||||||
|
|
||||||
impl MemBasicWidget for Painter {
|
let margined_loc = Layout::default()
|
||||||
fn draw_basic_memory<B: Backend>(
|
.constraints([Constraint::Percentage(100)])
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
.horizontal_margin(1)
|
||||||
) {
|
.split(draw_loc);
|
||||||
let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data;
|
|
||||||
let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data;
|
|
||||||
|
|
||||||
let margined_loc = Layout::default()
|
|
||||||
.constraints([Constraint::Percentage(100)])
|
|
||||||
.horizontal_margin(1)
|
|
||||||
.split(draw_loc);
|
|
||||||
|
|
||||||
if app_state.current_widget.widget_id == widget_id {
|
|
||||||
f.render_widget(
|
|
||||||
Block::default()
|
|
||||||
.borders(*SIDE_BORDERS)
|
|
||||||
.border_style(self.colours.highlighted_border_style),
|
|
||||||
draw_loc,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ram_use_percentage = if let Some(mem) = mem_data.last() {
|
|
||||||
mem.1
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
let swap_use_percentage = if let Some(swap) = swap_data.last() {
|
|
||||||
swap.1
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
|
|
||||||
const EMPTY_MEMORY_FRAC_STRING: &str = "0.0B/0.0B";
|
|
||||||
|
|
||||||
let trimmed_memory_frac =
|
|
||||||
if let Some((_label_percent, label_frac)) = &app_state.canvas_data.mem_labels {
|
|
||||||
label_frac.trim()
|
|
||||||
} else {
|
|
||||||
EMPTY_MEMORY_FRAC_STRING
|
|
||||||
};
|
|
||||||
|
|
||||||
let trimmed_swap_frac =
|
|
||||||
if let Some((_label_percent, label_frac)) = &app_state.canvas_data.swap_labels {
|
|
||||||
label_frac.trim()
|
|
||||||
} else {
|
|
||||||
EMPTY_MEMORY_FRAC_STRING
|
|
||||||
};
|
|
||||||
|
|
||||||
// +7 due to 3 + 2 + 2 columns for the name & space + bar bounds + margin spacing
|
|
||||||
// Then + length of fraction
|
|
||||||
let ram_bar_length =
|
|
||||||
usize::from(draw_loc.width.saturating_sub(7)).saturating_sub(trimmed_memory_frac.len());
|
|
||||||
let swap_bar_length =
|
|
||||||
usize::from(draw_loc.width.saturating_sub(7)).saturating_sub(trimmed_swap_frac.len());
|
|
||||||
|
|
||||||
let num_bars_ram = calculate_basic_use_bars(ram_use_percentage, ram_bar_length);
|
|
||||||
let num_bars_swap = calculate_basic_use_bars(swap_use_percentage, swap_bar_length);
|
|
||||||
// TODO: Use different styling for the frac.
|
|
||||||
let mem_label = if app_state.basic_mode_use_percent {
|
|
||||||
format!(
|
|
||||||
"RAM[{}{}{:3.0}%]\n",
|
|
||||||
"|".repeat(num_bars_ram),
|
|
||||||
" ".repeat(ram_bar_length - num_bars_ram + trimmed_memory_frac.len() - 4),
|
|
||||||
ram_use_percentage.round()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"RAM[{}{}{}]\n",
|
|
||||||
"|".repeat(num_bars_ram),
|
|
||||||
" ".repeat(ram_bar_length - num_bars_ram),
|
|
||||||
trimmed_memory_frac
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let swap_label = if app_state.basic_mode_use_percent {
|
|
||||||
format!(
|
|
||||||
"SWP[{}{}{:3.0}%]",
|
|
||||||
"|".repeat(num_bars_swap),
|
|
||||||
" ".repeat(swap_bar_length - num_bars_swap + trimmed_swap_frac.len() - 4),
|
|
||||||
swap_use_percentage.round()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"SWP[{}{}{}]",
|
|
||||||
"|".repeat(num_bars_swap),
|
|
||||||
" ".repeat(swap_bar_length - num_bars_swap),
|
|
||||||
trimmed_swap_frac
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mem_text = vec![
|
|
||||||
Spans::from(Span::styled(mem_label, self.colours.ram_style)),
|
|
||||||
Spans::from(Span::styled(swap_label, self.colours.swap_style)),
|
|
||||||
];
|
|
||||||
|
|
||||||
|
if app_state.current_widget.widget_id == widget_id {
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Paragraph::new(mem_text).block(Block::default()),
|
Block::default()
|
||||||
margined_loc[0],
|
.borders(*SIDE_BORDERS)
|
||||||
|
.border_style(painter.colours.highlighted_border_style),
|
||||||
|
draw_loc,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Update draw loc in widget map
|
let ram_use_percentage = if let Some(mem) = mem_data.last() {
|
||||||
if app_state.should_get_widget_bounds() {
|
mem.1
|
||||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
} else {
|
||||||
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
0.0
|
||||||
widget.bottom_right_corner =
|
};
|
||||||
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
let swap_use_percentage = if let Some(swap) = swap_data.last() {
|
||||||
}
|
swap.1
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
|
const EMPTY_MEMORY_FRAC_STRING: &str = "0.0B/0.0B";
|
||||||
|
|
||||||
|
let trimmed_memory_frac =
|
||||||
|
if let Some((_label_percent, label_frac)) = &app_state.canvas_data.mem_labels {
|
||||||
|
label_frac.trim()
|
||||||
|
} else {
|
||||||
|
EMPTY_MEMORY_FRAC_STRING
|
||||||
|
};
|
||||||
|
|
||||||
|
let trimmed_swap_frac =
|
||||||
|
if let Some((_label_percent, label_frac)) = &app_state.canvas_data.swap_labels {
|
||||||
|
label_frac.trim()
|
||||||
|
} else {
|
||||||
|
EMPTY_MEMORY_FRAC_STRING
|
||||||
|
};
|
||||||
|
|
||||||
|
// +7 due to 3 + 2 + 2 columns for the name & space + bar bounds + margin spacing
|
||||||
|
// Then + length of fraction
|
||||||
|
let ram_bar_length =
|
||||||
|
usize::from(draw_loc.width.saturating_sub(7)).saturating_sub(trimmed_memory_frac.len());
|
||||||
|
let swap_bar_length =
|
||||||
|
usize::from(draw_loc.width.saturating_sub(7)).saturating_sub(trimmed_swap_frac.len());
|
||||||
|
|
||||||
|
let num_bars_ram = calculate_basic_use_bars(ram_use_percentage, ram_bar_length);
|
||||||
|
let num_bars_swap = calculate_basic_use_bars(swap_use_percentage, swap_bar_length);
|
||||||
|
// TODO: Use different styling for the frac.
|
||||||
|
let mem_label = if app_state.basic_mode_use_percent {
|
||||||
|
format!(
|
||||||
|
"RAM[{}{}{:3.0}%]\n",
|
||||||
|
"|".repeat(num_bars_ram),
|
||||||
|
" ".repeat(ram_bar_length - num_bars_ram + trimmed_memory_frac.len() - 4),
|
||||||
|
ram_use_percentage.round()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"RAM[{}{}{}]\n",
|
||||||
|
"|".repeat(num_bars_ram),
|
||||||
|
" ".repeat(ram_bar_length - num_bars_ram),
|
||||||
|
trimmed_memory_frac
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let swap_label = if app_state.basic_mode_use_percent {
|
||||||
|
format!(
|
||||||
|
"SWP[{}{}{:3.0}%]",
|
||||||
|
"|".repeat(num_bars_swap),
|
||||||
|
" ".repeat(swap_bar_length - num_bars_swap + trimmed_swap_frac.len() - 4),
|
||||||
|
swap_use_percentage.round()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"SWP[{}{}{}]",
|
||||||
|
"|".repeat(num_bars_swap),
|
||||||
|
" ".repeat(swap_bar_length - num_bars_swap),
|
||||||
|
trimmed_swap_frac
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mem_text = vec![
|
||||||
|
Spans::from(Span::styled(mem_label, painter.colours.ram_style)),
|
||||||
|
Spans::from(Span::styled(swap_label, painter.colours.swap_style)),
|
||||||
|
];
|
||||||
|
|
||||||
|
f.render_widget(
|
||||||
|
Paragraph::new(mem_text).block(Block::default()),
|
||||||
|
margined_loc[0],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update draw loc in widget map
|
||||||
|
if app_state.should_get_widget_bounds() {
|
||||||
|
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||||
|
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
||||||
|
widget.bottom_right_corner =
|
||||||
|
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,235 +15,226 @@ use tui::{
|
|||||||
};
|
};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
pub trait MemGraphWidget {
|
pub fn draw_memory_graph<B: Backend>(
|
||||||
fn draw_memory_graph<B: Backend>(
|
painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect,
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
widget_id: u64,
|
||||||
);
|
) {
|
||||||
}
|
if let Some(mem_widget_state) = app_state.mem_state.widget_states.get_mut(&widget_id) {
|
||||||
|
let mem_data: &mut [(f64, f64)] = &mut app_state.canvas_data.mem_data;
|
||||||
|
let swap_data: &mut [(f64, f64)] = &mut app_state.canvas_data.swap_data;
|
||||||
|
|
||||||
impl MemGraphWidget for Painter {
|
let time_start = -(mem_widget_state.current_display_time as f64);
|
||||||
fn draw_memory_graph<B: Backend>(
|
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
|
||||||
) {
|
|
||||||
if let Some(mem_widget_state) = app_state.mem_state.widget_states.get_mut(&widget_id) {
|
|
||||||
let mem_data: &mut [(f64, f64)] = &mut app_state.canvas_data.mem_data;
|
|
||||||
let swap_data: &mut [(f64, f64)] = &mut app_state.canvas_data.swap_data;
|
|
||||||
|
|
||||||
let time_start = -(mem_widget_state.current_display_time as f64);
|
let display_time_labels = vec![
|
||||||
|
Span::styled(
|
||||||
|
format!("{}s", mem_widget_state.current_display_time / 1000),
|
||||||
|
painter.colours.graph_style,
|
||||||
|
),
|
||||||
|
Span::styled("0s".to_string(), painter.colours.graph_style),
|
||||||
|
];
|
||||||
|
let y_axis_label = vec![
|
||||||
|
Span::styled(" 0%", painter.colours.graph_style),
|
||||||
|
Span::styled("100%", painter.colours.graph_style),
|
||||||
|
];
|
||||||
|
|
||||||
let display_time_labels = vec![
|
let x_axis = if app_state.app_config_fields.hide_time
|
||||||
Span::styled(
|
|| (app_state.app_config_fields.autohide_time
|
||||||
format!("{}s", mem_widget_state.current_display_time / 1000),
|
&& mem_widget_state.autohide_timer.is_none())
|
||||||
self.colours.graph_style,
|
{
|
||||||
),
|
Axis::default().bounds([time_start, 0.0])
|
||||||
Span::styled("0s".to_string(), self.colours.graph_style),
|
} else if let Some(time) = mem_widget_state.autohide_timer {
|
||||||
];
|
if std::time::Instant::now().duration_since(time).as_millis()
|
||||||
let y_axis_label = vec![
|
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
|
||||||
Span::styled(" 0%", self.colours.graph_style),
|
|
||||||
Span::styled("100%", self.colours.graph_style),
|
|
||||||
];
|
|
||||||
|
|
||||||
let x_axis = if app_state.app_config_fields.hide_time
|
|
||||||
|| (app_state.app_config_fields.autohide_time
|
|
||||||
&& mem_widget_state.autohide_timer.is_none())
|
|
||||||
{
|
{
|
||||||
Axis::default().bounds([time_start, 0.0])
|
|
||||||
} else if let Some(time) = mem_widget_state.autohide_timer {
|
|
||||||
if std::time::Instant::now().duration_since(time).as_millis()
|
|
||||||
< AUTOHIDE_TIMEOUT_MILLISECONDS as u128
|
|
||||||
{
|
|
||||||
Axis::default()
|
|
||||||
.bounds([time_start, 0.0])
|
|
||||||
.style(self.colours.graph_style)
|
|
||||||
.labels(display_time_labels)
|
|
||||||
} else {
|
|
||||||
mem_widget_state.autohide_timer = None;
|
|
||||||
Axis::default().bounds([time_start, 0.0])
|
|
||||||
}
|
|
||||||
} else if draw_loc.height < TIME_LABEL_HEIGHT_LIMIT {
|
|
||||||
Axis::default().bounds([time_start, 0.0])
|
|
||||||
} else {
|
|
||||||
Axis::default()
|
Axis::default()
|
||||||
.bounds([time_start, 0.0])
|
.bounds([time_start, 0.0])
|
||||||
.style(self.colours.graph_style)
|
.style(painter.colours.graph_style)
|
||||||
.labels(display_time_labels)
|
.labels(display_time_labels)
|
||||||
};
|
} else {
|
||||||
|
mem_widget_state.autohide_timer = None;
|
||||||
|
Axis::default().bounds([time_start, 0.0])
|
||||||
|
}
|
||||||
|
} else if draw_loc.height < TIME_LABEL_HEIGHT_LIMIT {
|
||||||
|
Axis::default().bounds([time_start, 0.0])
|
||||||
|
} else {
|
||||||
|
Axis::default()
|
||||||
|
.bounds([time_start, 0.0])
|
||||||
|
.style(painter.colours.graph_style)
|
||||||
|
.labels(display_time_labels)
|
||||||
|
};
|
||||||
|
|
||||||
let y_axis = Axis::default()
|
let y_axis = Axis::default()
|
||||||
.style(self.colours.graph_style)
|
.style(painter.colours.graph_style)
|
||||||
.bounds([0.0, 100.5])
|
.bounds([0.0, 100.5])
|
||||||
.labels(y_axis_label);
|
.labels(y_axis_label);
|
||||||
|
|
||||||
// Interpolate values to avoid ugly gaps
|
// Interpolate values to avoid ugly gaps
|
||||||
let interpolated_mem_point = if let Some(end_pos) = mem_data
|
let interpolated_mem_point = if let Some(end_pos) = mem_data
|
||||||
.iter()
|
.iter()
|
||||||
.position(|(time, _data)| *time >= time_start)
|
.position(|(time, _data)| *time >= time_start)
|
||||||
{
|
{
|
||||||
if end_pos > 1 {
|
if end_pos > 1 {
|
||||||
let start_pos = end_pos - 1;
|
let start_pos = end_pos - 1;
|
||||||
let outside_point = mem_data.get(start_pos);
|
let outside_point = mem_data.get(start_pos);
|
||||||
let inside_point = mem_data.get(end_pos);
|
let inside_point = mem_data.get(end_pos);
|
||||||
|
|
||||||
if let (Some(outside_point), Some(inside_point)) = (outside_point, inside_point)
|
if let (Some(outside_point), Some(inside_point)) = (outside_point, inside_point) {
|
||||||
{
|
let old = *outside_point;
|
||||||
let old = *outside_point;
|
|
||||||
|
|
||||||
let new_point = (
|
let new_point = (
|
||||||
time_start,
|
time_start,
|
||||||
interpolate_points(outside_point, inside_point, time_start),
|
interpolate_points(outside_point, inside_point, time_start),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(to_replace) = mem_data.get_mut(start_pos) {
|
if let Some(to_replace) = mem_data.get_mut(start_pos) {
|
||||||
*to_replace = new_point;
|
*to_replace = new_point;
|
||||||
Some((start_pos, old))
|
Some((start_pos, old))
|
||||||
} else {
|
|
||||||
None // Failed to get mutable reference.
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None // Point somehow doesn't exist in our data
|
None // Failed to get mutable reference.
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None // Point is already "leftmost", no need to interpolate.
|
None // Point somehow doesn't exist in our data
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None // There is no point.
|
None // Point is already "leftmost", no need to interpolate.
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
|
None // There is no point.
|
||||||
|
};
|
||||||
|
|
||||||
let interpolated_swap_point = if let Some(end_pos) = swap_data
|
let interpolated_swap_point = if let Some(end_pos) = swap_data
|
||||||
.iter()
|
.iter()
|
||||||
.position(|(time, _data)| *time >= time_start)
|
.position(|(time, _data)| *time >= time_start)
|
||||||
{
|
{
|
||||||
if end_pos > 1 {
|
if end_pos > 1 {
|
||||||
let start_pos = end_pos - 1;
|
let start_pos = end_pos - 1;
|
||||||
let outside_point = swap_data.get(start_pos);
|
let outside_point = swap_data.get(start_pos);
|
||||||
let inside_point = swap_data.get(end_pos);
|
let inside_point = swap_data.get(end_pos);
|
||||||
|
|
||||||
if let (Some(outside_point), Some(inside_point)) = (outside_point, inside_point)
|
if let (Some(outside_point), Some(inside_point)) = (outside_point, inside_point) {
|
||||||
{
|
let old = *outside_point;
|
||||||
let old = *outside_point;
|
|
||||||
|
|
||||||
let new_point = (
|
let new_point = (
|
||||||
time_start,
|
time_start,
|
||||||
interpolate_points(outside_point, inside_point, time_start),
|
interpolate_points(outside_point, inside_point, time_start),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(to_replace) = swap_data.get_mut(start_pos) {
|
if let Some(to_replace) = swap_data.get_mut(start_pos) {
|
||||||
*to_replace = new_point;
|
*to_replace = new_point;
|
||||||
Some((start_pos, old))
|
Some((start_pos, old))
|
||||||
} else {
|
|
||||||
None // Failed to get mutable reference.
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None // Point somehow doesn't exist in our data
|
None // Failed to get mutable reference.
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None // Point is already "leftmost", no need to interpolate.
|
None // Point somehow doesn't exist in our data
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None // There is no point.
|
None // Point is already "leftmost", no need to interpolate.
|
||||||
};
|
|
||||||
|
|
||||||
let mut mem_canvas_vec: Vec<Dataset<'_>> = vec![];
|
|
||||||
|
|
||||||
if let Some((label_percent, label_frac)) = &app_state.canvas_data.mem_labels {
|
|
||||||
let mem_label = format!("RAM:{}{}", label_percent, label_frac);
|
|
||||||
mem_canvas_vec.push(
|
|
||||||
Dataset::default()
|
|
||||||
.name(mem_label)
|
|
||||||
.marker(if app_state.app_config_fields.use_dot {
|
|
||||||
Marker::Dot
|
|
||||||
} else {
|
|
||||||
Marker::Braille
|
|
||||||
})
|
|
||||||
.style(self.colours.ram_style)
|
|
||||||
.data(mem_data)
|
|
||||||
.graph_type(tui::widgets::GraphType::Line),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
None // There is no point.
|
||||||
|
};
|
||||||
|
|
||||||
if let Some((label_percent, label_frac)) = &app_state.canvas_data.swap_labels {
|
let mut mem_canvas_vec: Vec<Dataset<'_>> = vec![];
|
||||||
let swap_label = format!("SWP:{}{}", label_percent, label_frac);
|
|
||||||
mem_canvas_vec.push(
|
|
||||||
Dataset::default()
|
|
||||||
.name(swap_label)
|
|
||||||
.marker(if app_state.app_config_fields.use_dot {
|
|
||||||
Marker::Dot
|
|
||||||
} else {
|
|
||||||
Marker::Braille
|
|
||||||
})
|
|
||||||
.style(self.colours.swap_style)
|
|
||||||
.data(swap_data)
|
|
||||||
.graph_type(tui::widgets::GraphType::Line),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
if let Some((label_percent, label_frac)) = &app_state.canvas_data.mem_labels {
|
||||||
let border_style = if is_on_widget {
|
let mem_label = format!("RAM:{}{}", label_percent, label_frac);
|
||||||
self.colours.highlighted_border_style
|
mem_canvas_vec.push(
|
||||||
} else {
|
Dataset::default()
|
||||||
self.colours.border_style
|
.name(mem_label)
|
||||||
};
|
.marker(if app_state.app_config_fields.use_dot {
|
||||||
|
Marker::Dot
|
||||||
let title = if app_state.is_expanded {
|
} else {
|
||||||
const TITLE_BASE: &str = " Memory ── Esc to go back ";
|
Marker::Braille
|
||||||
Spans::from(vec![
|
})
|
||||||
Span::styled(" Memory ", self.colours.widget_title_style),
|
.style(painter.colours.ram_style)
|
||||||
Span::styled(
|
.data(mem_data)
|
||||||
format!(
|
.graph_type(tui::widgets::GraphType::Line),
|
||||||
"─{}─ Esc to go back ",
|
|
||||||
"─".repeat(usize::from(draw_loc.width).saturating_sub(
|
|
||||||
UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
|
|
||||||
))
|
|
||||||
),
|
|
||||||
border_style,
|
|
||||||
),
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
Spans::from(Span::styled(
|
|
||||||
" Memory ".to_string(),
|
|
||||||
self.colours.widget_title_style,
|
|
||||||
))
|
|
||||||
};
|
|
||||||
|
|
||||||
f.render_widget(
|
|
||||||
Chart::new(mem_canvas_vec)
|
|
||||||
.block(
|
|
||||||
Block::default()
|
|
||||||
.title(title)
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.border_style(if app_state.current_widget.widget_id == widget_id {
|
|
||||||
self.colours.highlighted_border_style
|
|
||||||
} else {
|
|
||||||
self.colours.border_style
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.x_axis(x_axis)
|
|
||||||
.y_axis(y_axis)
|
|
||||||
.hidden_legend_constraints((Constraint::Ratio(3, 4), Constraint::Ratio(3, 4))),
|
|
||||||
draw_loc,
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Now if you're done, reset any interpolated points!
|
if let Some((label_percent, label_frac)) = &app_state.canvas_data.swap_labels {
|
||||||
if let Some((index, old_value)) = interpolated_mem_point {
|
let swap_label = format!("SWP:{}{}", label_percent, label_frac);
|
||||||
if let Some(to_replace) = mem_data.get_mut(index) {
|
mem_canvas_vec.push(
|
||||||
*to_replace = old_value;
|
Dataset::default()
|
||||||
}
|
.name(swap_label)
|
||||||
}
|
.marker(if app_state.app_config_fields.use_dot {
|
||||||
|
Marker::Dot
|
||||||
|
} else {
|
||||||
|
Marker::Braille
|
||||||
|
})
|
||||||
|
.style(painter.colours.swap_style)
|
||||||
|
.data(swap_data)
|
||||||
|
.graph_type(tui::widgets::GraphType::Line),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some((index, old_value)) = interpolated_swap_point {
|
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||||
if let Some(to_replace) = swap_data.get_mut(index) {
|
let border_style = if is_on_widget {
|
||||||
*to_replace = old_value;
|
painter.colours.highlighted_border_style
|
||||||
}
|
} else {
|
||||||
|
painter.colours.border_style
|
||||||
|
};
|
||||||
|
|
||||||
|
let title = if app_state.is_expanded {
|
||||||
|
const TITLE_BASE: &str = " Memory ── Esc to go back ";
|
||||||
|
Spans::from(vec![
|
||||||
|
Span::styled(" Memory ", painter.colours.widget_title_style),
|
||||||
|
Span::styled(
|
||||||
|
format!(
|
||||||
|
"─{}─ Esc to go back ",
|
||||||
|
"─".repeat(usize::from(draw_loc.width).saturating_sub(
|
||||||
|
UnicodeSegmentation::graphemes(TITLE_BASE, true).count() + 2
|
||||||
|
))
|
||||||
|
),
|
||||||
|
border_style,
|
||||||
|
),
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
Spans::from(Span::styled(
|
||||||
|
" Memory ".to_string(),
|
||||||
|
painter.colours.widget_title_style,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
f.render_widget(
|
||||||
|
Chart::new(mem_canvas_vec)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.title(title)
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(if app_state.current_widget.widget_id == widget_id {
|
||||||
|
painter.colours.highlighted_border_style
|
||||||
|
} else {
|
||||||
|
painter.colours.border_style
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.x_axis(x_axis)
|
||||||
|
.y_axis(y_axis)
|
||||||
|
.hidden_legend_constraints((Constraint::Ratio(3, 4), Constraint::Ratio(3, 4))),
|
||||||
|
draw_loc,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now if you're done, reset any interpolated points!
|
||||||
|
if let Some((index, old_value)) = interpolated_mem_point {
|
||||||
|
if let Some(to_replace) = mem_data.get_mut(index) {
|
||||||
|
*to_replace = old_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if app_state.should_get_widget_bounds() {
|
if let Some((index, old_value)) = interpolated_swap_point {
|
||||||
// Update draw loc in widget map
|
if let Some(to_replace) = swap_data.get_mut(index) {
|
||||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
*to_replace = old_value;
|
||||||
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
|
||||||
widget.bottom_right_corner =
|
|
||||||
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if app_state.should_get_widget_bounds() {
|
||||||
|
// Update draw loc in widget map
|
||||||
|
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||||
|
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
||||||
|
widget.bottom_right_corner =
|
||||||
|
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,71 +8,64 @@ use tui::{
|
|||||||
widgets::{Block, Paragraph},
|
widgets::{Block, Paragraph},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait NetworkBasicWidget {
|
pub fn draw_basic_network<B: Backend>(
|
||||||
fn draw_basic_network<B: Backend>(
|
painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect,
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
widget_id: u64,
|
||||||
);
|
) {
|
||||||
}
|
let divided_loc = Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||||
|
.split(draw_loc);
|
||||||
|
|
||||||
impl NetworkBasicWidget for Painter {
|
let net_loc = Layout::default()
|
||||||
fn draw_basic_network<B: Backend>(
|
.direction(Direction::Horizontal)
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut AppState, draw_loc: Rect, widget_id: u64,
|
.constraints([Constraint::Percentage(100)])
|
||||||
) {
|
.horizontal_margin(1)
|
||||||
let divided_loc = Layout::default()
|
.split(divided_loc[0]);
|
||||||
.direction(Direction::Horizontal)
|
|
||||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
|
||||||
.split(draw_loc);
|
|
||||||
|
|
||||||
let net_loc = Layout::default()
|
let total_loc = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.constraints([Constraint::Percentage(100)])
|
.constraints([Constraint::Percentage(100)])
|
||||||
.horizontal_margin(1)
|
.horizontal_margin(1)
|
||||||
.split(divided_loc[0]);
|
.split(divided_loc[1]);
|
||||||
|
|
||||||
let total_loc = Layout::default()
|
|
||||||
.direction(Direction::Horizontal)
|
|
||||||
.constraints([Constraint::Percentage(100)])
|
|
||||||
.horizontal_margin(1)
|
|
||||||
.split(divided_loc[1]);
|
|
||||||
|
|
||||||
if app_state.current_widget.widget_id == widget_id {
|
|
||||||
f.render_widget(
|
|
||||||
Block::default()
|
|
||||||
.borders(*SIDE_BORDERS)
|
|
||||||
.border_style(self.colours.highlighted_border_style),
|
|
||||||
draw_loc,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let rx_label = format!("RX: {}", &app_state.canvas_data.rx_display);
|
|
||||||
let tx_label = format!("TX: {}", &app_state.canvas_data.tx_display);
|
|
||||||
let total_rx_label = format!("Total RX: {}", &app_state.canvas_data.total_rx_display);
|
|
||||||
let total_tx_label = format!("Total TX: {}", &app_state.canvas_data.total_tx_display);
|
|
||||||
|
|
||||||
let net_text = vec![
|
|
||||||
Spans::from(Span::styled(rx_label, self.colours.rx_style)),
|
|
||||||
Spans::from(Span::styled(tx_label, self.colours.tx_style)),
|
|
||||||
];
|
|
||||||
|
|
||||||
let total_net_text = vec![
|
|
||||||
Spans::from(Span::styled(total_rx_label, self.colours.total_rx_style)),
|
|
||||||
Spans::from(Span::styled(total_tx_label, self.colours.total_tx_style)),
|
|
||||||
];
|
|
||||||
|
|
||||||
f.render_widget(Paragraph::new(net_text).block(Block::default()), net_loc[0]);
|
|
||||||
|
|
||||||
|
if app_state.current_widget.widget_id == widget_id {
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Paragraph::new(total_net_text).block(Block::default()),
|
Block::default()
|
||||||
total_loc[0],
|
.borders(*SIDE_BORDERS)
|
||||||
|
.border_style(painter.colours.highlighted_border_style),
|
||||||
|
draw_loc,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Update draw loc in widget map
|
let rx_label = format!("RX: {}", &app_state.canvas_data.rx_display);
|
||||||
if app_state.should_get_widget_bounds() {
|
let tx_label = format!("TX: {}", &app_state.canvas_data.tx_display);
|
||||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
let total_rx_label = format!("Total RX: {}", &app_state.canvas_data.total_rx_display);
|
||||||
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
let total_tx_label = format!("Total TX: {}", &app_state.canvas_data.total_tx_display);
|
||||||
widget.bottom_right_corner =
|
|
||||||
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
let net_text = vec![
|
||||||
}
|
Spans::from(Span::styled(rx_label, painter.colours.rx_style)),
|
||||||
|
Spans::from(Span::styled(tx_label, painter.colours.tx_style)),
|
||||||
|
];
|
||||||
|
|
||||||
|
let total_net_text = vec![
|
||||||
|
Spans::from(Span::styled(total_rx_label, painter.colours.total_rx_style)),
|
||||||
|
Spans::from(Span::styled(total_tx_label, painter.colours.total_tx_style)),
|
||||||
|
];
|
||||||
|
|
||||||
|
f.render_widget(Paragraph::new(net_text).block(Block::default()), net_loc[0]);
|
||||||
|
|
||||||
|
f.render_widget(
|
||||||
|
Paragraph::new(total_net_text).block(Block::default()),
|
||||||
|
total_loc[0],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update draw loc in widget map
|
||||||
|
if app_state.should_get_widget_bounds() {
|
||||||
|
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||||
|
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
|
||||||
|
widget.bottom_right_corner =
|
||||||
|
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -27,105 +27,97 @@ static TEMP_HEADERS_LENS: Lazy<Vec<u16>> = Lazy::new(|| {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
});
|
});
|
||||||
|
|
||||||
pub trait TempTableWidget {
|
pub fn draw_temp_table<B: Backend>(
|
||||||
fn draw_temp_table<B: Backend>(
|
painter: &Painter, f: &mut Frame<'_, B>, app_state: &mut app::AppState, draw_loc: Rect,
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::AppState, draw_loc: Rect,
|
draw_border: bool, widget_id: u64,
|
||||||
draw_border: bool, widget_id: u64,
|
) {
|
||||||
);
|
let recalculate_column_widths = app_state.should_get_widget_bounds();
|
||||||
}
|
if let Some(temp_widget_state) = app_state.temp_state.widget_states.get_mut(&widget_id) {
|
||||||
|
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
app_state.app_config_fields.table_gap
|
||||||
|
};
|
||||||
|
let start_position = get_start_position(
|
||||||
|
usize::from(
|
||||||
|
(draw_loc.height + (1 - table_gap)).saturating_sub(painter.table_height_offset),
|
||||||
|
),
|
||||||
|
&temp_widget_state.scroll_state.scroll_direction,
|
||||||
|
&mut temp_widget_state.scroll_state.previous_scroll_position,
|
||||||
|
temp_widget_state.scroll_state.current_scroll_position,
|
||||||
|
app_state.is_force_redraw,
|
||||||
|
);
|
||||||
|
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
||||||
|
let temp_table_state = &mut temp_widget_state.scroll_state.table_state;
|
||||||
|
temp_table_state.select(Some(
|
||||||
|
temp_widget_state
|
||||||
|
.scroll_state
|
||||||
|
.current_scroll_position
|
||||||
|
.saturating_sub(start_position),
|
||||||
|
));
|
||||||
|
let sliced_vec = &app_state.canvas_data.temp_sensor_data[start_position..];
|
||||||
|
|
||||||
impl TempTableWidget for Painter {
|
// Calculate widths
|
||||||
fn draw_temp_table<B: Backend>(
|
let hard_widths = [None, None];
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::AppState, draw_loc: Rect,
|
if recalculate_column_widths {
|
||||||
draw_border: bool, widget_id: u64,
|
temp_widget_state.table_width_state.desired_column_widths = {
|
||||||
) {
|
let mut column_widths = TEMP_HEADERS_LENS.clone();
|
||||||
let recalculate_column_widths = app_state.should_get_widget_bounds();
|
for row in sliced_vec {
|
||||||
if let Some(temp_widget_state) = app_state.temp_state.widget_states.get_mut(&widget_id) {
|
for (col, entry) in row.iter().enumerate() {
|
||||||
let table_gap = if draw_loc.height < TABLE_GAP_HEIGHT_LIMIT {
|
if entry.len() as u16 > column_widths[col] {
|
||||||
0
|
column_widths[col] = entry.len() as u16;
|
||||||
} else {
|
|
||||||
app_state.app_config_fields.table_gap
|
|
||||||
};
|
|
||||||
let start_position = get_start_position(
|
|
||||||
usize::from(
|
|
||||||
(draw_loc.height + (1 - table_gap)).saturating_sub(self.table_height_offset),
|
|
||||||
),
|
|
||||||
&temp_widget_state.scroll_state.scroll_direction,
|
|
||||||
&mut temp_widget_state.scroll_state.previous_scroll_position,
|
|
||||||
temp_widget_state.scroll_state.current_scroll_position,
|
|
||||||
app_state.is_force_redraw,
|
|
||||||
);
|
|
||||||
let is_on_widget = widget_id == app_state.current_widget.widget_id;
|
|
||||||
let temp_table_state = &mut temp_widget_state.scroll_state.table_state;
|
|
||||||
temp_table_state.select(Some(
|
|
||||||
temp_widget_state
|
|
||||||
.scroll_state
|
|
||||||
.current_scroll_position
|
|
||||||
.saturating_sub(start_position),
|
|
||||||
));
|
|
||||||
let sliced_vec = &app_state.canvas_data.temp_sensor_data[start_position..];
|
|
||||||
|
|
||||||
// Calculate widths
|
|
||||||
let hard_widths = [None, None];
|
|
||||||
if recalculate_column_widths {
|
|
||||||
temp_widget_state.table_width_state.desired_column_widths = {
|
|
||||||
let mut column_widths = TEMP_HEADERS_LENS.clone();
|
|
||||||
for row in sliced_vec {
|
|
||||||
for (col, entry) in row.iter().enumerate() {
|
|
||||||
if entry.len() as u16 > column_widths[col] {
|
|
||||||
column_widths[col] = entry.len() as u16;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
column_widths
|
column_widths
|
||||||
};
|
};
|
||||||
temp_widget_state.table_width_state.calculated_column_widths = get_column_widths(
|
temp_widget_state.table_width_state.calculated_column_widths = get_column_widths(
|
||||||
draw_loc.width,
|
draw_loc.width,
|
||||||
&hard_widths,
|
&hard_widths,
|
||||||
&(TEMP_HEADERS_LENS
|
&(TEMP_HEADERS_LENS
|
||||||
.iter()
|
.iter()
|
||||||
.map(|width| Some(*width))
|
.map(|width| Some(*width))
|
||||||
.collect::<Vec<_>>()),
|
.collect::<Vec<_>>()),
|
||||||
&[Some(0.80), Some(-1.0)],
|
&[Some(0.80), Some(-1.0)],
|
||||||
&temp_widget_state
|
&temp_widget_state
|
||||||
.table_width_state
|
.table_width_state
|
||||||
.desired_column_widths
|
.desired_column_widths
|
||||||
.iter()
|
.iter()
|
||||||
.map(|width| Some(*width))
|
.map(|width| Some(*width))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let dcw = &temp_widget_state.table_width_state.desired_column_widths;
|
let dcw = &temp_widget_state.table_width_state.desired_column_widths;
|
||||||
let ccw = &temp_widget_state.table_width_state.calculated_column_widths;
|
let ccw = &temp_widget_state.table_width_state.calculated_column_widths;
|
||||||
let temperature_rows =
|
let temperature_rows = sliced_vec.iter().map(|temp_row| {
|
||||||
sliced_vec.iter().map(|temp_row| {
|
let truncated_data =
|
||||||
let truncated_data = temp_row.iter().zip(&hard_widths).enumerate().map(
|
temp_row
|
||||||
|(itx, (entry, width))| {
|
.iter()
|
||||||
if width.is_none() {
|
.zip(&hard_widths)
|
||||||
if let (Some(desired_col_width), Some(calculated_col_width)) =
|
.enumerate()
|
||||||
(dcw.get(itx), ccw.get(itx))
|
.map(|(itx, (entry, width))| {
|
||||||
|
if width.is_none() {
|
||||||
|
if let (Some(desired_col_width), Some(calculated_col_width)) =
|
||||||
|
(dcw.get(itx), ccw.get(itx))
|
||||||
|
{
|
||||||
|
if *desired_col_width > *calculated_col_width
|
||||||
|
&& *calculated_col_width > 0
|
||||||
{
|
{
|
||||||
if *desired_col_width > *calculated_col_width
|
let graphemes =
|
||||||
&& *calculated_col_width > 0
|
UnicodeSegmentation::graphemes(entry.as_str(), true)
|
||||||
{
|
.collect::<Vec<&str>>();
|
||||||
let graphemes =
|
|
||||||
UnicodeSegmentation::graphemes(entry.as_str(), true)
|
|
||||||
.collect::<Vec<&str>>();
|
|
||||||
|
|
||||||
if graphemes.len() > *calculated_col_width as usize
|
if graphemes.len() > *calculated_col_width as usize
|
||||||
&& *calculated_col_width > 1
|
&& *calculated_col_width > 1
|
||||||
{
|
{
|
||||||
// Truncate with ellipsis
|
// Truncate with ellipsis
|
||||||
let first_n = graphemes
|
let first_n = graphemes
|
||||||
[..(*calculated_col_width as usize - 1)]
|
[..(*calculated_col_width as usize - 1)]
|
||||||
.concat();
|
.concat();
|
||||||
Text::raw(format!("{}…", first_n))
|
Text::raw(format!("{}…", first_n))
|
||||||
} else {
|
|
||||||
Text::raw(entry)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Text::raw(entry)
|
Text::raw(entry)
|
||||||
}
|
}
|
||||||
@ -135,131 +127,129 @@ impl TempTableWidget for Painter {
|
|||||||
} else {
|
} else {
|
||||||
Text::raw(entry)
|
Text::raw(entry)
|
||||||
}
|
}
|
||||||
},
|
} else {
|
||||||
);
|
Text::raw(entry)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Row::new(truncated_data)
|
Row::new(truncated_data)
|
||||||
});
|
});
|
||||||
|
|
||||||
let (border_style, highlight_style) = if is_on_widget {
|
let (border_style, highlight_style) = if is_on_widget {
|
||||||
(
|
(
|
||||||
self.colours.highlighted_border_style,
|
painter.colours.highlighted_border_style,
|
||||||
self.colours.currently_selected_text_style,
|
painter.colours.currently_selected_text_style,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(self.colours.border_style, self.colours.text_style)
|
(painter.colours.border_style, painter.colours.text_style)
|
||||||
};
|
};
|
||||||
|
|
||||||
let title_base = if app_state.app_config_fields.show_table_scroll_position {
|
let title_base = if app_state.app_config_fields.show_table_scroll_position {
|
||||||
let title_string = format!(
|
let title_string = format!(
|
||||||
" Temperatures ({} of {}) ",
|
" Temperatures ({} of {}) ",
|
||||||
temp_widget_state
|
temp_widget_state
|
||||||
.scroll_state
|
.scroll_state
|
||||||
.current_scroll_position
|
.current_scroll_position
|
||||||
.saturating_add(1),
|
.saturating_add(1),
|
||||||
app_state.canvas_data.temp_sensor_data.len()
|
app_state.canvas_data.temp_sensor_data.len()
|
||||||
);
|
|
||||||
|
|
||||||
if title_string.len() <= draw_loc.width as usize {
|
|
||||||
title_string
|
|
||||||
} else {
|
|
||||||
" Temperatures ".to_string()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
" Temperatures ".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let title = if app_state.is_expanded {
|
|
||||||
const ESCAPE_ENDING: &str = "── Esc to go back ";
|
|
||||||
|
|
||||||
let (chosen_title_base, expanded_title_base) = {
|
|
||||||
let temp_title_base = format!("{}{}", title_base, ESCAPE_ENDING);
|
|
||||||
|
|
||||||
if temp_title_base.len() > draw_loc.width as usize {
|
|
||||||
(
|
|
||||||
" Temperatures ".to_string(),
|
|
||||||
format!("{}{}", " Temperatures ".to_string(), ESCAPE_ENDING),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(title_base, temp_title_base)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Spans::from(vec![
|
|
||||||
Span::styled(chosen_title_base, self.colours.widget_title_style),
|
|
||||||
Span::styled(
|
|
||||||
format!(
|
|
||||||
"─{}─ Esc to go back ",
|
|
||||||
"─".repeat(
|
|
||||||
usize::from(draw_loc.width).saturating_sub(
|
|
||||||
UnicodeSegmentation::graphemes(
|
|
||||||
expanded_title_base.as_str(),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
.count()
|
|
||||||
+ 2
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
border_style,
|
|
||||||
),
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
Spans::from(Span::styled(title_base, self.colours.widget_title_style))
|
|
||||||
};
|
|
||||||
|
|
||||||
let temp_block = if draw_border {
|
|
||||||
Block::default()
|
|
||||||
.title(title)
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.border_style(border_style)
|
|
||||||
} else if is_on_widget {
|
|
||||||
Block::default()
|
|
||||||
.borders(*SIDE_BORDERS)
|
|
||||||
.border_style(self.colours.highlighted_border_style)
|
|
||||||
} else {
|
|
||||||
Block::default().borders(Borders::NONE)
|
|
||||||
};
|
|
||||||
|
|
||||||
let margined_draw_loc = Layout::default()
|
|
||||||
.constraints([Constraint::Percentage(100)])
|
|
||||||
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
|
||||||
.direction(Direction::Horizontal)
|
|
||||||
.split(draw_loc)[0];
|
|
||||||
|
|
||||||
// Draw
|
|
||||||
f.render_stateful_widget(
|
|
||||||
Table::new(temperature_rows)
|
|
||||||
.header(
|
|
||||||
Row::new(TEMP_HEADERS.to_vec())
|
|
||||||
.style(self.colours.table_header_style)
|
|
||||||
.bottom_margin(table_gap),
|
|
||||||
)
|
|
||||||
.block(temp_block)
|
|
||||||
.highlight_style(highlight_style)
|
|
||||||
.style(self.colours.text_style)
|
|
||||||
.widths(
|
|
||||||
&(temp_widget_state
|
|
||||||
.table_width_state
|
|
||||||
.calculated_column_widths
|
|
||||||
.iter()
|
|
||||||
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
|
||||||
.collect::<Vec<_>>()),
|
|
||||||
),
|
|
||||||
margined_draw_loc,
|
|
||||||
temp_table_state,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if app_state.should_get_widget_bounds() {
|
if title_string.len() <= draw_loc.width as usize {
|
||||||
// Update draw loc in widget map
|
title_string
|
||||||
// Note there is no difference between this and using draw_loc, but I'm too lazy to fix it.
|
} else {
|
||||||
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
" Temperatures ".to_string()
|
||||||
widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y));
|
}
|
||||||
widget.bottom_right_corner = Some((
|
} else {
|
||||||
margined_draw_loc.x + margined_draw_loc.width,
|
" Temperatures ".to_string()
|
||||||
margined_draw_loc.y + margined_draw_loc.height,
|
};
|
||||||
));
|
|
||||||
|
let title = if app_state.is_expanded {
|
||||||
|
const ESCAPE_ENDING: &str = "── Esc to go back ";
|
||||||
|
|
||||||
|
let (chosen_title_base, expanded_title_base) = {
|
||||||
|
let temp_title_base = format!("{}{}", title_base, ESCAPE_ENDING);
|
||||||
|
|
||||||
|
if temp_title_base.len() > draw_loc.width as usize {
|
||||||
|
(
|
||||||
|
" Temperatures ".to_string(),
|
||||||
|
format!("{}{}", " Temperatures ".to_string(), ESCAPE_ENDING),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(title_base, temp_title_base)
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Spans::from(vec![
|
||||||
|
Span::styled(chosen_title_base, painter.colours.widget_title_style),
|
||||||
|
Span::styled(
|
||||||
|
format!(
|
||||||
|
"─{}─ Esc to go back ",
|
||||||
|
"─".repeat(
|
||||||
|
usize::from(draw_loc.width).saturating_sub(
|
||||||
|
UnicodeSegmentation::graphemes(expanded_title_base.as_str(), true)
|
||||||
|
.count()
|
||||||
|
+ 2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
border_style,
|
||||||
|
),
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
Spans::from(Span::styled(title_base, painter.colours.widget_title_style))
|
||||||
|
};
|
||||||
|
|
||||||
|
let temp_block = if draw_border {
|
||||||
|
Block::default()
|
||||||
|
.title(title)
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(border_style)
|
||||||
|
} else if is_on_widget {
|
||||||
|
Block::default()
|
||||||
|
.borders(*SIDE_BORDERS)
|
||||||
|
.border_style(painter.colours.highlighted_border_style)
|
||||||
|
} else {
|
||||||
|
Block::default().borders(Borders::NONE)
|
||||||
|
};
|
||||||
|
|
||||||
|
let margined_draw_loc = Layout::default()
|
||||||
|
.constraints([Constraint::Percentage(100)])
|
||||||
|
.horizontal_margin(if is_on_widget || draw_border { 0 } else { 1 })
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.split(draw_loc)[0];
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
f.render_stateful_widget(
|
||||||
|
Table::new(temperature_rows)
|
||||||
|
.header(
|
||||||
|
Row::new(TEMP_HEADERS.to_vec())
|
||||||
|
.style(painter.colours.table_header_style)
|
||||||
|
.bottom_margin(table_gap),
|
||||||
|
)
|
||||||
|
.block(temp_block)
|
||||||
|
.highlight_style(highlight_style)
|
||||||
|
.style(painter.colours.text_style)
|
||||||
|
.widths(
|
||||||
|
&(temp_widget_state
|
||||||
|
.table_width_state
|
||||||
|
.calculated_column_widths
|
||||||
|
.iter()
|
||||||
|
.map(|calculated_width| Constraint::Length(*calculated_width as u16))
|
||||||
|
.collect::<Vec<_>>()),
|
||||||
|
),
|
||||||
|
margined_draw_loc,
|
||||||
|
temp_table_state,
|
||||||
|
);
|
||||||
|
|
||||||
|
if app_state.should_get_widget_bounds() {
|
||||||
|
// Update draw loc in widget map
|
||||||
|
// Note there is no difference between this and using draw_loc, but I'm too lazy to fix it.
|
||||||
|
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
|
||||||
|
widget.top_left_corner = Some((margined_draw_loc.x, margined_draw_loc.y));
|
||||||
|
widget.bottom_right_corner = Some((
|
||||||
|
margined_draw_loc.x + margined_draw_loc.width,
|
||||||
|
margined_draw_loc.y + margined_draw_loc.height,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,7 @@ pub enum BottomEvent {
|
|||||||
KeyInput(KeyEvent),
|
KeyInput(KeyEvent),
|
||||||
MouseInput(MouseEvent),
|
MouseInput(MouseEvent),
|
||||||
Update(Box<data_harvester::Data>),
|
Update(Box<data_harvester::Data>),
|
||||||
|
Resize { width: u16, height: u16 },
|
||||||
Clean,
|
Clean,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -635,7 +636,11 @@ pub fn create_input_thread(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Event::Resize(_, _) => {}
|
Event::Resize(width, height) => {
|
||||||
|
if sender.send(BottomEvent::Resize { width, height }).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user