mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-27 07:34:27 +02:00
Layout WIP
This commit is contained in:
parent
95318bb3e4
commit
3dbe788920
69
src/app.rs
69
src/app.rs
@ -30,14 +30,12 @@ use crate::{
|
|||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
constants,
|
constants,
|
||||||
data_conversion::ConvertedData,
|
data_conversion::ConvertedData,
|
||||||
tuine::{Application, Element, Flex, Status, ViewContext},
|
tuine::{Application, Element, Status, ViewContext},
|
||||||
units::data_units::DataUnit,
|
units::data_units::DataUnit,
|
||||||
Pid,
|
Pid,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use indextree::{Arena, NodeId};
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
|
|
||||||
// FIXME: Move this!
|
// FIXME: Move this!
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -86,10 +84,10 @@ impl UsedWidgets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// AppConfigFields is meant to cover basic fields that would normally be set
|
/// [`AppConfig`] is meant to cover basic fields that would normally be set
|
||||||
/// by config files or launch options.
|
/// by config files or launch options.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AppConfigFields {
|
pub struct AppConfig {
|
||||||
pub update_rate_in_milliseconds: u64,
|
pub update_rate_in_milliseconds: u64,
|
||||||
pub temperature_type: temperature::TemperatureType,
|
pub temperature_type: temperature::TemperatureType,
|
||||||
pub use_dot: bool,
|
pub use_dot: bool,
|
||||||
@ -145,34 +143,32 @@ pub enum AppMessages {
|
|||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub data_collection: DataCollection,
|
pub data_collection: DataCollection,
|
||||||
|
|
||||||
pub used_widgets: UsedWidgets,
|
pub used_widgets: UsedWidgets,
|
||||||
pub filters: DataFilters,
|
pub filters: DataFilters,
|
||||||
pub app_config_fields: AppConfigFields,
|
pub app_config: AppConfig,
|
||||||
|
|
||||||
// --- NEW STUFF ---
|
|
||||||
frozen_state: FrozenState,
|
frozen_state: FrozenState,
|
||||||
current_screen: CurrentScreen,
|
current_screen: CurrentScreen,
|
||||||
painter: Painter,
|
pub painter: Painter,
|
||||||
terminator: Arc<AtomicBool>,
|
terminator: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
/// Creates a new [`AppState`].
|
/// Creates a new [`AppState`].
|
||||||
pub fn new(
|
pub fn new(
|
||||||
app_config_fields: AppConfigFields, filters: DataFilters,
|
app_config: AppConfig, filters: DataFilters, layout_tree_output: LayoutCreationOutput,
|
||||||
layout_tree_output: LayoutCreationOutput, painter: Painter,
|
painter: Painter,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let LayoutCreationOutput {
|
let LayoutCreationOutput {
|
||||||
layout_tree,
|
layout_tree: _,
|
||||||
root: layout_tree_root,
|
root: _,
|
||||||
widget_lookup_map,
|
widget_lookup_map,
|
||||||
selected: selected_widget,
|
selected: _,
|
||||||
used_widgets,
|
used_widgets,
|
||||||
} = layout_tree_output;
|
} = layout_tree_output;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
app_config_fields,
|
app_config,
|
||||||
filters,
|
filters,
|
||||||
used_widgets,
|
used_widgets,
|
||||||
painter,
|
painter,
|
||||||
@ -250,48 +246,7 @@ impl Application for AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn view<'b>(&mut self, ctx: &mut ViewContext<'_>) -> Element<Self::Message> {
|
fn view<'b>(&mut self, ctx: &mut ViewContext<'_>) -> Element<Self::Message> {
|
||||||
use crate::tuine::FlexElement;
|
todo!()
|
||||||
use crate::tuine::StatefulComponent;
|
|
||||||
use crate::tuine::{TempTable, TextTable, TextTableProps};
|
|
||||||
|
|
||||||
let data = match &self.frozen_state {
|
|
||||||
FrozenState::NotFrozen => &self.data_collection,
|
|
||||||
FrozenState::Frozen(frozen_data_collection) => &frozen_data_collection,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut converted_data = ConvertedData::default();
|
|
||||||
|
|
||||||
Flex::column()
|
|
||||||
.with_flex_child(
|
|
||||||
Flex::row_with_children(vec![
|
|
||||||
FlexElement::new(TempTable::build(
|
|
||||||
ctx,
|
|
||||||
&self.painter,
|
|
||||||
converted_data.temp_table(data, self.app_config_fields.temperature_type),
|
|
||||||
)),
|
|
||||||
FlexElement::new(TextTable::build(
|
|
||||||
ctx,
|
|
||||||
TextTableProps::new(vec!["D", "E", "F"]),
|
|
||||||
)),
|
|
||||||
]),
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
.with_flex_child(
|
|
||||||
Flex::row_with_children(vec![
|
|
||||||
FlexElement::new(TextTable::build(
|
|
||||||
ctx,
|
|
||||||
TextTableProps::new(vec!["G", "H", "I", "J"]),
|
|
||||||
)),
|
|
||||||
FlexElement::new(TextTable::build(
|
|
||||||
ctx,
|
|
||||||
TextTableProps::new(vec!["L", "EM", "NO", "PQ"])
|
|
||||||
.rows(vec![vec![1, 2, 3, 4], vec![4, 3, 2, 1]])
|
|
||||||
.default_sort(crate::tuine::SortType::Descending(0)),
|
|
||||||
)),
|
|
||||||
]),
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn destructor(&mut self) {
|
fn destructor(&mut self) {
|
||||||
|
@ -5,19 +5,20 @@ use crate::{
|
|||||||
},
|
},
|
||||||
error::{BottomError, Result},
|
error::{BottomError, Result},
|
||||||
options::{
|
options::{
|
||||||
layout_options::{LayoutRule, Row, RowChildren},
|
layout_options::{LayoutRow, LayoutRowChild, LayoutRule},
|
||||||
ProcessDefaults,
|
ProcessDefaults,
|
||||||
},
|
},
|
||||||
|
tuine::{Element, Flex},
|
||||||
};
|
};
|
||||||
use indextree::{Arena, NodeId};
|
use indextree::{Arena, NodeId};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use std::cmp::min;
|
use std::str::FromStr;
|
||||||
use tui::layout::Rect;
|
use tui::layout::Rect;
|
||||||
|
|
||||||
use crate::app::widgets::Widget;
|
use crate::app::widgets::Widget;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
event::SelectionAction, AppConfigFields, BottomWidget, CpuGraph, TimeGraph, UsedWidgets,
|
event::SelectionAction, AppConfig, AppState, CpuGraph, OldBottomWidget, TimeGraph, UsedWidgets,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
@ -46,7 +47,7 @@ impl Default for BottomWidgetType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for BottomWidgetType {
|
impl FromStr for BottomWidgetType {
|
||||||
type Err = BottomError;
|
type Err = BottomError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
@ -169,22 +170,6 @@ pub enum LayoutNode {
|
|||||||
Widget(WidgetLayout),
|
Widget(WidgetLayout),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutNode {
|
|
||||||
fn set_bound(&mut self, bound: Rect) {
|
|
||||||
match self {
|
|
||||||
LayoutNode::Row(row) => {
|
|
||||||
row.bound = bound;
|
|
||||||
}
|
|
||||||
LayoutNode::Col(col) => {
|
|
||||||
col.bound = bound;
|
|
||||||
}
|
|
||||||
LayoutNode::Widget(widget) => {
|
|
||||||
widget.bound = bound;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Relative movement direction from the currently selected widget.
|
/// Relative movement direction from the currently selected widget.
|
||||||
pub enum MovementDirection {
|
pub enum MovementDirection {
|
||||||
Left,
|
Left,
|
||||||
@ -193,11 +178,40 @@ pub enum MovementDirection {
|
|||||||
Down,
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
/// A wrapper struct to simplify the output of [`create_layout_tree`].
|
/// A wrapper struct to simplify the output of [`create_layout_tree`].
|
||||||
pub struct LayoutCreationOutput {
|
pub struct LayoutCreationOutput {
|
||||||
pub layout_tree: Arena<LayoutNode>,
|
pub layout_tree: Arena<LayoutNode>,
|
||||||
pub root: NodeId,
|
pub root: NodeId,
|
||||||
pub widget_lookup_map: FxHashMap<NodeId, BottomWidget>,
|
pub widget_lookup_map: FxHashMap<NodeId, OldBottomWidget>,
|
||||||
pub selected: NodeId,
|
pub selected: NodeId,
|
||||||
pub used_widgets: UsedWidgets,
|
pub used_widgets: UsedWidgets,
|
||||||
}
|
}
|
||||||
@ -207,11 +221,11 @@ pub struct LayoutCreationOutput {
|
|||||||
/// selected [`NodeId`].
|
/// 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!
|
// 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(
|
pub fn create_layout_tree(
|
||||||
rows: &[Row], process_defaults: ProcessDefaults, app_config_fields: &AppConfigFields,
|
rows: &[LayoutRow], process_defaults: ProcessDefaults, app_config_fields: &AppConfig,
|
||||||
) -> Result<LayoutCreationOutput> {
|
) -> Result<LayoutCreationOutput> {
|
||||||
fn add_widget_to_map(
|
fn add_widget_to_map(
|
||||||
widget_lookup_map: &mut FxHashMap<NodeId, BottomWidget>, widget_type: BottomWidgetType,
|
widget_lookup_map: &mut FxHashMap<NodeId, OldBottomWidget>, widget_type: BottomWidgetType,
|
||||||
widget_id: NodeId, process_defaults: &ProcessDefaults, app_config_fields: &AppConfigFields,
|
widget_id: NodeId, process_defaults: &ProcessDefaults, app_config_fields: &AppConfig,
|
||||||
width: LayoutRule, height: LayoutRule,
|
width: LayoutRule, height: LayoutRule,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match widget_type {
|
match widget_type {
|
||||||
@ -341,7 +355,7 @@ pub fn create_layout_tree(
|
|||||||
if let Some(children) = &row.child {
|
if let Some(children) = &row.child {
|
||||||
for child in children {
|
for child in children {
|
||||||
match child {
|
match child {
|
||||||
RowChildren::Widget(widget) => {
|
LayoutRowChild::Widget(widget) => {
|
||||||
let widget_id = arena.new_node(LayoutNode::Widget(WidgetLayout::default()));
|
let widget_id = arena.new_node(LayoutNode::Widget(WidgetLayout::default()));
|
||||||
row_id.append(widget_id, &mut arena);
|
row_id.append(widget_id, &mut arena);
|
||||||
|
|
||||||
@ -366,7 +380,7 @@ pub fn create_layout_tree(
|
|||||||
LayoutRule::default(),
|
LayoutRule::default(),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
RowChildren::Carousel {
|
LayoutRowChild::Carousel {
|
||||||
carousel_children,
|
carousel_children,
|
||||||
default,
|
default,
|
||||||
} => {
|
} => {
|
||||||
@ -422,7 +436,7 @@ pub fn create_layout_tree(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RowChildren::Col {
|
LayoutRowChild::LayoutCol {
|
||||||
ratio,
|
ratio,
|
||||||
child: col_child,
|
child: col_child,
|
||||||
} => {
|
} => {
|
||||||
@ -518,7 +532,7 @@ pub enum MoveWidgetResult {
|
|||||||
|
|
||||||
/// A more restricted movement, only within a single widget.
|
/// A more restricted movement, only within a single widget.
|
||||||
pub fn move_expanded_widget_selection(
|
pub fn move_expanded_widget_selection(
|
||||||
widget_lookup_map: &mut FxHashMap<NodeId, BottomWidget>, current_widget_id: NodeId,
|
widget_lookup_map: &mut FxHashMap<NodeId, OldBottomWidget>, current_widget_id: NodeId,
|
||||||
direction: MovementDirection,
|
direction: MovementDirection,
|
||||||
) -> MoveWidgetResult {
|
) -> MoveWidgetResult {
|
||||||
if let Some(current_widget) = widget_lookup_map.get_mut(¤t_widget_id) {
|
if let Some(current_widget) = widget_lookup_map.get_mut(¤t_widget_id) {
|
||||||
@ -542,8 +556,9 @@ pub fn move_expanded_widget_selection(
|
|||||||
/// - Only [`LayoutNode::Widget`]s are leaves.
|
/// - Only [`LayoutNode::Widget`]s are leaves.
|
||||||
/// - Only [`LayoutNode::Row`]s or [`LayoutNode::Col`]s are non-leaves.
|
/// - Only [`LayoutNode::Row`]s or [`LayoutNode::Col`]s are non-leaves.
|
||||||
pub fn move_widget_selection(
|
pub fn move_widget_selection(
|
||||||
layout_tree: &mut Arena<LayoutNode>, widget_lookup_map: &mut FxHashMap<NodeId, BottomWidget>,
|
layout_tree: &mut Arena<LayoutNode>,
|
||||||
current_widget_id: NodeId, direction: MovementDirection,
|
widget_lookup_map: &mut FxHashMap<NodeId, OldBottomWidget>, current_widget_id: NodeId,
|
||||||
|
direction: MovementDirection,
|
||||||
) -> MoveWidgetResult {
|
) -> MoveWidgetResult {
|
||||||
// We first give our currently-selected widget a chance to react to the movement - it may handle it internally!
|
// We first give our currently-selected widget a chance to react to the movement - it may handle it internally!
|
||||||
let handled = {
|
let handled = {
|
||||||
@ -816,317 +831,3 @@ pub fn move_widget_selection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the bounds for each node in the `arena, taking into account per-leaf desires,
|
|
||||||
/// and finally storing the calculated bounds in the given `arena`.
|
|
||||||
///
|
|
||||||
/// Stored bounds are given in *relative* coordinates - they are relative to their parents.
|
|
||||||
/// That is, you may have a child widget "start" at (0, 0), but its parent is actually at x = 5,s
|
|
||||||
/// so the absolute coordinate of the child widget is actually (5, 0).
|
|
||||||
///
|
|
||||||
/// The algorithm is mostly based on the algorithm used by Flutter, adapted to work for
|
|
||||||
/// our use case. For more information, check out both:
|
|
||||||
///
|
|
||||||
/// - [How the constraint system works in Flutter](https://flutter.dev/docs/development/ui/layout/constraints)
|
|
||||||
/// - [How Flutter does sublinear layout](https://flutter.dev/docs/resources/inside-flutter#sublinear-layout)
|
|
||||||
pub fn generate_layout(
|
|
||||||
root: NodeId, arena: &mut Arena<LayoutNode>, area: Rect,
|
|
||||||
lookup_map: &FxHashMap<NodeId, BottomWidget>,
|
|
||||||
) {
|
|
||||||
// TODO: [Optimization, Layout] Add some caching/dirty mechanisms to reduce calls.
|
|
||||||
|
|
||||||
/// A [`Size`] is a set of widths and heights that a node in our layout wants to be.
|
|
||||||
#[derive(Default, Clone, Copy, Debug)]
|
|
||||||
struct Size {
|
|
||||||
width: u16,
|
|
||||||
height: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [`LayoutConstraint`] is just a set of maximal widths/heights.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
struct LayoutConstraints {
|
|
||||||
max_width: u16,
|
|
||||||
max_height: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LayoutConstraints {
|
|
||||||
fn new(max_width: u16, max_height: u16) -> Self {
|
|
||||||
Self {
|
|
||||||
max_width,
|
|
||||||
max_height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shrinks the width of itself given another width.
|
|
||||||
fn shrink_width(&mut self, width: u16) {
|
|
||||||
self.max_width = self.max_width.saturating_sub(width);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shrinks the height of itself given another height.
|
|
||||||
fn shrink_height(&mut self, height: u16) {
|
|
||||||
self.max_height = self.max_height.saturating_sub(height);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [`LayoutConstraints`] with a new width given a ratio.
|
|
||||||
fn ratio_width(&self, numerator: u32, denominator: u32) -> Self {
|
|
||||||
Self {
|
|
||||||
max_width: (self.max_width as u32 * numerator / denominator) as u16,
|
|
||||||
max_height: self.max_height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [`LayoutConstraints`] with a new height given a ratio.
|
|
||||||
fn ratio_height(&self, numerator: u32, denominator: u32) -> Self {
|
|
||||||
Self {
|
|
||||||
max_width: self.max_width,
|
|
||||||
max_height: (self.max_height as u32 * numerator / denominator) as u16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The internal recursive call to build a layout. Builds off of `arena` and stores bounds inside it.
|
|
||||||
fn layout(
|
|
||||||
node: NodeId, arena: &mut Arena<LayoutNode>, lookup_map: &FxHashMap<NodeId, BottomWidget>,
|
|
||||||
mut constraints: LayoutConstraints,
|
|
||||||
) -> Size {
|
|
||||||
if let Some(layout_node) = arena.get(node).map(|n| n.get()) {
|
|
||||||
match layout_node {
|
|
||||||
LayoutNode::Row(row) => {
|
|
||||||
let children = node.children(arena).collect::<Vec<_>>();
|
|
||||||
let mut row_bounds = vec![Size::default(); children.len()];
|
|
||||||
|
|
||||||
if let LayoutRule::Length { length } = row.parent_rule {
|
|
||||||
constraints.max_height = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (flexible_indices, inflexible_indices): (Vec<_>, Vec<_>) = children
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(itx, node)| {
|
|
||||||
if let Some(layout_node) = arena.get(*node).map(|n| n.get()) {
|
|
||||||
match layout_node {
|
|
||||||
LayoutNode::Row(RowLayout { parent_rule, .. })
|
|
||||||
| LayoutNode::Col(ColLayout { parent_rule, .. }) => {
|
|
||||||
match parent_rule {
|
|
||||||
LayoutRule::Expand { ratio } => {
|
|
||||||
Some((itx, true, *ratio))
|
|
||||||
}
|
|
||||||
LayoutRule::Child => Some((itx, false, 0)),
|
|
||||||
LayoutRule::Length { .. } => Some((itx, false, 0)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LayoutNode::Widget(_) => {
|
|
||||||
if let Some(widget) = lookup_map.get(node) {
|
|
||||||
match widget.width() {
|
|
||||||
LayoutRule::Expand { ratio } => {
|
|
||||||
Some((itx, true, ratio))
|
|
||||||
}
|
|
||||||
LayoutRule::Child => Some((itx, false, 0)),
|
|
||||||
LayoutRule::Length { .. } => Some((itx, false, 0)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.partition(|(_itx, is_flex, _ratio)| *is_flex);
|
|
||||||
|
|
||||||
// First handle non-flexible children.
|
|
||||||
for (index, _, _) in inflexible_indices {
|
|
||||||
// The unchecked get is safe, since the index is obtained by iterating through the children
|
|
||||||
// vector in the first place.
|
|
||||||
let child = unsafe { children.get_unchecked(index) };
|
|
||||||
let desired_size = layout(*child, arena, lookup_map, constraints);
|
|
||||||
|
|
||||||
constraints.shrink_width(desired_size.width);
|
|
||||||
|
|
||||||
// This won't panic, since the two vectors are the same length.
|
|
||||||
row_bounds[index] = desired_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle flexible children now.
|
|
||||||
let denominator: u32 = flexible_indices.iter().map(|(_, _, ratio)| ratio).sum();
|
|
||||||
let original_constraints = constraints;
|
|
||||||
let mut split_constraints = flexible_indices
|
|
||||||
.iter()
|
|
||||||
.map(|(_, _, numerator)| {
|
|
||||||
let constraint =
|
|
||||||
original_constraints.ratio_width(*numerator, denominator);
|
|
||||||
constraints.shrink_width(constraint.max_width);
|
|
||||||
|
|
||||||
constraint
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
(0..constraints.max_width)
|
|
||||||
.zip(&mut split_constraints)
|
|
||||||
.for_each(|(_, split_constraint)| {
|
|
||||||
split_constraint.max_width += 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
for ((index, _, _), constraint) in
|
|
||||||
flexible_indices.into_iter().zip(split_constraints)
|
|
||||||
{
|
|
||||||
// The unchecked get is safe, since the index is obtained by iterating through the children
|
|
||||||
// vector in the first place.
|
|
||||||
let child = unsafe { children.get_unchecked(index) };
|
|
||||||
let desired_size = layout(*child, arena, lookup_map, constraint);
|
|
||||||
|
|
||||||
// This won't panic, since the two vectors are the same length.
|
|
||||||
row_bounds[index] = desired_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now let's turn each Size into a relative Rect!
|
|
||||||
let mut current_x = 0;
|
|
||||||
row_bounds.iter().zip(children).for_each(|(size, child)| {
|
|
||||||
let bound = Rect::new(current_x, 0, size.width, size.height);
|
|
||||||
current_x += size.width;
|
|
||||||
if let Some(node) = arena.get_mut(child) {
|
|
||||||
node.get_mut().set_bound(bound);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Size {
|
|
||||||
height: row_bounds.iter().map(|size| size.height).max().unwrap_or(0),
|
|
||||||
width: row_bounds.into_iter().map(|size| size.width).sum(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LayoutNode::Col(col) => {
|
|
||||||
let children = node.children(arena).collect::<Vec<_>>();
|
|
||||||
let mut col_bounds = vec![Size::default(); children.len()];
|
|
||||||
|
|
||||||
if let LayoutRule::Length { length } = col.parent_rule {
|
|
||||||
constraints.max_width = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (flexible_indices, inflexible_indices): (Vec<_>, Vec<_>) = children
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(itx, node)| {
|
|
||||||
if let Some(layout_node) = arena.get(*node).map(|n| n.get()) {
|
|
||||||
match layout_node {
|
|
||||||
LayoutNode::Row(RowLayout { parent_rule, .. })
|
|
||||||
| LayoutNode::Col(ColLayout { parent_rule, .. }) => {
|
|
||||||
match parent_rule {
|
|
||||||
LayoutRule::Expand { ratio } => {
|
|
||||||
Some((itx, true, *ratio))
|
|
||||||
}
|
|
||||||
LayoutRule::Child => Some((itx, false, 0)),
|
|
||||||
LayoutRule::Length { .. } => Some((itx, false, 0)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LayoutNode::Widget(_) => {
|
|
||||||
if let Some(widget) = lookup_map.get(node) {
|
|
||||||
match widget.height() {
|
|
||||||
LayoutRule::Expand { ratio } => {
|
|
||||||
Some((itx, true, ratio))
|
|
||||||
}
|
|
||||||
LayoutRule::Child => Some((itx, false, 0)),
|
|
||||||
LayoutRule::Length { length: _ } => {
|
|
||||||
Some((itx, false, 0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.partition(|(_itx, is_flex, _ratio)| *is_flex);
|
|
||||||
|
|
||||||
for (index, _, _) in inflexible_indices {
|
|
||||||
// The unchecked get is safe, since the index is obtained by iterating through the children
|
|
||||||
// vector in the first place.
|
|
||||||
let child = unsafe { children.get_unchecked(index) };
|
|
||||||
let desired_size = layout(*child, arena, lookup_map, constraints);
|
|
||||||
|
|
||||||
constraints.shrink_height(desired_size.height);
|
|
||||||
|
|
||||||
// This won't panic, since the two vectors are the same length.
|
|
||||||
col_bounds[index] = desired_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
let denominator: u32 = flexible_indices.iter().map(|(_, _, ratio)| ratio).sum();
|
|
||||||
let original_constraints = constraints;
|
|
||||||
let mut split_constraints = flexible_indices
|
|
||||||
.iter()
|
|
||||||
.map(|(_, _, numerator)| {
|
|
||||||
let new_constraint =
|
|
||||||
original_constraints.ratio_height(*numerator, denominator);
|
|
||||||
constraints.shrink_height(new_constraint.max_height);
|
|
||||||
|
|
||||||
new_constraint
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
(0..constraints.max_height)
|
|
||||||
.zip(&mut split_constraints)
|
|
||||||
.for_each(|(_, split_constraint)| {
|
|
||||||
split_constraint.max_height += 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
for ((index, _, _), constraint) in
|
|
||||||
flexible_indices.into_iter().zip(split_constraints)
|
|
||||||
{
|
|
||||||
// The unchecked get is safe, since the index is obtained by iterating through the children
|
|
||||||
// vector in the first place.
|
|
||||||
let child = unsafe { children.get_unchecked(index) };
|
|
||||||
let desired_size = layout(*child, arena, lookup_map, constraint);
|
|
||||||
|
|
||||||
// This won't panic, since the two vectors are the same length.
|
|
||||||
col_bounds[index] = desired_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now let's turn each Size into a relative Rect!
|
|
||||||
let mut current_y = 0;
|
|
||||||
col_bounds.iter().zip(children).for_each(|(size, child)| {
|
|
||||||
let bound = Rect::new(0, current_y, size.width, size.height);
|
|
||||||
current_y += size.height;
|
|
||||||
if let Some(node) = arena.get_mut(child) {
|
|
||||||
node.get_mut().set_bound(bound);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Size {
|
|
||||||
width: col_bounds.iter().map(|size| size.width).max().unwrap_or(0),
|
|
||||||
height: col_bounds.into_iter().map(|size| size.height).sum(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LayoutNode::Widget(_) => {
|
|
||||||
if let Some(widget) = lookup_map.get(&node) {
|
|
||||||
let width = match widget.width() {
|
|
||||||
LayoutRule::Expand { ratio: _ } => constraints.max_width,
|
|
||||||
LayoutRule::Length { length } => min(length, constraints.max_width),
|
|
||||||
LayoutRule::Child => constraints.max_width,
|
|
||||||
};
|
|
||||||
|
|
||||||
let height = match widget.height() {
|
|
||||||
LayoutRule::Expand { ratio: _ } => constraints.max_height,
|
|
||||||
LayoutRule::Length { length } => min(length, constraints.max_height),
|
|
||||||
LayoutRule::Child => constraints.max_height,
|
|
||||||
};
|
|
||||||
|
|
||||||
Size { width, height }
|
|
||||||
} else {
|
|
||||||
Size::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Size::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// And this is all you need to call, the layout function will do it all~
|
|
||||||
layout(
|
|
||||||
root,
|
|
||||||
arena,
|
|
||||||
lookup_map,
|
|
||||||
LayoutConstraints::new(area.width, area.height),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
@ -156,7 +156,7 @@ pub enum SelectableType {
|
|||||||
/// The "main" widgets that are used by bottom to display information!
|
/// The "main" widgets that are used by bottom to display information!
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
#[enum_dispatch(Component, Widget)]
|
#[enum_dispatch(Component, Widget)]
|
||||||
pub enum BottomWidget {
|
pub enum OldBottomWidget {
|
||||||
MemGraph,
|
MemGraph,
|
||||||
TempTable,
|
TempTable,
|
||||||
DiskTable,
|
DiskTable,
|
||||||
@ -172,7 +172,7 @@ pub enum BottomWidget {
|
|||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for BottomWidget {
|
impl Debug for OldBottomWidget {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::MemGraph(_) => write!(f, "MemGraph"),
|
Self::MemGraph(_) => write!(f, "MemGraph"),
|
||||||
|
@ -20,7 +20,7 @@ use crate::{
|
|||||||
custom_legend_chart::{Axis, Dataset},
|
custom_legend_chart::{Axis, Dataset},
|
||||||
TimeChart,
|
TimeChart,
|
||||||
},
|
},
|
||||||
AppConfigFields, Component,
|
AppConfig, Component,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
constants::{
|
constants::{
|
||||||
@ -139,7 +139,7 @@ impl TimeGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`TimeGraph`] given an [`AppConfigFields`].
|
/// Creates a new [`TimeGraph`] given an [`AppConfigFields`].
|
||||||
pub fn from_config(app_config_fields: &AppConfigFields) -> Self {
|
pub fn from_config(app_config_fields: &AppConfig) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
app_config_fields.default_time_value,
|
app_config_fields.default_time_value,
|
||||||
if app_config_fields.hide_time {
|
if app_config_fields.hide_time {
|
||||||
|
@ -8,7 +8,7 @@ use tui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{widgets::tui_stuff::PipeGauge, AppConfigFields, Component, DataCollection, Widget},
|
app::{widgets::tui_stuff::PipeGauge, AppConfig, Component, DataCollection, Widget},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
constants::SIDE_BORDERS,
|
constants::SIDE_BORDERS,
|
||||||
options::layout_options::LayoutRule,
|
options::layout_options::LayoutRule,
|
||||||
@ -26,7 +26,7 @@ pub struct BasicCpu {
|
|||||||
|
|
||||||
impl BasicCpu {
|
impl BasicCpu {
|
||||||
/// Creates a new [`BasicCpu`] given a [`AppConfigFields`].
|
/// Creates a new [`BasicCpu`] given a [`AppConfigFields`].
|
||||||
pub fn from_config(app_config_fields: &AppConfigFields) -> Self {
|
pub fn from_config(app_config_fields: &AppConfig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bounds: Default::default(),
|
bounds: Default::default(),
|
||||||
display_data: Default::default(),
|
display_data: Default::default(),
|
||||||
|
@ -7,7 +7,7 @@ use tui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{AppConfigFields, AxisScaling, Component, DataCollection, Widget},
|
app::{AppConfig, AxisScaling, Component, DataCollection, Widget},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
constants::SIDE_BORDERS,
|
constants::SIDE_BORDERS,
|
||||||
data_conversion::convert_network_data_points,
|
data_conversion::convert_network_data_points,
|
||||||
@ -31,7 +31,7 @@ pub struct BasicNet {
|
|||||||
|
|
||||||
impl BasicNet {
|
impl BasicNet {
|
||||||
/// Creates a new [`BasicNet`] given a [`AppConfigFields`].
|
/// Creates a new [`BasicNet`] given a [`AppConfigFields`].
|
||||||
pub fn from_config(app_config_fields: &AppConfigFields) -> Self {
|
pub fn from_config(app_config_fields: &AppConfig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bounds: Default::default(),
|
bounds: Default::default(),
|
||||||
width: Default::default(),
|
width: Default::default(),
|
||||||
|
@ -13,7 +13,7 @@ use crate::{
|
|||||||
text_table::SimpleColumn,
|
text_table::SimpleColumn,
|
||||||
time_graph::TimeGraphData,
|
time_graph::TimeGraphData,
|
||||||
widgets::tui_stuff::BlockBuilder,
|
widgets::tui_stuff::BlockBuilder,
|
||||||
AppConfigFields, Component, DataCollection, TextTable, TimeGraph, Widget,
|
AppConfig, Component, DataCollection, TextTable, TimeGraph, Widget,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
data_conversion::{convert_cpu_data_points, ConvertedCpuData},
|
data_conversion::{convert_cpu_data_points, ConvertedCpuData},
|
||||||
@ -51,7 +51,7 @@ pub struct CpuGraph {
|
|||||||
|
|
||||||
impl CpuGraph {
|
impl CpuGraph {
|
||||||
/// Creates a new [`CpuGraph`] from a config.
|
/// Creates a new [`CpuGraph`] from a config.
|
||||||
pub fn from_config(app_config_fields: &AppConfigFields) -> Self {
|
pub fn from_config(app_config_fields: &AppConfig) -> Self {
|
||||||
let graph = TimeGraph::from_config(app_config_fields);
|
let graph = TimeGraph::from_config(app_config_fields);
|
||||||
let legend = TextTable::new(vec![
|
let legend = TextTable::new(vec![
|
||||||
SimpleColumn::new_flex("CPU".into(), 0.5),
|
SimpleColumn::new_flex("CPU".into(), 0.5),
|
||||||
|
@ -4,7 +4,7 @@ use tui::{backend::Backend, layout::Rect, widgets::Borders, Frame};
|
|||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
data_farmer::DataCollection, event::ComponentEventResult,
|
data_farmer::DataCollection, event::ComponentEventResult,
|
||||||
sort_text_table::SimpleSortableColumn, text_table::TextTableData, AppConfigFields,
|
sort_text_table::SimpleSortableColumn, text_table::TextTableData, AppConfig,
|
||||||
Component, TextTable, Widget,
|
Component, TextTable, Widget,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
@ -27,7 +27,7 @@ pub struct DiskTable {
|
|||||||
|
|
||||||
impl DiskTable {
|
impl DiskTable {
|
||||||
/// Creates a [`DiskTable`] from a config.
|
/// Creates a [`DiskTable`] from a config.
|
||||||
pub fn from_config(app_config_fields: &AppConfigFields) -> Self {
|
pub fn from_config(app_config_fields: &AppConfig) -> Self {
|
||||||
let table = TextTable::new(vec![
|
let table = TextTable::new(vec![
|
||||||
SimpleSortableColumn::new_flex("Disk".into(), None, false, 0.2),
|
SimpleSortableColumn::new_flex("Disk".into(), None, false, 0.2),
|
||||||
SimpleSortableColumn::new_flex("Mount".into(), None, false, 0.2),
|
SimpleSortableColumn::new_flex("Mount".into(), None, false, 0.2),
|
||||||
|
@ -10,7 +10,7 @@ use tui::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
data_farmer::DataCollection, event::ComponentEventResult, text_table::SimpleColumn,
|
data_farmer::DataCollection, event::ComponentEventResult, text_table::SimpleColumn,
|
||||||
time_graph::TimeGraphData, widgets::tui_stuff::BlockBuilder, AppConfigFields, AxisScaling,
|
time_graph::TimeGraphData, widgets::tui_stuff::BlockBuilder, AppConfig, AxisScaling,
|
||||||
Component, TextTable, TimeGraph, Widget,
|
Component, TextTable, TimeGraph, Widget,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
@ -387,7 +387,7 @@ pub struct NetGraph {
|
|||||||
|
|
||||||
impl NetGraph {
|
impl NetGraph {
|
||||||
/// Creates a new [`NetGraph`] given a [`AppConfigFields`].
|
/// Creates a new [`NetGraph`] given a [`AppConfigFields`].
|
||||||
pub fn from_config(app_config_fields: &AppConfigFields) -> Self {
|
pub fn from_config(app_config_fields: &AppConfig) -> Self {
|
||||||
let graph = TimeGraph::from_config(app_config_fields);
|
let graph = TimeGraph::from_config(app_config_fields);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -590,7 +590,7 @@ pub struct OldNetGraph {
|
|||||||
|
|
||||||
impl OldNetGraph {
|
impl OldNetGraph {
|
||||||
/// Creates a new [`OldNetGraph`] from a [`AppConfigFields`].
|
/// Creates a new [`OldNetGraph`] from a [`AppConfigFields`].
|
||||||
pub fn from_config(config: &AppConfigFields) -> Self {
|
pub fn from_config(config: &AppConfig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
net_graph: NetGraph::from_config(config).hide_legend(),
|
net_graph: NetGraph::from_config(config).hide_legend(),
|
||||||
table: TextTable::new(vec![
|
table: TextTable::new(vec![
|
||||||
|
@ -22,7 +22,7 @@ use crate::{
|
|||||||
query::*,
|
query::*,
|
||||||
text_table::{DesiredColumnWidth, TextTableRow},
|
text_table::{DesiredColumnWidth, TextTableRow},
|
||||||
widgets::tui_stuff::BlockBuilder,
|
widgets::tui_stuff::BlockBuilder,
|
||||||
AppConfigFields, DataCollection, ProcessData,
|
AppConfig, DataCollection, ProcessData,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
data_conversion::{get_string_with_bytes, get_string_with_bytes_per_second},
|
data_conversion::{get_string_with_bytes, get_string_with_bytes_per_second},
|
||||||
@ -278,7 +278,7 @@ pub struct ProcessManager {
|
|||||||
|
|
||||||
impl ProcessManager {
|
impl ProcessManager {
|
||||||
/// Creates a new [`ProcessManager`].
|
/// Creates a new [`ProcessManager`].
|
||||||
pub fn new(process_defaults: &ProcessDefaults, config: &AppConfigFields) -> Self {
|
pub fn new(process_defaults: &ProcessDefaults, config: &AppConfig) -> Self {
|
||||||
let process_table_columns = vec![
|
let process_table_columns = vec![
|
||||||
ProcessSortColumn::new(ProcessSortType::Pid),
|
ProcessSortColumn::new(ProcessSortType::Pid),
|
||||||
ProcessSortColumn::new(ProcessSortType::Name),
|
ProcessSortColumn::new(ProcessSortType::Name),
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
app::{
|
app::{
|
||||||
data_farmer::DataCollection, data_harvester::temperature::TemperatureType,
|
data_farmer::DataCollection, data_harvester::temperature::TemperatureType,
|
||||||
event::ComponentEventResult, sort_text_table::SimpleSortableColumn,
|
event::ComponentEventResult, sort_text_table::SimpleSortableColumn,
|
||||||
text_table::TextTableData, AppConfigFields, Component, TextTable, Widget,
|
text_table::TextTableData, AppConfig, Component, TextTable, Widget,
|
||||||
},
|
},
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
data_conversion::convert_temp_row,
|
data_conversion::convert_temp_row,
|
||||||
@ -26,7 +26,7 @@ pub struct TempTable {
|
|||||||
|
|
||||||
impl TempTable {
|
impl TempTable {
|
||||||
/// Creates a [`TempTable`] from a config.
|
/// Creates a [`TempTable`] from a config.
|
||||||
pub fn from_config(app_config_fields: &AppConfigFields) -> Self {
|
pub fn from_config(app_config_fields: &AppConfig) -> Self {
|
||||||
let table = TextTable::new(vec![
|
let table = TextTable::new(vec![
|
||||||
SimpleSortableColumn::new_flex("Sensor".into(), None, false, 0.8),
|
SimpleSortableColumn::new_flex("Sensor".into(), None, false, 0.8),
|
||||||
SimpleSortableColumn::new_hard("Temp".into(), None, false, Some(5)),
|
SimpleSortableColumn::new_hard("Temp".into(), None, false, Some(5)),
|
||||||
|
@ -77,7 +77,7 @@ fn main() -> Result<()> {
|
|||||||
collection_thread_ctrl_receiver,
|
collection_thread_ctrl_receiver,
|
||||||
thread_termination_lock.clone(),
|
thread_termination_lock.clone(),
|
||||||
thread_termination_cvar.clone(),
|
thread_termination_cvar.clone(),
|
||||||
&app.app_config_fields,
|
&app.app_config,
|
||||||
app.filters.clone(),
|
app.filters.clone(),
|
||||||
app.used_widgets.clone(),
|
app.used_widgets.clone(),
|
||||||
);
|
);
|
||||||
|
@ -1,29 +1,8 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use indextree::{Arena, NodeId};
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use tui::{
|
|
||||||
backend::Backend,
|
|
||||||
layout::{Constraint, Layout, Rect},
|
|
||||||
text::Span,
|
|
||||||
widgets::Paragraph,
|
|
||||||
Frame, Terminal,
|
|
||||||
};
|
|
||||||
|
|
||||||
use canvas_colours::*;
|
use canvas_colours::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{constants::*, options::Config, utils::error, utils::error::BottomError};
|
||||||
app::{
|
|
||||||
self,
|
|
||||||
layout_manager::{generate_layout, ColLayout, LayoutNode, RowLayout},
|
|
||||||
widgets::{Component, Widget},
|
|
||||||
BottomWidget,
|
|
||||||
},
|
|
||||||
constants::*,
|
|
||||||
options::Config,
|
|
||||||
utils::error,
|
|
||||||
utils::error::BottomError,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod canvas_colours;
|
mod canvas_colours;
|
||||||
|
|
||||||
|
@ -8,19 +8,24 @@ use crate::{app::AxisScaling, units::data_units::DataUnit};
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
/// Stores converted data, and caches results.
|
/// Stores converted data, and caches results.
|
||||||
#[derive(Default)]
|
pub struct ConvertedData<'a> {
|
||||||
pub struct ConvertedData {
|
data: &'a DataCollection,
|
||||||
temp_table: Option<Vec<Vec<Cow<'static, str>>>>,
|
temp_table: Option<Vec<Vec<Cow<'static, str>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConvertedData {
|
impl<'a> ConvertedData<'a> {
|
||||||
pub fn temp_table(
|
pub fn new(data: &'a DataCollection) -> Self {
|
||||||
&mut self, data: &DataCollection, temp_type: TemperatureType,
|
Self {
|
||||||
) -> Vec<Vec<Cow<'static, str>>> {
|
data,
|
||||||
|
temp_table: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn temp_table(&mut self, temp_type: TemperatureType) -> Vec<Vec<Cow<'static, str>>> {
|
||||||
match &self.temp_table {
|
match &self.temp_table {
|
||||||
Some(temp_table) => temp_table.clone(),
|
Some(temp_table) => temp_table.clone(),
|
||||||
None => {
|
None => {
|
||||||
let temp_table = if data.temp_harvest.is_empty() {
|
let temp_table = if self.data.temp_harvest.is_empty() {
|
||||||
vec![vec!["No Sensors Found".into(), "".into()]]
|
vec![vec!["No Sensors Found".into(), "".into()]]
|
||||||
} else {
|
} else {
|
||||||
let unit = match temp_type {
|
let unit = match temp_type {
|
||||||
@ -29,7 +34,8 @@ impl ConvertedData {
|
|||||||
data_harvester::temperature::TemperatureType::Fahrenheit => "°F",
|
data_harvester::temperature::TemperatureType::Fahrenheit => "°F",
|
||||||
};
|
};
|
||||||
|
|
||||||
data.temp_harvest
|
self.data
|
||||||
|
.temp_harvest
|
||||||
.iter()
|
.iter()
|
||||||
.map(|temp_harvest| {
|
.map(|temp_harvest| {
|
||||||
let val = temp_harvest.temperature.ceil().to_string();
|
let val = temp_harvest.temperature.ceil().to_string();
|
||||||
|
@ -60,7 +60,7 @@ pub type Pid = libc::pid_t;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ThreadControlEvent {
|
pub enum ThreadControlEvent {
|
||||||
Reset,
|
Reset,
|
||||||
UpdateConfig(Box<app::AppConfigFields>),
|
UpdateConfig(Box<app::AppConfig>),
|
||||||
UpdateUsedWidgets(Box<UsedWidgets>),
|
UpdateUsedWidgets(Box<UsedWidgets>),
|
||||||
UpdateUpdateTime(u64),
|
UpdateUpdateTime(u64),
|
||||||
}
|
}
|
||||||
@ -247,7 +247,7 @@ pub fn create_collection_thread(
|
|||||||
sender: std::sync::mpsc::Sender<RuntimeEvent<AppMessages>>,
|
sender: std::sync::mpsc::Sender<RuntimeEvent<AppMessages>>,
|
||||||
control_receiver: std::sync::mpsc::Receiver<ThreadControlEvent>,
|
control_receiver: std::sync::mpsc::Receiver<ThreadControlEvent>,
|
||||||
termination_ctrl_lock: Arc<Mutex<bool>>, termination_ctrl_cvar: Arc<Condvar>,
|
termination_ctrl_lock: Arc<Mutex<bool>>, termination_ctrl_cvar: Arc<Condvar>,
|
||||||
app_config_fields: &app::AppConfigFields, filters: app::DataFilters,
|
app_config_fields: &app::AppConfig, filters: app::DataFilters,
|
||||||
used_widget_set: UsedWidgets,
|
used_widget_set: UsedWidgets,
|
||||||
) -> std::thread::JoinHandle<()> {
|
) -> std::thread::JoinHandle<()> {
|
||||||
let temp_type = app_config_fields.temperature_type.clone();
|
let temp_type = app_config_fields.temperature_type.clone();
|
||||||
|
@ -20,7 +20,7 @@ use anyhow::{Context, Result};
|
|||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub flags: Option<ConfigFlags>,
|
pub flags: Option<ConfigFlags>,
|
||||||
pub colors: Option<ConfigColours>,
|
pub colors: Option<ConfigColours>,
|
||||||
pub row: Option<Vec<Row>>,
|
pub row: Option<Vec<LayoutRow>>,
|
||||||
pub disk_filter: Option<IgnoreList>,
|
pub disk_filter: Option<IgnoreList>,
|
||||||
pub mount_filter: Option<IgnoreList>,
|
pub mount_filter: Option<IgnoreList>,
|
||||||
pub temp_filter: Option<IgnoreList>,
|
pub temp_filter: Option<IgnoreList>,
|
||||||
@ -191,7 +191,7 @@ pub fn build_app(matches: &clap::ArgMatches<'static>, config: &mut Config) -> Re
|
|||||||
let network_scale_type = get_network_scale_type(matches, config);
|
let network_scale_type = get_network_scale_type(matches, config);
|
||||||
let network_use_binary_prefix = get_network_use_binary_prefix(matches, config);
|
let network_use_binary_prefix = get_network_use_binary_prefix(matches, config);
|
||||||
|
|
||||||
let app_config_fields = AppConfigFields {
|
let app_config_fields = AppConfig {
|
||||||
update_rate_in_milliseconds: get_update_rate_in_milliseconds(matches, config)
|
update_rate_in_milliseconds: get_update_rate_in_milliseconds(matches, config)
|
||||||
.context("Update 'rate' in your config file.")?,
|
.context("Update 'rate' in your config file.")?,
|
||||||
temperature_type: get_temperature(matches, config)
|
temperature_type: get_temperature(matches, config)
|
||||||
@ -218,28 +218,31 @@ pub fn build_app(matches: &clap::ArgMatches<'static>, config: &mut Config) -> Re
|
|||||||
network_use_binary_prefix,
|
network_use_binary_prefix,
|
||||||
};
|
};
|
||||||
|
|
||||||
let layout_tree_output = if let Some(row) = &config.row {
|
let rows: Vec<LayoutRow>;
|
||||||
create_layout_tree(row, process_defaults, &app_config_fields)?
|
let row_ref = if let Some(row) = &config.row {
|
||||||
|
row
|
||||||
} else if get_use_basic_mode(matches, config) {
|
} else if get_use_basic_mode(matches, config) {
|
||||||
if get_use_battery(matches, config) {
|
if get_use_battery(matches, config) {
|
||||||
let rows = toml::from_str::<Config>(DEFAULT_BASIC_BATTERY_LAYOUT)?
|
rows = toml::from_str::<Config>(DEFAULT_BASIC_BATTERY_LAYOUT)?
|
||||||
.row
|
.row
|
||||||
.unwrap();
|
.unwrap();
|
||||||
create_layout_tree(&rows, process_defaults, &app_config_fields)?
|
&rows
|
||||||
} else {
|
} else {
|
||||||
let rows = toml::from_str::<Config>(DEFAULT_BASIC_LAYOUT)?.row.unwrap();
|
rows = toml::from_str::<Config>(DEFAULT_BASIC_LAYOUT)?.row.unwrap();
|
||||||
create_layout_tree(&rows, process_defaults, &app_config_fields)?
|
&rows
|
||||||
}
|
}
|
||||||
} else if get_use_battery(matches, config) {
|
} else if get_use_battery(matches, config) {
|
||||||
let rows = toml::from_str::<Config>(DEFAULT_BATTERY_LAYOUT)?
|
rows = toml::from_str::<Config>(DEFAULT_BATTERY_LAYOUT)?
|
||||||
.row
|
.row
|
||||||
.unwrap();
|
.unwrap();
|
||||||
create_layout_tree(&rows, process_defaults, &app_config_fields)?
|
&rows
|
||||||
} else {
|
} else {
|
||||||
let rows = toml::from_str::<Config>(DEFAULT_LAYOUT)?.row.unwrap();
|
rows = toml::from_str::<Config>(DEFAULT_LAYOUT)?.row.unwrap();
|
||||||
create_layout_tree(&rows, process_defaults, &app_config_fields)?
|
&rows
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let layout_tree_output = create_layout_tree(row_ref, process_defaults, &app_config_fields)?;
|
||||||
|
|
||||||
let disk_filter =
|
let disk_filter =
|
||||||
get_ignore_list(&config.disk_filter).context("Update 'disk_filter' in your config file")?;
|
get_ignore_list(&config.disk_filter).context("Update 'disk_filter' in your config file")?;
|
||||||
let mount_filter = get_ignore_list(&config.mount_filter)
|
let mount_filter = get_ignore_list(&config.mount_filter)
|
||||||
|
@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
/// of children.
|
/// of children.
|
||||||
#[derive(Clone, Deserialize, Debug, Serialize)]
|
#[derive(Clone, Deserialize, Debug, Serialize)]
|
||||||
#[serde(rename = "row")]
|
#[serde(rename = "row")]
|
||||||
pub struct Row {
|
pub struct LayoutRow {
|
||||||
pub child: Option<Vec<RowChildren>>,
|
pub child: Option<Vec<LayoutRowChild>>,
|
||||||
pub ratio: Option<u32>,
|
pub ratio: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,14 +16,14 @@ pub struct Row {
|
|||||||
/// recursion between Row and Col.
|
/// recursion between Row and Col.
|
||||||
#[derive(Clone, Deserialize, Debug, Serialize)]
|
#[derive(Clone, Deserialize, Debug, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum RowChildren {
|
pub enum LayoutRowChild {
|
||||||
Widget(FinalWidget),
|
Widget(FinalWidget),
|
||||||
/// The first one in the list is the "default" selected widget.
|
/// The first one in the list is the "default" selected widget.
|
||||||
Carousel {
|
Carousel {
|
||||||
carousel_children: Vec<String>,
|
carousel_children: Vec<String>,
|
||||||
default: Option<bool>,
|
default: Option<bool>,
|
||||||
},
|
},
|
||||||
Col {
|
LayoutCol {
|
||||||
ratio: Option<u32>,
|
ratio: Option<u32>,
|
||||||
child: Vec<FinalWidget>,
|
child: Vec<FinalWidget>,
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
use tui::{text::Text, widgets::Paragraph, Frame};
|
||||||
|
|
||||||
|
use crate::tuine::{DrawContext, StateContext, TmpComponent};
|
||||||
|
|
||||||
|
/// A [`BatteryTable`] is a widget displaying battery stats.
|
||||||
|
pub struct BatteryTable {}
|
||||||
|
|
||||||
|
impl super::AppWidget for BatteryTable {
|
||||||
|
fn build(
|
||||||
|
ctx: &mut crate::tuine::ViewContext<'_>, painter: &crate::canvas::Painter,
|
||||||
|
config: &crate::app::AppConfig, data: &mut crate::data_conversion::ConvertedData<'_>,
|
||||||
|
) -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message> TmpComponent<Message> for BatteryTable {
|
||||||
|
fn draw<Backend>(
|
||||||
|
&mut self, _state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||||
|
frame: &mut Frame<'_, Backend>,
|
||||||
|
) where
|
||||||
|
Backend: tui::backend::Backend,
|
||||||
|
{
|
||||||
|
let rect = draw_ctx.global_rect();
|
||||||
|
frame.render_widget(
|
||||||
|
Paragraph::new(Text::raw("Battery Table")).block(tui::widgets::Block::default()),
|
||||||
|
rect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
use tui::{text::Text, widgets::Paragraph, Frame};
|
||||||
|
|
||||||
|
use crate::tuine::{DrawContext, StateContext, TmpComponent};
|
||||||
|
|
||||||
|
/// A [`CpuGraph`] is a widget displaying CPU data in a graph-like form, and with controls for showing only
|
||||||
|
/// specific plots.
|
||||||
|
pub struct CpuGraph {}
|
||||||
|
|
||||||
|
impl super::AppWidget for CpuGraph {
|
||||||
|
fn build(
|
||||||
|
ctx: &mut crate::tuine::ViewContext<'_>, painter: &crate::canvas::Painter,
|
||||||
|
config: &crate::app::AppConfig, data: &mut crate::data_conversion::ConvertedData<'_>,
|
||||||
|
) -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message> TmpComponent<Message> for CpuGraph {
|
||||||
|
fn draw<Backend>(
|
||||||
|
&mut self, _state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||||
|
frame: &mut Frame<'_, Backend>,
|
||||||
|
) where
|
||||||
|
Backend: tui::backend::Backend,
|
||||||
|
{
|
||||||
|
let rect = draw_ctx.global_rect();
|
||||||
|
frame.render_widget(
|
||||||
|
Paragraph::new(Text::raw("CPU Graph")).block(tui::widgets::Block::default()),
|
||||||
|
rect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
use tui::{text::Text, widgets::Paragraph, Frame};
|
||||||
|
|
||||||
|
use crate::tuine::{DrawContext, StateContext, TmpComponent};
|
||||||
|
|
||||||
|
/// A [`CpuSimple`] is a widget displaying simple CPU stats.
|
||||||
|
pub struct CpuSimple {}
|
||||||
|
|
||||||
|
impl super::AppWidget for CpuSimple {
|
||||||
|
fn build(
|
||||||
|
ctx: &mut crate::tuine::ViewContext<'_>, painter: &crate::canvas::Painter,
|
||||||
|
config: &crate::app::AppConfig, data: &mut crate::data_conversion::ConvertedData<'_>,
|
||||||
|
) -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message> TmpComponent<Message> for CpuSimple {
|
||||||
|
fn draw<Backend>(
|
||||||
|
&mut self, _state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||||
|
frame: &mut Frame<'_, Backend>,
|
||||||
|
) where
|
||||||
|
Backend: tui::backend::Backend,
|
||||||
|
{
|
||||||
|
let rect = draw_ctx.global_rect();
|
||||||
|
frame.render_widget(
|
||||||
|
Paragraph::new(Text::raw("CPU Simple")).block(tui::widgets::Block::default()),
|
||||||
|
rect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
use tui::{text::Text, widgets::Paragraph, Frame};
|
||||||
|
|
||||||
|
use crate::tuine::{DrawContext, StateContext, TmpComponent};
|
||||||
|
|
||||||
|
/// A [`DiskTable`] is a table displaying disk data.
|
||||||
|
pub struct DiskTable {}
|
||||||
|
|
||||||
|
impl super::AppWidget for DiskTable {
|
||||||
|
fn build(
|
||||||
|
ctx: &mut crate::tuine::ViewContext<'_>, painter: &crate::canvas::Painter,
|
||||||
|
config: &crate::app::AppConfig, data: &mut crate::data_conversion::ConvertedData<'_>,
|
||||||
|
) -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message> TmpComponent<Message> for DiskTable {
|
||||||
|
fn draw<Backend>(
|
||||||
|
&mut self, _state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||||
|
frame: &mut Frame<'_, Backend>,
|
||||||
|
) where
|
||||||
|
Backend: tui::backend::Backend,
|
||||||
|
{
|
||||||
|
let rect = draw_ctx.global_rect();
|
||||||
|
frame.render_widget(
|
||||||
|
Paragraph::new(Text::raw("Disk Table")).block(tui::widgets::Block::default()),
|
||||||
|
rect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
use tui::{text::Text, widgets::Paragraph, Frame};
|
||||||
|
|
||||||
|
use crate::tuine::{DrawContext, StateContext, TmpComponent};
|
||||||
|
|
||||||
|
/// A [`MemGraph`] is a widget displaying RAM/SWAP data in a graph-like form.
|
||||||
|
pub struct MemGraph {}
|
||||||
|
|
||||||
|
impl super::AppWidget for MemGraph {
|
||||||
|
fn build(
|
||||||
|
ctx: &mut crate::tuine::ViewContext<'_>, painter: &crate::canvas::Painter,
|
||||||
|
config: &crate::app::AppConfig, data: &mut crate::data_conversion::ConvertedData<'_>,
|
||||||
|
) -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message> TmpComponent<Message> for MemGraph {
|
||||||
|
fn draw<Backend>(
|
||||||
|
&mut self, _state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||||
|
frame: &mut Frame<'_, Backend>,
|
||||||
|
) where
|
||||||
|
Backend: tui::backend::Backend,
|
||||||
|
{
|
||||||
|
let rect = draw_ctx.global_rect();
|
||||||
|
frame.render_widget(
|
||||||
|
Paragraph::new(Text::raw("Mem Graph")).block(tui::widgets::Block::default()),
|
||||||
|
rect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
use tui::{text::Text, widgets::Paragraph, Frame};
|
||||||
|
|
||||||
|
use crate::tuine::{DrawContext, StateContext, TmpComponent};
|
||||||
|
|
||||||
|
/// A [`MemSimple`] is a widget displaying simple CPU stats.
|
||||||
|
pub struct MemSimple {}
|
||||||
|
|
||||||
|
impl super::AppWidget for MemSimple {
|
||||||
|
fn build(
|
||||||
|
ctx: &mut crate::tuine::ViewContext<'_>, painter: &crate::canvas::Painter,
|
||||||
|
config: &crate::app::AppConfig, data: &mut crate::data_conversion::ConvertedData<'_>,
|
||||||
|
) -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message> TmpComponent<Message> for MemSimple {
|
||||||
|
fn draw<Backend>(
|
||||||
|
&mut self, _state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||||
|
frame: &mut Frame<'_, Backend>,
|
||||||
|
) where
|
||||||
|
Backend: tui::backend::Backend,
|
||||||
|
{
|
||||||
|
let rect = draw_ctx.global_rect();
|
||||||
|
frame.render_widget(
|
||||||
|
Paragraph::new(Text::raw("Mem Simple")).block(tui::widgets::Block::default()),
|
||||||
|
rect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,6 @@
|
|||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
pub mod simple_table;
|
pub mod simple_table;
|
||||||
pub use simple_table::*;
|
pub use simple_table::*;
|
||||||
|
|
||||||
@ -30,3 +33,12 @@ pub use mem_simple::*;
|
|||||||
|
|
||||||
pub mod net_simple;
|
pub mod net_simple;
|
||||||
pub use net_simple::*;
|
pub use net_simple::*;
|
||||||
|
|
||||||
|
use crate::{app::AppConfig, canvas::Painter, data_conversion::ConvertedData, tuine::ViewContext};
|
||||||
|
|
||||||
|
pub trait AppWidget {
|
||||||
|
fn build(
|
||||||
|
ctx: &mut ViewContext<'_>, painter: &Painter, config: &AppConfig,
|
||||||
|
data: &mut ConvertedData<'_>,
|
||||||
|
) -> Self;
|
||||||
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
use tui::{text::Text, widgets::Paragraph, Frame};
|
||||||
|
|
||||||
|
use crate::tuine::{DrawContext, StateContext, TmpComponent};
|
||||||
|
|
||||||
|
/// A [`NetGraph`] is a widget displaying RAM/SWAP data in a graph-like form.
|
||||||
|
pub struct NetGraph {}
|
||||||
|
|
||||||
|
impl super::AppWidget for NetGraph {
|
||||||
|
fn build(
|
||||||
|
ctx: &mut crate::tuine::ViewContext<'_>, painter: &crate::canvas::Painter,
|
||||||
|
config: &crate::app::AppConfig, data: &mut crate::data_conversion::ConvertedData<'_>,
|
||||||
|
) -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message> TmpComponent<Message> for NetGraph {
|
||||||
|
fn draw<Backend>(
|
||||||
|
&mut self, _state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||||
|
frame: &mut Frame<'_, Backend>,
|
||||||
|
) where
|
||||||
|
Backend: tui::backend::Backend,
|
||||||
|
{
|
||||||
|
let rect = draw_ctx.global_rect();
|
||||||
|
frame.render_widget(
|
||||||
|
Paragraph::new(Text::raw("Net Graph")).block(tui::widgets::Block::default()),
|
||||||
|
rect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
use tui::{text::Text, widgets::Paragraph, Frame};
|
||||||
|
|
||||||
|
use crate::tuine::{DrawContext, StateContext, TmpComponent};
|
||||||
|
|
||||||
|
/// A [`NetSimple`] is a widget displaying simple CPU stats.
|
||||||
|
pub struct NetSimple {}
|
||||||
|
|
||||||
|
impl super::AppWidget for NetSimple {
|
||||||
|
fn build(
|
||||||
|
ctx: &mut crate::tuine::ViewContext<'_>, painter: &crate::canvas::Painter,
|
||||||
|
config: &crate::app::AppConfig, data: &mut crate::data_conversion::ConvertedData<'_>,
|
||||||
|
) -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message> TmpComponent<Message> for NetSimple {
|
||||||
|
fn draw<Backend>(
|
||||||
|
&mut self, _state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||||
|
frame: &mut Frame<'_, Backend>,
|
||||||
|
) where
|
||||||
|
Backend: tui::backend::Backend,
|
||||||
|
{
|
||||||
|
let rect = draw_ctx.global_rect();
|
||||||
|
frame.render_widget(
|
||||||
|
Paragraph::new(Text::raw("Net Simple")).block(tui::widgets::Block::default()),
|
||||||
|
rect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
use tui::{text::Text, widgets::Paragraph, Frame};
|
||||||
|
|
||||||
|
use crate::tuine::{DrawContext, StateContext, TmpComponent};
|
||||||
|
|
||||||
|
/// A [`ProcessTable`] is a widget displaying process data, and with controls for searching/filtering entries.
|
||||||
|
pub struct ProcessTable {}
|
||||||
|
|
||||||
|
impl super::AppWidget for ProcessTable {
|
||||||
|
fn build(
|
||||||
|
ctx: &mut crate::tuine::ViewContext<'_>, painter: &crate::canvas::Painter,
|
||||||
|
config: &crate::app::AppConfig, data: &mut crate::data_conversion::ConvertedData<'_>,
|
||||||
|
) -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message> TmpComponent<Message> for ProcessTable {
|
||||||
|
fn draw<Backend>(
|
||||||
|
&mut self, _state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||||
|
frame: &mut Frame<'_, Backend>,
|
||||||
|
) where
|
||||||
|
Backend: tui::backend::Backend,
|
||||||
|
{
|
||||||
|
let rect = draw_ctx.global_rect();
|
||||||
|
frame.render_widget(
|
||||||
|
Paragraph::new(Text::raw("Process Table")).block(tui::widgets::Block::default()),
|
||||||
|
rect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,14 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
app::AppConfig,
|
||||||
canvas::Painter,
|
canvas::Painter,
|
||||||
|
data_conversion::ConvertedData,
|
||||||
tuine::{
|
tuine::{
|
||||||
Bounds, DataRow, DrawContext, LayoutNode, SimpleTable, Size, StateContext, Status,
|
Bounds, DataRow, DrawContext, LayoutNode, SimpleTable, Size, StateContext, Status,
|
||||||
TmpComponent, ViewContext,
|
TmpComponent, ViewContext,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::simple_table;
|
use super::{simple_table, AppWidget};
|
||||||
|
|
||||||
/// A [`TempTable`] is a table displaying temperature data.
|
/// A [`TempTable`] is a table displaying temperature data.
|
||||||
///
|
///
|
||||||
@ -15,9 +17,12 @@ pub struct TempTable<Message> {
|
|||||||
inner: SimpleTable<Message>,
|
inner: SimpleTable<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message> TempTable<Message> {
|
impl<Message> TempTable<Message> {}
|
||||||
pub fn build<R: Into<DataRow>>(
|
|
||||||
ctx: &mut ViewContext<'_>, painter: &Painter, data: Vec<R>,
|
impl<Message> AppWidget for TempTable<Message> {
|
||||||
|
fn build(
|
||||||
|
ctx: &mut ViewContext<'_>, painter: &Painter, config: &AppConfig,
|
||||||
|
data: &mut ConvertedData<'_>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let style = simple_table::StyleSheet {
|
let style = simple_table::StyleSheet {
|
||||||
text: painter.colours.text_style,
|
text: painter.colours.text_style,
|
||||||
@ -25,9 +30,10 @@ impl<Message> TempTable<Message> {
|
|||||||
table_header: painter.colours.table_header_style,
|
table_header: painter.colours.table_header_style,
|
||||||
border: painter.colours.border_style,
|
border: painter.colours.border_style,
|
||||||
};
|
};
|
||||||
|
let rows = data.temp_table(config.temperature_type);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
inner: SimpleTable::build(ctx, style, vec!["Sensor", "Temp"], data),
|
inner: SimpleTable::build(ctx, style, vec!["Sensor", "Temp"], rows),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use tui::Frame;
|
use tui::Frame;
|
||||||
|
|
||||||
use super::{
|
use super::*;
|
||||||
Block, Bounds, Carousel, Container, DrawContext, Empty, Event, Flex, LayoutNode, Shortcut,
|
|
||||||
SimpleTable, Size, StateContext, Status, TempTable, TextTable, TmpComponent,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// An [`Element`] is an instantiated [`Component`].
|
/// An [`Element`] is an instantiated [`Component`].
|
||||||
#[enum_dispatch(TmpComponent<Message>)]
|
#[enum_dispatch(TmpComponent<Message>)]
|
||||||
@ -19,6 +16,15 @@ where
|
|||||||
Shortcut(Shortcut<Message, C>),
|
Shortcut(Shortcut<Message, C>),
|
||||||
TextTable(TextTable<Message>),
|
TextTable(TextTable<Message>),
|
||||||
Empty,
|
Empty,
|
||||||
|
BatteryTable(BatteryTable),
|
||||||
|
CpuGraph(CpuGraph),
|
||||||
|
CpuSimple(CpuSimple),
|
||||||
|
DiskTable(DiskTable),
|
||||||
|
MemGraph(MemGraph),
|
||||||
|
MemSimple(MemSimple),
|
||||||
|
NetGraph(NetGraph),
|
||||||
|
NetSimple(NetSimple),
|
||||||
|
ProcessTable(ProcessTable),
|
||||||
SimpleTable(SimpleTable<Message>),
|
SimpleTable(SimpleTable<Message>),
|
||||||
TempTable(TempTable<Message>),
|
TempTable(TempTable<Message>),
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user