mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-04-08 17:05:59 +02:00
Layout
This commit is contained in:
parent
3dbe788920
commit
65e36901c0
43
src/app.rs
43
src/app.rs
@ -29,7 +29,6 @@ use frozen_state::FrozenState;
|
||||
use crate::{
|
||||
canvas::Painter,
|
||||
constants,
|
||||
data_conversion::ConvertedData,
|
||||
tuine::{Application, Element, Status, ViewContext},
|
||||
units::data_units::DataUnit,
|
||||
Pid,
|
||||
@ -135,6 +134,7 @@ pub enum AppMessages {
|
||||
to_kill: Vec<Pid>,
|
||||
signal: Option<i32>,
|
||||
},
|
||||
Expand,
|
||||
ToggleFreeze,
|
||||
Reset,
|
||||
Clean,
|
||||
@ -150,23 +150,16 @@ pub struct AppState {
|
||||
frozen_state: FrozenState,
|
||||
current_screen: CurrentScreen,
|
||||
pub painter: Painter,
|
||||
layout: WidgetLayoutNode,
|
||||
terminator: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
/// Creates a new [`AppState`].
|
||||
pub fn new(
|
||||
app_config: AppConfig, filters: DataFilters, layout_tree_output: LayoutCreationOutput,
|
||||
painter: Painter,
|
||||
app_config: AppConfig, filters: DataFilters, layout: WidgetLayoutNode,
|
||||
used_widgets: UsedWidgets, painter: Painter,
|
||||
) -> Result<Self> {
|
||||
let LayoutCreationOutput {
|
||||
layout_tree: _,
|
||||
root: _,
|
||||
widget_lookup_map,
|
||||
selected: _,
|
||||
used_widgets,
|
||||
} = layout_tree_output;
|
||||
|
||||
Ok(Self {
|
||||
app_config,
|
||||
filters,
|
||||
@ -177,7 +170,7 @@ impl AppState {
|
||||
data_collection: Default::default(),
|
||||
frozen_state: Default::default(),
|
||||
current_screen: Default::default(),
|
||||
|
||||
layout,
|
||||
terminator: Self::register_terminator()?,
|
||||
})
|
||||
}
|
||||
@ -221,6 +214,10 @@ impl Application for AppState {
|
||||
// FIXME: Handle process termination
|
||||
true
|
||||
}
|
||||
AppMessages::Expand => {
|
||||
// FIXME: Expand current widget
|
||||
true
|
||||
}
|
||||
AppMessages::ToggleFreeze => {
|
||||
self.frozen_state.toggle(&self.data_collection);
|
||||
true
|
||||
@ -246,7 +243,27 @@ impl Application for AppState {
|
||||
}
|
||||
|
||||
fn view<'b>(&mut self, ctx: &mut ViewContext<'_>) -> Element<Self::Message> {
|
||||
todo!()
|
||||
match self.current_screen {
|
||||
CurrentScreen::Main => {
|
||||
// The main screen.
|
||||
|
||||
todo!()
|
||||
}
|
||||
CurrentScreen::Expanded => {
|
||||
// Displayed when a user "expands" a widget
|
||||
todo!()
|
||||
}
|
||||
CurrentScreen::Help => {
|
||||
// The help dialog.
|
||||
|
||||
todo!()
|
||||
}
|
||||
CurrentScreen::Delete => {
|
||||
// The delete dialog.
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn destructor(&mut self) {
|
||||
|
@ -1,14 +1,7 @@
|
||||
use crate::{
|
||||
app::{
|
||||
BasicCpu, BasicMem, BasicNet, BatteryTable, Carousel, DiskTable, Empty, MemGraph, NetGraph,
|
||||
OldNetGraph, ProcessManager, SelectableType, TempTable,
|
||||
},
|
||||
app::SelectableType,
|
||||
error::{BottomError, Result},
|
||||
options::{
|
||||
layout_options::{LayoutRow, LayoutRowChild, LayoutRule},
|
||||
ProcessDefaults,
|
||||
},
|
||||
tuine::{Element, Flex},
|
||||
options::layout_options::{FinalWidget, LayoutRow, LayoutRowChild, LayoutRule},
|
||||
};
|
||||
use indextree::{Arena, NodeId};
|
||||
use rustc_hash::FxHashMap;
|
||||
@ -17,9 +10,7 @@ use tui::layout::Rect;
|
||||
|
||||
use crate::app::widgets::Widget;
|
||||
|
||||
use super::{
|
||||
event::SelectionAction, AppConfig, AppState, CpuGraph, OldBottomWidget, TimeGraph, UsedWidgets,
|
||||
};
|
||||
use super::{event::SelectionAction, OldBottomWidget, UsedWidgets};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum BottomWidgetType {
|
||||
@ -41,12 +32,6 @@ pub enum BottomWidgetType {
|
||||
Carousel,
|
||||
}
|
||||
|
||||
impl Default for BottomWidgetType {
|
||||
fn default() -> Self {
|
||||
BottomWidgetType::Empty
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for BottomWidgetType {
|
||||
type Err = BottomError;
|
||||
|
||||
@ -122,16 +107,6 @@ pub struct RowLayout {
|
||||
pub bound: Rect,
|
||||
}
|
||||
|
||||
impl RowLayout {
|
||||
fn new(parent_rule: LayoutRule) -> Self {
|
||||
Self {
|
||||
last_selected: None,
|
||||
parent_rule,
|
||||
bound: Rect::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a column in the layout tree.
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct ColLayout {
|
||||
@ -140,16 +115,6 @@ pub struct ColLayout {
|
||||
pub bound: Rect,
|
||||
}
|
||||
|
||||
impl ColLayout {
|
||||
fn new(parent_rule: LayoutRule) -> Self {
|
||||
Self {
|
||||
last_selected: None,
|
||||
parent_rule,
|
||||
bound: Rect::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a widget in the layout tree.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Default)]
|
||||
pub struct WidgetLayout {
|
||||
@ -178,327 +143,125 @@ pub enum MovementDirection {
|
||||
Down,
|
||||
}
|
||||
|
||||
pub fn initialize_widget_layout<Message>(
|
||||
layout_rows: &[LayoutRow], app: &AppState,
|
||||
) -> anyhow::Result<Element<Message>> {
|
||||
let mut root = Flex::column();
|
||||
|
||||
for layout_row in layout_rows {
|
||||
let mut row = Flex::row();
|
||||
if let Some(children) = &layout_row.child {
|
||||
for child in children {
|
||||
match child {
|
||||
LayoutRowChild::Widget(widget) => {}
|
||||
LayoutRowChild::Carousel {
|
||||
carousel_children,
|
||||
default,
|
||||
} => {}
|
||||
LayoutRowChild::LayoutCol {
|
||||
ratio,
|
||||
child: children,
|
||||
} => for child in children {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
root = root.with_child(row);
|
||||
}
|
||||
|
||||
Ok(root.into())
|
||||
/// An intermediate representation of the widget layout.
|
||||
pub enum WidgetLayoutNode {
|
||||
Row {
|
||||
children: Vec<WidgetLayoutNode>,
|
||||
parent_rule: LayoutRule,
|
||||
},
|
||||
Col {
|
||||
children: Vec<WidgetLayoutNode>,
|
||||
parent_rule: LayoutRule,
|
||||
},
|
||||
Carousel {
|
||||
children: Vec<BottomWidgetType>,
|
||||
selected: bool,
|
||||
},
|
||||
Widget {
|
||||
widget_type: BottomWidgetType,
|
||||
selected: bool,
|
||||
width_rule: LayoutRule,
|
||||
height_rule: LayoutRule,
|
||||
},
|
||||
}
|
||||
|
||||
/// A wrapper struct to simplify the output of [`create_layout_tree`].
|
||||
pub struct LayoutCreationOutput {
|
||||
pub layout_tree: Arena<LayoutNode>,
|
||||
pub root: NodeId,
|
||||
pub widget_lookup_map: FxHashMap<NodeId, OldBottomWidget>,
|
||||
pub selected: NodeId,
|
||||
pub used_widgets: UsedWidgets,
|
||||
}
|
||||
|
||||
/// Creates a new [`Arena<LayoutNode>`] from the given config and returns it, along with the [`NodeId`] representing
|
||||
/// the root of the newly created [`Arena`], a mapping from [`NodeId`]s to [`BottomWidget`]s, and optionally, a default
|
||||
/// selected [`NodeId`].
|
||||
// FIXME: [AFTER REFACTOR] This is currently jury-rigged "glue" just to work with the existing config system! We are NOT keeping it like this, it's too awful to keep like this!
|
||||
pub fn create_layout_tree(
|
||||
rows: &[LayoutRow], process_defaults: ProcessDefaults, app_config_fields: &AppConfig,
|
||||
) -> Result<LayoutCreationOutput> {
|
||||
fn add_widget_to_map(
|
||||
widget_lookup_map: &mut FxHashMap<NodeId, OldBottomWidget>, widget_type: BottomWidgetType,
|
||||
widget_id: NodeId, process_defaults: &ProcessDefaults, app_config_fields: &AppConfig,
|
||||
width: LayoutRule, height: LayoutRule,
|
||||
) -> Result<()> {
|
||||
match widget_type {
|
||||
BottomWidgetType::Cpu => {
|
||||
widget_lookup_map.insert(
|
||||
widget_id,
|
||||
CpuGraph::from_config(app_config_fields)
|
||||
.width(width)
|
||||
.height(height)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Mem => {
|
||||
let graph = TimeGraph::from_config(app_config_fields);
|
||||
widget_lookup_map.insert(
|
||||
widget_id,
|
||||
MemGraph::new(graph).width(width).height(height).into(),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Net => {
|
||||
if app_config_fields.use_old_network_legend {
|
||||
widget_lookup_map.insert(
|
||||
widget_id,
|
||||
OldNetGraph::from_config(app_config_fields)
|
||||
.width(width)
|
||||
.height(height)
|
||||
.into(),
|
||||
);
|
||||
} else {
|
||||
widget_lookup_map.insert(
|
||||
widget_id,
|
||||
NetGraph::from_config(app_config_fields)
|
||||
.width(width)
|
||||
.height(height)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
BottomWidgetType::Proc => {
|
||||
widget_lookup_map.insert(
|
||||
widget_id,
|
||||
ProcessManager::new(process_defaults, app_config_fields)
|
||||
.width(width)
|
||||
.height(height)
|
||||
.basic_mode(app_config_fields.use_basic_mode)
|
||||
.show_scroll_index(app_config_fields.show_table_scroll_position)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Temp => {
|
||||
widget_lookup_map.insert(
|
||||
widget_id,
|
||||
TempTable::from_config(app_config_fields)
|
||||
.set_temp_type(app_config_fields.temperature_type.clone())
|
||||
.width(width)
|
||||
.height(height)
|
||||
.basic_mode(app_config_fields.use_basic_mode)
|
||||
.show_scroll_index(app_config_fields.show_table_scroll_position)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Disk => {
|
||||
widget_lookup_map.insert(
|
||||
widget_id,
|
||||
DiskTable::from_config(app_config_fields)
|
||||
.width(width)
|
||||
.height(height)
|
||||
.basic_mode(app_config_fields.use_basic_mode)
|
||||
.show_scroll_index(app_config_fields.show_table_scroll_position)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Battery => {
|
||||
widget_lookup_map.insert(
|
||||
widget_id,
|
||||
BatteryTable::default()
|
||||
.width(width)
|
||||
.height(height)
|
||||
.basic_mode(app_config_fields.use_basic_mode)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::BasicCpu => {
|
||||
widget_lookup_map.insert(
|
||||
widget_id,
|
||||
BasicCpu::from_config(app_config_fields).width(width).into(),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::BasicMem => {
|
||||
widget_lookup_map.insert(widget_id, BasicMem::default().width(width).into());
|
||||
}
|
||||
BottomWidgetType::BasicNet => {
|
||||
widget_lookup_map.insert(
|
||||
widget_id,
|
||||
BasicNet::from_config(app_config_fields).width(width).into(),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Empty => {
|
||||
widget_lookup_map.insert(
|
||||
widget_id,
|
||||
Empty::default().width(width).height(height).into(),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let mut arena = Arena::new();
|
||||
let root_id = arena.new_node(LayoutNode::Col(ColLayout::new(LayoutRule::Expand {
|
||||
ratio: 1,
|
||||
})));
|
||||
let mut widget_lookup_map = FxHashMap::default();
|
||||
let mut first_selected = None;
|
||||
let mut first_widget_seen = None; // Backup selected widget
|
||||
/// Parses the layout in the config into an intermediate representation.
|
||||
pub fn parse_widget_layout(
|
||||
layout_rows: &[LayoutRow],
|
||||
) -> anyhow::Result<(WidgetLayoutNode, UsedWidgets)> {
|
||||
let mut root_children = Vec::with_capacity(layout_rows.len());
|
||||
let mut used_widgets = UsedWidgets::default();
|
||||
|
||||
for row in rows {
|
||||
let row_id = arena.new_node(LayoutNode::Row(RowLayout::new(
|
||||
row.ratio
|
||||
.map(|ratio| LayoutRule::Expand { ratio })
|
||||
.unwrap_or(LayoutRule::Child),
|
||||
)));
|
||||
root_id.append(row_id, &mut arena);
|
||||
for layout_row in layout_rows {
|
||||
if let Some(children) = &layout_row.child {
|
||||
let mut row_children = Vec::with_capacity(children.len());
|
||||
|
||||
if let Some(children) = &row.child {
|
||||
for child in children {
|
||||
match child {
|
||||
LayoutRowChild::Widget(widget) => {
|
||||
let widget_id = arena.new_node(LayoutNode::Widget(WidgetLayout::default()));
|
||||
row_id.append(widget_id, &mut arena);
|
||||
let FinalWidget {
|
||||
rule,
|
||||
widget_type,
|
||||
default,
|
||||
} = widget;
|
||||
|
||||
if let Some(true) = widget.default {
|
||||
first_selected = Some(widget_id);
|
||||
}
|
||||
|
||||
if first_widget_seen.is_none() {
|
||||
first_widget_seen = Some(widget_id);
|
||||
}
|
||||
|
||||
let widget_type = widget.widget_type.parse::<BottomWidgetType>()?;
|
||||
let widget_type = widget_type.parse::<BottomWidgetType>()?;
|
||||
used_widgets.add(&widget_type);
|
||||
|
||||
add_widget_to_map(
|
||||
&mut widget_lookup_map,
|
||||
row_children.push(WidgetLayoutNode::Widget {
|
||||
widget_type,
|
||||
widget_id,
|
||||
&process_defaults,
|
||||
app_config_fields,
|
||||
widget.rule.unwrap_or_default(),
|
||||
LayoutRule::default(),
|
||||
)?;
|
||||
selected: default.unwrap_or(false),
|
||||
width_rule: rule.unwrap_or_default(),
|
||||
height_rule: LayoutRule::default(),
|
||||
});
|
||||
}
|
||||
LayoutRowChild::Carousel {
|
||||
carousel_children,
|
||||
default,
|
||||
} => {
|
||||
if !carousel_children.is_empty() {
|
||||
let mut child_ids = Vec::with_capacity(carousel_children.len());
|
||||
let carousel_widget_id =
|
||||
arena.new_node(LayoutNode::Widget(WidgetLayout::default()));
|
||||
row_id.append(carousel_widget_id, &mut arena);
|
||||
let mut car_children = Vec::with_capacity(carousel_children.len());
|
||||
for widget_type in carousel_children {
|
||||
let widget_type = widget_type.parse::<BottomWidgetType>()?;
|
||||
used_widgets.add(&widget_type);
|
||||
|
||||
if let Some(true) = default {
|
||||
first_selected = Some(carousel_widget_id);
|
||||
}
|
||||
|
||||
if first_widget_seen.is_none() {
|
||||
first_widget_seen = Some(carousel_widget_id);
|
||||
}
|
||||
|
||||
// Handle the rest of the children.
|
||||
for child in carousel_children {
|
||||
let widget_id =
|
||||
arena.new_node(LayoutNode::Widget(WidgetLayout::default()));
|
||||
carousel_widget_id.append(widget_id, &mut arena);
|
||||
|
||||
let widget_type = child.parse::<BottomWidgetType>()?;
|
||||
used_widgets.add(&widget_type);
|
||||
|
||||
add_widget_to_map(
|
||||
&mut widget_lookup_map,
|
||||
widget_type,
|
||||
widget_id,
|
||||
&process_defaults,
|
||||
app_config_fields,
|
||||
LayoutRule::default(),
|
||||
LayoutRule::default(),
|
||||
)?;
|
||||
|
||||
child_ids.push(widget_id);
|
||||
}
|
||||
|
||||
widget_lookup_map.insert(
|
||||
carousel_widget_id,
|
||||
Carousel::new(
|
||||
child_ids
|
||||
.into_iter()
|
||||
.filter_map(|child_id| {
|
||||
widget_lookup_map
|
||||
.get(&child_id)
|
||||
.map(|w| (child_id, w.get_pretty_name().into()))
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
car_children.push(widget_type);
|
||||
}
|
||||
|
||||
row_children.push(WidgetLayoutNode::Carousel {
|
||||
children: car_children,
|
||||
selected: default.unwrap_or(false),
|
||||
});
|
||||
}
|
||||
LayoutRowChild::LayoutCol {
|
||||
ratio,
|
||||
child: col_child,
|
||||
child: children,
|
||||
} => {
|
||||
let col_id = arena.new_node(LayoutNode::Col(ColLayout::new(
|
||||
ratio
|
||||
.map(|ratio| LayoutRule::Expand { ratio })
|
||||
.unwrap_or(LayoutRule::Child),
|
||||
)));
|
||||
row_id.append(col_id, &mut arena);
|
||||
|
||||
for widget in col_child {
|
||||
let widget_id =
|
||||
arena.new_node(LayoutNode::Widget(WidgetLayout::default()));
|
||||
col_id.append(widget_id, &mut arena);
|
||||
|
||||
if let Some(true) = widget.default {
|
||||
first_selected = Some(widget_id);
|
||||
}
|
||||
|
||||
if first_widget_seen.is_none() {
|
||||
first_widget_seen = Some(widget_id);
|
||||
}
|
||||
|
||||
let widget_type = widget.widget_type.parse::<BottomWidgetType>()?;
|
||||
let mut col_children = Vec::with_capacity(children.len());
|
||||
for widget in children {
|
||||
let FinalWidget {
|
||||
rule,
|
||||
widget_type,
|
||||
default,
|
||||
} = widget;
|
||||
let widget_type = widget_type.parse::<BottomWidgetType>()?;
|
||||
used_widgets.add(&widget_type);
|
||||
|
||||
add_widget_to_map(
|
||||
&mut widget_lookup_map,
|
||||
col_children.push(WidgetLayoutNode::Widget {
|
||||
widget_type,
|
||||
widget_id,
|
||||
&process_defaults,
|
||||
app_config_fields,
|
||||
LayoutRule::default(),
|
||||
widget.rule.unwrap_or_default(),
|
||||
)?;
|
||||
selected: default.unwrap_or(false),
|
||||
width_rule: LayoutRule::default(),
|
||||
height_rule: rule.unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
|
||||
row_children.push(WidgetLayoutNode::Col {
|
||||
children: col_children,
|
||||
parent_rule: match ratio {
|
||||
Some(ratio) => LayoutRule::Expand { ratio: *ratio },
|
||||
None => LayoutRule::Child,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let row = WidgetLayoutNode::Row {
|
||||
children: row_children,
|
||||
parent_rule: match layout_row.ratio {
|
||||
Some(ratio) => LayoutRule::Expand { ratio },
|
||||
None => LayoutRule::Child,
|
||||
},
|
||||
};
|
||||
root_children.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
let selected: NodeId;
|
||||
if let Some(first_selected) = first_selected {
|
||||
selected = first_selected;
|
||||
} else if let Some(first_widget_seen) = first_widget_seen {
|
||||
selected = first_widget_seen;
|
||||
} else {
|
||||
return Err(BottomError::ConfigError(
|
||||
"A layout cannot contain zero widgets!".to_string(),
|
||||
));
|
||||
}
|
||||
let root = WidgetLayoutNode::Col {
|
||||
children: root_children,
|
||||
parent_rule: LayoutRule::Expand { ratio: 1 },
|
||||
};
|
||||
|
||||
correct_layout_last_selections(&mut arena, selected);
|
||||
|
||||
Ok(LayoutCreationOutput {
|
||||
layout_tree: arena,
|
||||
root: root_id,
|
||||
widget_lookup_map,
|
||||
selected,
|
||||
used_widgets,
|
||||
})
|
||||
Ok((root, used_widgets))
|
||||
}
|
||||
|
||||
/// We may have situations where we also have to make sure the correct layout indices are selected.
|
||||
|
@ -119,7 +119,6 @@ pub struct SimpleSortableColumn {
|
||||
original_name: Cow<'static, str>,
|
||||
pub shortcut: Option<(KeyEvent, String)>,
|
||||
pub default_descending: bool,
|
||||
x_bounds: Option<(u16, u16)>,
|
||||
|
||||
pub internal: SimpleColumn,
|
||||
|
||||
@ -138,7 +137,6 @@ impl SimpleSortableColumn {
|
||||
original_name,
|
||||
shortcut,
|
||||
default_descending,
|
||||
x_bounds: None,
|
||||
internal: SimpleColumn::new(full_name, desired_width),
|
||||
sorting_status: SortStatus::NotSorting,
|
||||
}
|
||||
|
@ -225,7 +225,7 @@ pub const HELP_CONTENTS_TEXT: [&str; 8] = [
|
||||
];
|
||||
|
||||
pub const GENERAL_HELP_TITLE: &str = "General";
|
||||
pub const GENERAL_HELP_TEXT: [[&str; 2]; 21] = [
|
||||
pub const GENERAL_HELP_TEXT: [[&str; 2]; 23] = [
|
||||
["q, Ctrl-c", "Quit"],
|
||||
[
|
||||
"Esc",
|
||||
@ -251,6 +251,8 @@ pub const GENERAL_HELP_TEXT: [[&str; 2]; 21] = [
|
||||
["+", "Zoom in on chart (decrease time range)"],
|
||||
["-", "Zoom out on chart (increase time range)"],
|
||||
["=", "Reset zoom"],
|
||||
["Page Up", "Move up one page in a table"],
|
||||
["Page Down", "Move down one page in a table"],
|
||||
[
|
||||
"Mouse scroll",
|
||||
"Scroll through the tables or zoom in/out of charts by scrolling up/down",
|
||||
|
@ -241,7 +241,7 @@ pub fn build_app(matches: &clap::ArgMatches<'static>, config: &mut Config) -> Re
|
||||
&rows
|
||||
};
|
||||
|
||||
let layout_tree_output = create_layout_tree(row_ref, process_defaults, &app_config_fields)?;
|
||||
let (layout, used_widgets) = parse_widget_layout(row_ref)?;
|
||||
|
||||
let disk_filter =
|
||||
get_ignore_list(&config.disk_filter).context("Update 'disk_filter' in your config file")?;
|
||||
@ -259,7 +259,13 @@ pub fn build_app(matches: &clap::ArgMatches<'static>, config: &mut Config) -> Re
|
||||
};
|
||||
|
||||
let painter = Painter::init(&config, get_color_scheme(&matches, &config)?)?;
|
||||
AppState::new(app_config_fields, data_filter, layout_tree_output, painter)
|
||||
AppState::new(
|
||||
app_config_fields,
|
||||
data_filter,
|
||||
layout,
|
||||
used_widgets,
|
||||
painter,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_update_rate_in_milliseconds(
|
||||
|
@ -1,6 +1,3 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use enum_dispatch::enum_dispatch;
|
||||
|
||||
pub mod simple_table;
|
||||
pub use simple_table::*;
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use tui::style::Style;
|
||||
|
||||
use crate::tuine::{
|
||||
self, block,
|
||||
text_table::{self, DataRow, SortType, TextTableProps},
|
||||
Block, Event, Shortcut, StatefulComponent, Status, TextTable, TmpComponent, ViewContext,
|
||||
Block, Shortcut, StatefulComponent, TextTable, TmpComponent, ViewContext,
|
||||
};
|
||||
|
||||
/// A set of styles for a [`SimpleTable`].
|
||||
|
@ -3,8 +3,8 @@ use crate::{
|
||||
canvas::Painter,
|
||||
data_conversion::ConvertedData,
|
||||
tuine::{
|
||||
Bounds, DataRow, DrawContext, LayoutNode, SimpleTable, Size, StateContext, Status,
|
||||
TmpComponent, ViewContext,
|
||||
Bounds, DrawContext, LayoutNode, SimpleTable, Size, StateContext, Status, TmpComponent,
|
||||
ViewContext,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1,121 +0,0 @@
|
||||
use tui::{
|
||||
layout::Rect,
|
||||
text::Span,
|
||||
widgets::{Block, Borders},
|
||||
};
|
||||
|
||||
use crate::canvas::Painter;
|
||||
|
||||
/// A factory pattern builder for a tui [`Block`].
|
||||
pub struct BlockBuilder {
|
||||
borders: Borders,
|
||||
selected: bool,
|
||||
show_esc: bool,
|
||||
name: &'static str,
|
||||
hide_title: bool,
|
||||
extra_text: Option<String>,
|
||||
}
|
||||
|
||||
impl BlockBuilder {
|
||||
/// Creates a new [`BlockBuilder`] with the name of block.
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
borders: Borders::ALL,
|
||||
selected: false,
|
||||
show_esc: false,
|
||||
name,
|
||||
hide_title: false,
|
||||
extra_text: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates that this block is currently selected, and should be drawn as such.
|
||||
pub fn selected(mut self, selected: bool) -> Self {
|
||||
self.selected = selected;
|
||||
self
|
||||
}
|
||||
|
||||
/// Indicates that this block should show esc, and should be drawn as such.
|
||||
pub fn show_esc(mut self, show_esc: bool) -> Self {
|
||||
self.show_esc = show_esc;
|
||||
self
|
||||
}
|
||||
|
||||
/// Indicates that this block has some extra text beyond the name.
|
||||
pub fn extra_text(mut self, extra_text: Option<String>) -> Self {
|
||||
self.extra_text = extra_text;
|
||||
self
|
||||
}
|
||||
|
||||
/// Determines the borders of the built [`Block`].
|
||||
pub fn borders(mut self, borders: Borders) -> Self {
|
||||
self.borders = borders;
|
||||
self
|
||||
}
|
||||
|
||||
/// Forcibly hides the title of the built [`Block`].
|
||||
pub fn hide_title(mut self, hide_title: bool) -> Self {
|
||||
self.hide_title = hide_title;
|
||||
self
|
||||
}
|
||||
|
||||
/// Converts the [`BlockBuilder`] into an actual [`Block`].
|
||||
pub fn build(self, painter: &Painter, area: Rect) -> Block<'static> {
|
||||
let has_title = !self.hide_title
|
||||
&& (self.borders.contains(Borders::TOP) || self.borders.contains(Borders::BOTTOM));
|
||||
|
||||
let border_style = if self.selected {
|
||||
painter.colours.highlighted_border_style
|
||||
} else {
|
||||
painter.colours.border_style
|
||||
};
|
||||
|
||||
let block = Block::default()
|
||||
.border_style(border_style)
|
||||
.borders(self.borders);
|
||||
|
||||
let inner_width = block.inner(area).width as usize;
|
||||
|
||||
if has_title {
|
||||
let name = Span::styled(
|
||||
format!(" {} ", self.name),
|
||||
painter.colours.widget_title_style,
|
||||
);
|
||||
let mut title_len = name.width();
|
||||
let mut title = vec![name, Span::from(""), Span::from(""), Span::from("")];
|
||||
|
||||
if self.show_esc {
|
||||
const EXPAND_TEXT: &str = " Esc to go back ";
|
||||
const EXPAND_TEXT_LEN: usize = EXPAND_TEXT.len();
|
||||
|
||||
let expand_span = Span::styled(EXPAND_TEXT, border_style);
|
||||
|
||||
if title_len + EXPAND_TEXT_LEN <= inner_width {
|
||||
title_len += EXPAND_TEXT_LEN;
|
||||
title[3] = expand_span;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(extra_text) = self.extra_text {
|
||||
let extra_span = Span::styled(
|
||||
format!("{} ", extra_text),
|
||||
painter.colours.widget_title_style,
|
||||
);
|
||||
let width = extra_span.width();
|
||||
if title_len + width <= inner_width {
|
||||
title_len += width;
|
||||
title[1] = extra_span;
|
||||
}
|
||||
}
|
||||
|
||||
if self.show_esc {
|
||||
let difference = inner_width.saturating_sub(title_len);
|
||||
title[2] = Span::styled("─".repeat(difference), border_style);
|
||||
}
|
||||
|
||||
block.title(title)
|
||||
} else {
|
||||
block
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,3 @@ pub use custom_legend_chart::TimeChart;
|
||||
|
||||
pub mod pipe_gauge;
|
||||
pub use pipe_gauge::PipeGauge;
|
||||
|
||||
pub mod block_builder;
|
||||
pub use block_builder::BlockBuilder;
|
||||
|
Loading…
x
Reference in New Issue
Block a user