mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-26 23:24:20 +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,
|
||||
constants,
|
||||
data_conversion::ConvertedData,
|
||||
tuine::{Application, Element, Flex, Status, ViewContext},
|
||||
tuine::{Application, Element, Status, ViewContext},
|
||||
units::data_units::DataUnit,
|
||||
Pid,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use indextree::{Arena, NodeId};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
// FIXME: Move this!
|
||||
#[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.
|
||||
#[derive(Debug)]
|
||||
pub struct AppConfigFields {
|
||||
pub struct AppConfig {
|
||||
pub update_rate_in_milliseconds: u64,
|
||||
pub temperature_type: temperature::TemperatureType,
|
||||
pub use_dot: bool,
|
||||
@ -145,34 +143,32 @@ pub enum AppMessages {
|
||||
|
||||
pub struct AppState {
|
||||
pub data_collection: DataCollection,
|
||||
|
||||
pub used_widgets: UsedWidgets,
|
||||
pub filters: DataFilters,
|
||||
pub app_config_fields: AppConfigFields,
|
||||
pub app_config: AppConfig,
|
||||
|
||||
// --- NEW STUFF ---
|
||||
frozen_state: FrozenState,
|
||||
current_screen: CurrentScreen,
|
||||
painter: Painter,
|
||||
pub painter: Painter,
|
||||
terminator: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
/// Creates a new [`AppState`].
|
||||
pub fn new(
|
||||
app_config_fields: AppConfigFields, filters: DataFilters,
|
||||
layout_tree_output: LayoutCreationOutput, painter: Painter,
|
||||
app_config: AppConfig, filters: DataFilters, layout_tree_output: LayoutCreationOutput,
|
||||
painter: Painter,
|
||||
) -> Result<Self> {
|
||||
let LayoutCreationOutput {
|
||||
layout_tree,
|
||||
root: layout_tree_root,
|
||||
layout_tree: _,
|
||||
root: _,
|
||||
widget_lookup_map,
|
||||
selected: selected_widget,
|
||||
selected: _,
|
||||
used_widgets,
|
||||
} = layout_tree_output;
|
||||
|
||||
Ok(Self {
|
||||
app_config_fields,
|
||||
app_config,
|
||||
filters,
|
||||
used_widgets,
|
||||
painter,
|
||||
@ -250,48 +246,7 @@ impl Application for AppState {
|
||||
}
|
||||
|
||||
fn view<'b>(&mut self, ctx: &mut ViewContext<'_>) -> Element<Self::Message> {
|
||||
use crate::tuine::FlexElement;
|
||||
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()
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn destructor(&mut self) {
|
||||
|
@ -5,19 +5,20 @@ use crate::{
|
||||
},
|
||||
error::{BottomError, Result},
|
||||
options::{
|
||||
layout_options::{LayoutRule, Row, RowChildren},
|
||||
layout_options::{LayoutRow, LayoutRowChild, LayoutRule},
|
||||
ProcessDefaults,
|
||||
},
|
||||
tuine::{Element, Flex},
|
||||
};
|
||||
use indextree::{Arena, NodeId};
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::cmp::min;
|
||||
use std::str::FromStr;
|
||||
use tui::layout::Rect;
|
||||
|
||||
use crate::app::widgets::Widget;
|
||||
|
||||
use super::{
|
||||
event::SelectionAction, AppConfigFields, BottomWidget, CpuGraph, TimeGraph, UsedWidgets,
|
||||
event::SelectionAction, AppConfig, AppState, CpuGraph, OldBottomWidget, TimeGraph, UsedWidgets,
|
||||
};
|
||||
|
||||
#[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;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
@ -169,22 +170,6 @@ pub enum LayoutNode {
|
||||
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.
|
||||
pub enum MovementDirection {
|
||||
Left,
|
||||
@ -193,11 +178,40 @@ 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())
|
||||
}
|
||||
|
||||
/// 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, BottomWidget>,
|
||||
pub widget_lookup_map: FxHashMap<NodeId, OldBottomWidget>,
|
||||
pub selected: NodeId,
|
||||
pub used_widgets: UsedWidgets,
|
||||
}
|
||||
@ -207,11 +221,11 @@ pub struct LayoutCreationOutput {
|
||||
/// 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: &[Row], process_defaults: ProcessDefaults, app_config_fields: &AppConfigFields,
|
||||
rows: &[LayoutRow], process_defaults: ProcessDefaults, app_config_fields: &AppConfig,
|
||||
) -> Result<LayoutCreationOutput> {
|
||||
fn add_widget_to_map(
|
||||
widget_lookup_map: &mut FxHashMap<NodeId, BottomWidget>, widget_type: BottomWidgetType,
|
||||
widget_id: NodeId, process_defaults: &ProcessDefaults, app_config_fields: &AppConfigFields,
|
||||
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 {
|
||||
@ -341,7 +355,7 @@ pub fn create_layout_tree(
|
||||
if let Some(children) = &row.child {
|
||||
for child in children {
|
||||
match child {
|
||||
RowChildren::Widget(widget) => {
|
||||
LayoutRowChild::Widget(widget) => {
|
||||
let widget_id = arena.new_node(LayoutNode::Widget(WidgetLayout::default()));
|
||||
row_id.append(widget_id, &mut arena);
|
||||
|
||||
@ -366,7 +380,7 @@ pub fn create_layout_tree(
|
||||
LayoutRule::default(),
|
||||
)?;
|
||||
}
|
||||
RowChildren::Carousel {
|
||||
LayoutRowChild::Carousel {
|
||||
carousel_children,
|
||||
default,
|
||||
} => {
|
||||
@ -422,7 +436,7 @@ pub fn create_layout_tree(
|
||||
);
|
||||
}
|
||||
}
|
||||
RowChildren::Col {
|
||||
LayoutRowChild::LayoutCol {
|
||||
ratio,
|
||||
child: col_child,
|
||||
} => {
|
||||
@ -518,7 +532,7 @@ pub enum MoveWidgetResult {
|
||||
|
||||
/// A more restricted movement, only within a single widget.
|
||||
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,
|
||||
) -> MoveWidgetResult {
|
||||
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::Row`]s or [`LayoutNode::Col`]s are non-leaves.
|
||||
pub fn move_widget_selection(
|
||||
layout_tree: &mut Arena<LayoutNode>, widget_lookup_map: &mut FxHashMap<NodeId, BottomWidget>,
|
||||
current_widget_id: NodeId, direction: MovementDirection,
|
||||
layout_tree: &mut Arena<LayoutNode>,
|
||||
widget_lookup_map: &mut FxHashMap<NodeId, OldBottomWidget>, current_widget_id: NodeId,
|
||||
direction: MovementDirection,
|
||||
) -> MoveWidgetResult {
|
||||
// We first give our currently-selected widget a chance to react to the movement - it may handle it internally!
|
||||
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!
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[enum_dispatch(Component, Widget)]
|
||||
pub enum BottomWidget {
|
||||
pub enum OldBottomWidget {
|
||||
MemGraph,
|
||||
TempTable,
|
||||
DiskTable,
|
||||
@ -172,7 +172,7 @@ pub enum BottomWidget {
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl Debug for BottomWidget {
|
||||
impl Debug for OldBottomWidget {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::MemGraph(_) => write!(f, "MemGraph"),
|
||||
|
@ -20,7 +20,7 @@ use crate::{
|
||||
custom_legend_chart::{Axis, Dataset},
|
||||
TimeChart,
|
||||
},
|
||||
AppConfigFields, Component,
|
||||
AppConfig, Component,
|
||||
},
|
||||
canvas::Painter,
|
||||
constants::{
|
||||
@ -139,7 +139,7 @@ impl TimeGraph {
|
||||
}
|
||||
|
||||
/// 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(
|
||||
app_config_fields.default_time_value,
|
||||
if app_config_fields.hide_time {
|
||||
|
@ -8,7 +8,7 @@ use tui::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
app::{widgets::tui_stuff::PipeGauge, AppConfigFields, Component, DataCollection, Widget},
|
||||
app::{widgets::tui_stuff::PipeGauge, AppConfig, Component, DataCollection, Widget},
|
||||
canvas::Painter,
|
||||
constants::SIDE_BORDERS,
|
||||
options::layout_options::LayoutRule,
|
||||
@ -26,7 +26,7 @@ pub struct BasicCpu {
|
||||
|
||||
impl BasicCpu {
|
||||
/// 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 {
|
||||
bounds: Default::default(),
|
||||
display_data: Default::default(),
|
||||
|
@ -7,7 +7,7 @@ use tui::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
app::{AppConfigFields, AxisScaling, Component, DataCollection, Widget},
|
||||
app::{AppConfig, AxisScaling, Component, DataCollection, Widget},
|
||||
canvas::Painter,
|
||||
constants::SIDE_BORDERS,
|
||||
data_conversion::convert_network_data_points,
|
||||
@ -31,7 +31,7 @@ pub struct BasicNet {
|
||||
|
||||
impl BasicNet {
|
||||
/// 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 {
|
||||
bounds: Default::default(),
|
||||
width: Default::default(),
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
text_table::SimpleColumn,
|
||||
time_graph::TimeGraphData,
|
||||
widgets::tui_stuff::BlockBuilder,
|
||||
AppConfigFields, Component, DataCollection, TextTable, TimeGraph, Widget,
|
||||
AppConfig, Component, DataCollection, TextTable, TimeGraph, Widget,
|
||||
},
|
||||
canvas::Painter,
|
||||
data_conversion::{convert_cpu_data_points, ConvertedCpuData},
|
||||
@ -51,7 +51,7 @@ pub struct CpuGraph {
|
||||
|
||||
impl CpuGraph {
|
||||
/// 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 legend = TextTable::new(vec![
|
||||
SimpleColumn::new_flex("CPU".into(), 0.5),
|
||||
|
@ -4,7 +4,7 @@ use tui::{backend::Backend, layout::Rect, widgets::Borders, Frame};
|
||||
use crate::{
|
||||
app::{
|
||||
data_farmer::DataCollection, event::ComponentEventResult,
|
||||
sort_text_table::SimpleSortableColumn, text_table::TextTableData, AppConfigFields,
|
||||
sort_text_table::SimpleSortableColumn, text_table::TextTableData, AppConfig,
|
||||
Component, TextTable, Widget,
|
||||
},
|
||||
canvas::Painter,
|
||||
@ -27,7 +27,7 @@ pub struct DiskTable {
|
||||
|
||||
impl DiskTable {
|
||||
/// 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![
|
||||
SimpleSortableColumn::new_flex("Disk".into(), None, false, 0.2),
|
||||
SimpleSortableColumn::new_flex("Mount".into(), None, false, 0.2),
|
||||
|
@ -10,7 +10,7 @@ use tui::{
|
||||
use crate::{
|
||||
app::{
|
||||
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,
|
||||
},
|
||||
canvas::Painter,
|
||||
@ -387,7 +387,7 @@ pub struct NetGraph {
|
||||
|
||||
impl NetGraph {
|
||||
/// 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);
|
||||
|
||||
Self {
|
||||
@ -590,7 +590,7 @@ pub struct OldNetGraph {
|
||||
|
||||
impl OldNetGraph {
|
||||
/// Creates a new [`OldNetGraph`] from a [`AppConfigFields`].
|
||||
pub fn from_config(config: &AppConfigFields) -> Self {
|
||||
pub fn from_config(config: &AppConfig) -> Self {
|
||||
Self {
|
||||
net_graph: NetGraph::from_config(config).hide_legend(),
|
||||
table: TextTable::new(vec![
|
||||
|
@ -22,7 +22,7 @@ use crate::{
|
||||
query::*,
|
||||
text_table::{DesiredColumnWidth, TextTableRow},
|
||||
widgets::tui_stuff::BlockBuilder,
|
||||
AppConfigFields, DataCollection, ProcessData,
|
||||
AppConfig, DataCollection, ProcessData,
|
||||
},
|
||||
canvas::Painter,
|
||||
data_conversion::{get_string_with_bytes, get_string_with_bytes_per_second},
|
||||
@ -278,7 +278,7 @@ pub struct ProcessManager {
|
||||
|
||||
impl 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![
|
||||
ProcessSortColumn::new(ProcessSortType::Pid),
|
||||
ProcessSortColumn::new(ProcessSortType::Name),
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
app::{
|
||||
data_farmer::DataCollection, data_harvester::temperature::TemperatureType,
|
||||
event::ComponentEventResult, sort_text_table::SimpleSortableColumn,
|
||||
text_table::TextTableData, AppConfigFields, Component, TextTable, Widget,
|
||||
text_table::TextTableData, AppConfig, Component, TextTable, Widget,
|
||||
},
|
||||
canvas::Painter,
|
||||
data_conversion::convert_temp_row,
|
||||
@ -26,7 +26,7 @@ pub struct TempTable {
|
||||
|
||||
impl TempTable {
|
||||
/// 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![
|
||||
SimpleSortableColumn::new_flex("Sensor".into(), None, false, 0.8),
|
||||
SimpleSortableColumn::new_hard("Temp".into(), None, false, Some(5)),
|
||||
|
@ -77,7 +77,7 @@ fn main() -> Result<()> {
|
||||
collection_thread_ctrl_receiver,
|
||||
thread_termination_lock.clone(),
|
||||
thread_termination_cvar.clone(),
|
||||
&app.app_config_fields,
|
||||
&app.app_config,
|
||||
app.filters.clone(),
|
||||
app.used_widgets.clone(),
|
||||
);
|
||||
|
@ -1,29 +1,8 @@
|
||||
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 crate::{
|
||||
app::{
|
||||
self,
|
||||
layout_manager::{generate_layout, ColLayout, LayoutNode, RowLayout},
|
||||
widgets::{Component, Widget},
|
||||
BottomWidget,
|
||||
},
|
||||
constants::*,
|
||||
options::Config,
|
||||
utils::error,
|
||||
utils::error::BottomError,
|
||||
};
|
||||
use crate::{constants::*, options::Config, utils::error, utils::error::BottomError};
|
||||
|
||||
mod canvas_colours;
|
||||
|
||||
|
@ -8,19 +8,24 @@ use crate::{app::AxisScaling, units::data_units::DataUnit};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Stores converted data, and caches results.
|
||||
#[derive(Default)]
|
||||
pub struct ConvertedData {
|
||||
pub struct ConvertedData<'a> {
|
||||
data: &'a DataCollection,
|
||||
temp_table: Option<Vec<Vec<Cow<'static, str>>>>,
|
||||
}
|
||||
|
||||
impl ConvertedData {
|
||||
pub fn temp_table(
|
||||
&mut self, data: &DataCollection, temp_type: TemperatureType,
|
||||
) -> Vec<Vec<Cow<'static, str>>> {
|
||||
impl<'a> ConvertedData<'a> {
|
||||
pub fn new(data: &'a DataCollection) -> Self {
|
||||
Self {
|
||||
data,
|
||||
temp_table: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn temp_table(&mut self, temp_type: TemperatureType) -> Vec<Vec<Cow<'static, str>>> {
|
||||
match &self.temp_table {
|
||||
Some(temp_table) => temp_table.clone(),
|
||||
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()]]
|
||||
} else {
|
||||
let unit = match temp_type {
|
||||
@ -29,7 +34,8 @@ impl ConvertedData {
|
||||
data_harvester::temperature::TemperatureType::Fahrenheit => "°F",
|
||||
};
|
||||
|
||||
data.temp_harvest
|
||||
self.data
|
||||
.temp_harvest
|
||||
.iter()
|
||||
.map(|temp_harvest| {
|
||||
let val = temp_harvest.temperature.ceil().to_string();
|
||||
|
@ -60,7 +60,7 @@ pub type Pid = libc::pid_t;
|
||||
#[derive(Debug)]
|
||||
pub enum ThreadControlEvent {
|
||||
Reset,
|
||||
UpdateConfig(Box<app::AppConfigFields>),
|
||||
UpdateConfig(Box<app::AppConfig>),
|
||||
UpdateUsedWidgets(Box<UsedWidgets>),
|
||||
UpdateUpdateTime(u64),
|
||||
}
|
||||
@ -247,7 +247,7 @@ pub fn create_collection_thread(
|
||||
sender: std::sync::mpsc::Sender<RuntimeEvent<AppMessages>>,
|
||||
control_receiver: std::sync::mpsc::Receiver<ThreadControlEvent>,
|
||||
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,
|
||||
) -> std::thread::JoinHandle<()> {
|
||||
let temp_type = app_config_fields.temperature_type.clone();
|
||||
|
@ -20,7 +20,7 @@ use anyhow::{Context, Result};
|
||||
pub struct Config {
|
||||
pub flags: Option<ConfigFlags>,
|
||||
pub colors: Option<ConfigColours>,
|
||||
pub row: Option<Vec<Row>>,
|
||||
pub row: Option<Vec<LayoutRow>>,
|
||||
pub disk_filter: Option<IgnoreList>,
|
||||
pub mount_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_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)
|
||||
.context("Update 'rate' in your config file.")?,
|
||||
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,
|
||||
};
|
||||
|
||||
let layout_tree_output = if let Some(row) = &config.row {
|
||||
create_layout_tree(row, process_defaults, &app_config_fields)?
|
||||
let rows: Vec<LayoutRow>;
|
||||
let row_ref = if let Some(row) = &config.row {
|
||||
row
|
||||
} else if get_use_basic_mode(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
|
||||
.unwrap();
|
||||
create_layout_tree(&rows, process_defaults, &app_config_fields)?
|
||||
&rows
|
||||
} else {
|
||||
let rows = toml::from_str::<Config>(DEFAULT_BASIC_LAYOUT)?.row.unwrap();
|
||||
create_layout_tree(&rows, process_defaults, &app_config_fields)?
|
||||
rows = toml::from_str::<Config>(DEFAULT_BASIC_LAYOUT)?.row.unwrap();
|
||||
&rows
|
||||
}
|
||||
} 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
|
||||
.unwrap();
|
||||
create_layout_tree(&rows, process_defaults, &app_config_fields)?
|
||||
&rows
|
||||
} else {
|
||||
let rows = toml::from_str::<Config>(DEFAULT_LAYOUT)?.row.unwrap();
|
||||
create_layout_tree(&rows, process_defaults, &app_config_fields)?
|
||||
rows = toml::from_str::<Config>(DEFAULT_LAYOUT)?.row.unwrap();
|
||||
&rows
|
||||
};
|
||||
|
||||
let layout_tree_output = create_layout_tree(row_ref, process_defaults, &app_config_fields)?;
|
||||
|
||||
let disk_filter =
|
||||
get_ignore_list(&config.disk_filter).context("Update 'disk_filter' in your config file")?;
|
||||
let mount_filter = get_ignore_list(&config.mount_filter)
|
||||
|
@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
|
||||
/// of children.
|
||||
#[derive(Clone, Deserialize, Debug, Serialize)]
|
||||
#[serde(rename = "row")]
|
||||
pub struct Row {
|
||||
pub child: Option<Vec<RowChildren>>,
|
||||
pub struct LayoutRow {
|
||||
pub child: Option<Vec<LayoutRowChild>>,
|
||||
pub ratio: Option<u32>,
|
||||
}
|
||||
|
||||
@ -16,14 +16,14 @@ pub struct Row {
|
||||
/// recursion between Row and Col.
|
||||
#[derive(Clone, Deserialize, Debug, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RowChildren {
|
||||
pub enum LayoutRowChild {
|
||||
Widget(FinalWidget),
|
||||
/// The first one in the list is the "default" selected widget.
|
||||
Carousel {
|
||||
carousel_children: Vec<String>,
|
||||
default: Option<bool>,
|
||||
},
|
||||
Col {
|
||||
LayoutCol {
|
||||
ratio: Option<u32>,
|
||||
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 use simple_table::*;
|
||||
|
||||
@ -30,3 +33,12 @@ pub use mem_simple::*;
|
||||
|
||||
pub mod 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::{
|
||||
app::AppConfig,
|
||||
canvas::Painter,
|
||||
data_conversion::ConvertedData,
|
||||
tuine::{
|
||||
Bounds, DataRow, DrawContext, LayoutNode, SimpleTable, Size, StateContext, Status,
|
||||
TmpComponent, ViewContext,
|
||||
},
|
||||
};
|
||||
|
||||
use super::simple_table;
|
||||
use super::{simple_table, AppWidget};
|
||||
|
||||
/// A [`TempTable`] is a table displaying temperature data.
|
||||
///
|
||||
@ -15,9 +17,12 @@ pub struct TempTable<Message> {
|
||||
inner: SimpleTable<Message>,
|
||||
}
|
||||
|
||||
impl<Message> TempTable<Message> {
|
||||
pub fn build<R: Into<DataRow>>(
|
||||
ctx: &mut ViewContext<'_>, painter: &Painter, data: Vec<R>,
|
||||
impl<Message> TempTable<Message> {}
|
||||
|
||||
impl<Message> AppWidget for TempTable<Message> {
|
||||
fn build(
|
||||
ctx: &mut ViewContext<'_>, painter: &Painter, config: &AppConfig,
|
||||
data: &mut ConvertedData<'_>,
|
||||
) -> Self {
|
||||
let style = simple_table::StyleSheet {
|
||||
text: painter.colours.text_style,
|
||||
@ -25,9 +30,10 @@ impl<Message> TempTable<Message> {
|
||||
table_header: painter.colours.table_header_style,
|
||||
border: painter.colours.border_style,
|
||||
};
|
||||
let rows = data.temp_table(config.temperature_type);
|
||||
|
||||
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 tui::Frame;
|
||||
|
||||
use super::{
|
||||
Block, Bounds, Carousel, Container, DrawContext, Empty, Event, Flex, LayoutNode, Shortcut,
|
||||
SimpleTable, Size, StateContext, Status, TempTable, TextTable, TmpComponent,
|
||||
};
|
||||
use super::*;
|
||||
|
||||
/// An [`Element`] is an instantiated [`Component`].
|
||||
#[enum_dispatch(TmpComponent<Message>)]
|
||||
@ -19,6 +16,15 @@ where
|
||||
Shortcut(Shortcut<Message, C>),
|
||||
TextTable(TextTable<Message>),
|
||||
Empty,
|
||||
BatteryTable(BatteryTable),
|
||||
CpuGraph(CpuGraph),
|
||||
CpuSimple(CpuSimple),
|
||||
DiskTable(DiskTable),
|
||||
MemGraph(MemGraph),
|
||||
MemSimple(MemSimple),
|
||||
NetGraph(NetGraph),
|
||||
NetSimple(NetSimple),
|
||||
ProcessTable(ProcessTable),
|
||||
SimpleTable(SimpleTable<Message>),
|
||||
TempTable(TempTable<Message>),
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user