summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClementTsang <34804052+ClementTsang@users.noreply.github.com>2023-06-05 01:44:40 -0400
committerClementTsang <34804052+ClementTsang@users.noreply.github.com>2023-06-05 23:58:19 -0400
commit000ea7cce462334f4f5a535d946bbc6fdb83f0fe (patch)
tree5da0f09ee7a5a910fd5f41081be7d4fbae69fb06
parenta68a7346c3ecc0e58158556fd62ef6725b4baede (diff)
fully migrate over custom layout creation
-rw-r--r--src/app/layout_manager.rs602
-rw-r--r--src/canvas.rs83
-rw-r--r--src/options.rs228
-rw-r--r--src/options/layout_options.rs207
-rw-r--r--tests/layout_management_tests.rs938
5 files changed, 1048 insertions, 1010 deletions
diff --git a/src/app/layout_manager.rs b/src/app/layout_manager.rs
index 27c0bbe3..da55b9ac 100644
--- a/src/app/layout_manager.rs
+++ b/src/app/layout_manager.rs
@@ -5,7 +5,7 @@ use tui::layout::Direction;
use crate::canvas::LayoutConstraint;
use crate::constants::DEFAULT_WIDGET_ID;
use crate::error::{BottomError, Result};
-use crate::options::layout_options::Row;
+use crate::options::layout_options::{Row, RowChildren};
use crate::utils::error;
/// Represents a start and end coordinate in some dimension.
@@ -15,29 +15,11 @@ type WidgetMappings = (u32, BTreeMap<LineSegment, u64>);
type ColumnRowMappings = (u32, BTreeMap<LineSegment, WidgetMappings>);
type ColumnMappings = (u32, BTreeMap<LineSegment, ColumnRowMappings>);
-#[derive(Clone, Debug)]
-pub enum Node {
- /// A container type, containing more [`Node`] children.
- Container(Container),
-
- /// A leaf node, containing a [`BottomWidget`].
- Widget(BottomWidget),
-}
-
-impl Node {
- pub fn constraint(&self) -> LayoutConstraint {
- match self {
- Node::Container(c) => c.constraint,
- Node::Widget(w) => w.constraint,
- }
- }
-}
-
/// A "container" that contains more [`Node`]s.
#[derive(Debug, Clone)]
pub(crate) struct Container {
/// The children elements.
- pub(crate) children: Vec<usize>,
+ pub(crate) children: Vec<NodeId>,
/// How the container should be sized.
pub(crate) constraint: LayoutConstraint,
@@ -47,7 +29,7 @@ pub(crate) struct Container {
}
impl Container {
- pub(crate) fn row(children: Vec<usize>, sizing: LayoutConstraint) -> Self {
+ pub(crate) fn row(children: Vec<NodeId>, sizing: LayoutConstraint) -> Self {
Self {
children,
constraint: sizing,
@@ -55,7 +37,7 @@ impl Container {
}
}
- pub(crate) fn col(children: Vec<usize>, constraint: LayoutConstraint) -> Self {
+ pub(crate) fn col(children: Vec<NodeId>, constraint: LayoutConstraint) -> Self {
Self {
children,
constraint,
@@ -88,42 +70,301 @@ impl From<ContainerDirection> for Direction {
}
}
+/// An ID for a node in a [`BottomLayout`].
+#[derive(Clone, Copy, Debug)]
+pub enum NodeId {
+ /// The ID for a [`Container`].
+ Container(usize),
+
+ /// The ID for a [`BottomWidget`].
+ Widget(usize),
+}
+
+fn new_cpu(
+ layout: &mut BottomLayout, left_legend: bool, iter_id: &mut u64, width: u32, total: u32,
+) -> NodeId {
+ let cpu_id = *iter_id;
+ *iter_id += 1;
+ let legend_id = *iter_id;
+
+ if left_legend {
+ let cpu_legend = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::CpuLegend, legend_id)
+ .parent_reflector(Some((WidgetDirection::Right, 1))),
+ );
+ let cpu = layout.add_widget(BottomWidget::new_handled(BottomWidgetType::Cpu, cpu_id));
+
+ layout.add_container(Container::row(
+ vec![cpu_legend, cpu],
+ LayoutConstraint::Ratio { a: width, b: total },
+ ))
+ } else {
+ let cpu = layout.add_widget(BottomWidget::new_handled(BottomWidgetType::Cpu, cpu_id));
+ let cpu_legend = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::CpuLegend, legend_id)
+ .parent_reflector(Some((WidgetDirection::Left, 1))),
+ );
+
+ layout.add_container(Container::row(
+ vec![cpu, cpu_legend],
+ LayoutConstraint::Ratio { a: width, b: total },
+ ))
+ }
+}
+
+fn new_proc(layout: &mut BottomLayout, iter_id: &mut u64, width: u32, total: u32) -> NodeId {
+ let main_id = *iter_id;
+ let search_id = *iter_id + 1;
+ *iter_id += 2;
+ let sort_id = *iter_id;
+
+ let main = layout.add_widget(BottomWidget::new_fill(BottomWidgetType::Proc, main_id));
+
+ let search = layout.add_widget(
+ BottomWidget::new_fill(BottomWidgetType::ProcSearch, search_id)
+ .parent_reflector(Some((WidgetDirection::Up, 1))),
+ );
+
+ let sort = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::ProcSort, sort_id)
+ .parent_reflector(Some((WidgetDirection::Right, 2))),
+ );
+
+ let top = layout.add_container(Container::row(
+ vec![sort, main],
+ LayoutConstraint::CanvasHandled,
+ ));
+
+ layout.add_container(Container::col(
+ vec![top, search],
+ LayoutConstraint::Ratio { a: width, b: total },
+ ))
+}
+
+fn new_widget(
+ layout: &mut BottomLayout, widget_type: BottomWidgetType, iter_id: &mut u64, width: u32,
+ total_ratio: u32, left_legend: bool,
+) -> NodeId {
+ *iter_id += 1;
+
+ match widget_type {
+ BottomWidgetType::Cpu => new_cpu(layout, left_legend, iter_id, width, total_ratio),
+ BottomWidgetType::Proc => new_proc(layout, iter_id, width, total_ratio),
+ _ => layout.add_widget(BottomWidget::new(
+ widget_type,
+ *iter_id,
+ LayoutConstraint::Ratio {
+ a: width,
+ b: total_ratio,
+ },
+ )),
+ }
+}
+
/// Represents a more usable representation of the layout, derived from the
/// config.
///
/// Internally represented by an arena-backed tree.
#[derive(Clone, Debug, Default)]
pub struct BottomLayout {
- arena: Vec<Node>,
+ containers: Vec<Container>,
+ widgets: Vec<BottomWidget>,
}
impl BottomLayout {
+ /// Add a container to the layout arena. The ID is returned.
+ pub fn add_container(&mut self, container: Container) -> NodeId {
+ let id = self.containers.len();
+ self.containers.push(container);
+
+ NodeId::Container(id)
+ }
+
/// Add a node to the layout arena. The ID is returned.
- pub fn add_node(&mut self, node: Node) -> usize {
- let id = self.arena.len();
- self.arena.push(node);
+ pub fn add_widget(&mut self, widget: BottomWidget) -> NodeId {
+ let id = self.widgets.len();
+ self.widgets.push(widget);
- id
+ NodeId::Widget(id)
+ }
+
+ /// Get the node with the corresponding ID.
+ pub fn get_container(&self, id: usize) -> Option<&Container> {
+ self.containers.get(id)
}
/// Get the node with the corresponding ID.
- pub fn get_node(&self, id: usize) -> Option<&Node> {
- self.arena.get(id)
+ pub fn get_widget(&self, id: usize) -> Option<&BottomWidget> {
+ self.widgets.get(id)
}
- /// Returns the number of elements in the layout.
+ /// Returns an iterator of all widgets.
+ pub fn widgets_iter(&self) -> impl Iterator<Item = &BottomWidget> {
+ self.widgets.iter()
+ }
+
+ /// Returns the root ID if there is one. If there are no nodes, it will return [`None`].
+ pub fn root_id(&self) -> Option<NodeId> {
+ if self.containers.is_empty() {
+ if self.widgets.is_empty() {
+ None
+ } else {
+ Some(NodeId::Widget(self.widgets.len() - 1))
+ }
+ } else {
+ Some(NodeId::Container(self.containers.len() - 1))
+ }
+ }
+
+ /// Returns the number of elements (widgets + containers) in the layout.
pub fn len(&self) -> usize {
- self.arena.len()
+ self.widgets.len() + self.containers.len()
}
- /// Creates a new [`BottomLayout`] given a slice of [`Row`]s.
- pub fn from_rows(rows: &[Row]) -> error::Result<Self> {
- let mut num_widgets = 0;
+ /// Returns the number of widgets in the layout.
+ pub fn widgets_len(&self) -> usize {
+ self.widgets.len()
+ }
- // TODO: Create the thing; use num_widgets to count how many widgets were inserted.
+ /// Creates a new [`BottomLayout`] given a slice of [`Row`]s, as well as the default widget ID.
+ pub fn from_rows(
+ rows: &[Row], default_widget_type: Option<BottomWidgetType>, mut default_widget_count: u64,
+ left_legend: bool,
+ ) -> error::Result<(Self, u64)> {
+ let mut layout = Self::default();
+ let mut default_widget_id = 1;
+ let mut iter_id = 0; // TODO: In the future, remove this in favour of using the layout's ID system.
+
+ let outer_col_total_ratio = rows.iter().map(|row| row.ratio.unwrap_or(1)).sum();
+ let mut outer_col_children = Vec::with_capacity(rows.len());
+
+ for row in rows {
+ // This code is all ported from the old row-to-bottom_row code, and converted
+ // to work with our new system.
+
+ // TODO: In the future we want to also add percentages.
+ // But for MVP, we aren't going to bother.
+
+ let row_ratio = row.ratio.unwrap_or(1);
+
+ if let Some(children) = &row.child {
+ let mut row_children = Vec::with_capacity(children.len());
+
+ let rows_total_ratio = children
+ .iter()
+ .map(|c| match c {
+ RowChildren::Widget(w) => w.ratio.unwrap_or(1),
+ RowChildren::Col { ratio, .. } => ratio.unwrap_or(1),
+ })
+ .sum();
+
+ for child in children {
+ match child {
+ RowChildren::Widget(widget) => {
+ let width = widget.ratio.unwrap_or(1);
+ let widget_type = widget.widget_type.parse::<BottomWidgetType>()?;
+
+ if let Some(default_widget_type_val) = default_widget_type {
+ if default_widget_type_val == widget_type
+ && default_widget_count > 0
+ {
+ default_widget_count -= 1;
+ if default_widget_count == 0 {
+ default_widget_id = iter_id;
+ }
+ }
+ } else {
+ // Check default flag
+ if let Some(default_widget_flag) = widget.default {
+ if default_widget_flag {
+ default_widget_id = iter_id;
+ }
+ }
+ }
- if num_widgets > 0 {
- todo!()
+ let widget = new_widget(
+ &mut layout,
+ widget_type,
+ &mut iter_id,
+ width,
+ rows_total_ratio,
+ left_legend,
+ );
+
+ row_children.push(widget);
+ }
+ RowChildren::Col {
+ ratio,
+ child: children,
+ } => {
+ let col_ratio = ratio.unwrap_or(1);
+ let mut col_children = vec![];
+
+ let inner_col_total_ratio =
+ children.iter().map(|w| w.ratio.unwrap_or(1)).sum();
+ for widget in children {
+ let widget_type = widget.widget_type.parse::<BottomWidgetType>()?;
+ let height = widget.ratio.unwrap_or(1);
+
+ if let Some(default_widget_type_val) = default_widget_type {
+ if default_widget_type_val == widget_type
+ && default_widget_count > 0
+ {
+ default_widget_count -= 1;
+ if default_widget_count == 0 {
+ default_widget_id = iter_id;
+ }
+ }
+ } else {
+ // Check default flag
+ if let Some(default_widget_flag) = widget.default {
+ if default_widget_flag {
+ default_widget_id = iter_id;
+ }
+ }
+ }
+
+ let widget = new_widget(
+ &mut layout,
+ widget_type,
+ &mut iter_id,
+ height,
+ inner_col_total_ratio,
+ left_legend,
+ );
+
+ col_children.push(widget);
+ }
+
+ row_children.push(layout.add_container(Container::col(
+ col_children,
+ LayoutConstraint::Ratio {
+ a: col_ratio,
+ b: rows_total_ratio,
+ },
+ )));
+ }
+ }
+ }
+
+ outer_col_children.push(layout.add_container(Container::row(
+ row_children,
+ LayoutConstraint::Ratio {
+ a: row_ratio,
+ b: outer_col_total_ratio,
+ },
+ )));
+ };
+ }
+
+ layout.add_container(Container::col(
+ outer_col_children,
+ LayoutConstraint::FlexGrow,
+ ));
+
+ if layout.widgets_len() > 0 {
+ layout.get_movement_mappings();
+ Ok((layout, default_widget_id))
} else {
Err(error::BottomError::ConfigError(
"please have at least one widget under the '[[row]]' section.".to_string(),
@@ -133,153 +374,162 @@ impl BottomLayout {
/// Creates a new [`BottomLayout`] following the basic layout.
pub fn new_basic(use_battery: bool) -> Self {
+ let mut layout = BottomLayout::default();
+
let table_widgets = if use_battery {
- let disk_widget = BottomWidget::new_handled(BottomWidgetType::Disk, 4)
- .up_neighbour(Some(100))
- .left_neighbour(Some(8))
- .right_neighbour(Some(DEFAULT_WIDGET_ID + 2));
+ let disk = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::Disk, 4)
+ .up_neighbour(Some(100))
+ .left_neighbour(Some(8))
+ .right_neighbour(Some(DEFAULT_WIDGET_ID + 2)),
+ );
+
+ let proc = {
+ let proc_sort = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::ProcSort, DEFAULT_WIDGET_ID + 2)
+ .up_neighbour(Some(100))
+ .down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
+ .left_neighbour(Some(4))
+ .right_neighbour(Some(DEFAULT_WIDGET_ID))
+ .parent_reflector(Some((WidgetDirection::Right, 2))),
+ );
+
+ let main_proc = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::Proc, DEFAULT_WIDGET_ID)
+ .up_neighbour(Some(100))
+ .down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
+ .left_neighbour(Some(DEFAULT_WIDGET_ID + 2))
+ .right_neighbour(Some(7)),
+ );
- let proc_sort =
- BottomWidget::new_handled(BottomWidgetType::ProcSort, DEFAULT_WIDGET_ID + 2)
+ let proc_search = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::ProcSearch, DEFAULT_WIDGET_ID + 1)
+ .up_neighbour(Some(DEFAULT_WIDGET_ID))
+ .left_neighbour(Some(4))
+ .right_neighbour(Some(7))
+ .parent_reflector(Some((WidgetDirection::Up, 1))),
+ );
+
+ let top = layout.add_container(Container::row(
+ vec![proc_sort, main_proc],
+ LayoutConstraint::CanvasHandled,
+ ));
+ layout.add_container(Container::col(
+ vec![top, proc_search],
+ LayoutConstraint::CanvasHandled,
+ ))
+ };
+
+ let temp = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::Temp, 7)
.up_neighbour(Some(100))
- .down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
- .left_neighbour(Some(4))
- .right_neighbour(Some(DEFAULT_WIDGET_ID))
- .parent_reflector(Some((WidgetDirection::Right, 2)));
-
- let proc = BottomWidget::new_handled(BottomWidgetType::Proc, DEFAULT_WIDGET_ID)
- .up_neighbour(Some(100))
- .down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
- .left_neighbour(Some(DEFAULT_WIDGET_ID + 2))
- .right_neighbour(Some(7));
-
- let proc_search =
- BottomWidget::new_handled(BottomWidgetType::ProcSearch, DEFAULT_WIDGET_ID + 1)
- .up_neighbour(Some(DEFAULT_WIDGET_ID))
- .left_neighbour(Some(4))
- .right_neighbour(Some(7))
- .parent_reflector(Some((WidgetDirection::Up, 1)));
-
- let temp = BottomWidget::new_handled(BottomWidgetType::Temp, 7)
- .up_neighbour(Some(100))
- .left_neighbour(Some(DEFAULT_WIDGET_ID))
- .right_neighbour(Some(8));
-
- let battery = BottomWidget::new_handled(BottomWidgetType::Battery, 8)
- .up_neighbour(Some(100))
- .left_neighbour(Some(7))
- .right_neighbour(Some(4));
-
- vec![
- BottomCol::new(vec![
- BottomColRow::new(vec![disk_widget]).canvas_handle_height(true)
- ])
- .canvas_handle_width(true),
- BottomCol::new(vec![
- BottomColRow::new(vec![proc_sort, proc])
- .canvas_handle_height(true)
- .total_widget_ratio(3),
- BottomColRow::new(vec![proc_search]).canvas_handle_height(true),
- ])
- .canvas_handle_width(true),
- BottomCol::new(vec![
- BottomColRow::new(vec![temp]).canvas_handle_height(true)
- ])
- .canvas_handle_width(true),
- BottomCol::new(vec![
- BottomColRow::new(vec![battery]).canvas_handle_height(true)
- ])
- .canvas_handle_width(true),
- ]
- } else {
- let disk = BottomWidget::new_handled(BottomWidgetType::Disk, 4)
- .up_neighbour(Some(100))
- .left_neighbour(Some(7))
- .right_neighbour(Some(DEFAULT_WIDGET_ID + 2));
+ .left_neighbour(Some(DEFAULT_WIDGET_ID))
+ .right_neighbour(Some(8)),
+ );
- let proc_sort =
- BottomWidget::new_handled(BottomWidgetType::ProcSort, DEFAULT_WIDGET_ID + 2)
+ let battery = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::Battery, 8)
.up_neighbour(Some(100))
- .down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
- .left_neighbour(Some(4))
- .right_neighbour(Some(DEFAULT_WIDGET_ID))
- .parent_reflector(Some((WidgetDirection::Right, 2)));
-
- let proc = BottomWidget::new_handled(BottomWidgetType::Proc, DEFAULT_WIDGET_ID)
- .up_neighbour(Some(100))
- .down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
- .left_neighbour(Some(DEFAULT_WIDGET_ID + 2))
- .right_neighbour(Some(7));
-
- let proc_search =
- BottomWidget::new_handled(BottomWidgetType::ProcSearch, DEFAULT_WIDGET_ID + 1)
- .up_neighbour(Some(DEFAULT_WIDGET_ID))
- .left_neighbour(Some(4))
- .right_neighbour(Some(7))
- .parent_reflector(Some((WidgetDirection::Up, 1)));
-
- let temp = BottomWidget::new_handled(BottomWidgetType::Temp, 7)
- .up_neighbour(Some(100))
- .left_neighbour(Some(DEFAULT_WIDGET_ID))
- .right_neighbour(Some(4));
-
- vec![
- BottomCol::new(vec![
- BottomColRow::new(vec![disk]).canvas_handle_height(true)
- ])
- .canvas_handle_width(true),
- BottomCol::new(vec![
- BottomColRow::new(vec![proc_sort, proc]).canvas_handle_height(true),
- BottomColRow::new(vec![proc_search]).canvas_handle_height(true),
- ])
- .canvas_handle_width(true),
- BottomCol::new(vec![
- BottomColRow::new(vec![temp]).canvas_handle_height(true)
- ])
- .canvas_handle_width(true),
- ]
- };
+ .left_neighbour(Some(7))
+ .right_neighbour(Some(4)),
+ );
- let cpu = BottomWidget::new_handled(BottomWidgetType::BasicCpu, 1).down_neighbour(Some(2));
+ layout.add_container(Container::row(
+ vec![disk, proc, temp, battery],
+ LayoutConstraint::CanvasHandled,
+ ))
+ } else {
+ let disk = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::Disk, 4)
+ .up_neighbour(Some(100))
+ .left_neighbour(Some(7))
+ .right_neighbour(Some(DEFAULT_WIDGET_ID + 2)),
+ );
+
+ let proc = {
+ let proc_sort = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::ProcSort, DEFAULT_WIDGET_ID + 2)
+ .up_neighbour(Some(100))
+ .down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
+ .left_neighbour(Some(4))
+ .right_neighbour(Some(DEFAULT_WIDGET_ID))
+ .parent_reflector(Some((WidgetDirection::Right, 2))),
+ );
+
+ let main_proc = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::Proc, DEFAULT_WIDGET_ID)
+ .up_neighbour(Some(100))
+ .down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
+ .left_neighbour(Some(DEFAULT_WIDGET_ID + 2))
+ .right_neighbour(Some(7)),
+ );
- let mem = BottomWidget::new_handled(BottomWidgetType::BasicMem, 2)
- .up_neighbour(Some(1))
- .down_neighbour(Some(100))
- .right_neighbour(Some(3));
+ let proc_search = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::ProcSearch, DEFAULT_WIDGET_ID + 1)
+ .up_neighbour(Some(DEFAULT_WIDGET_ID))
+ .left_neighbour(Some(4))
+ .right_neighbour(Some(7))
+ .parent_reflector(Some((WidgetDirection::Up, 1))),
+ );
- let net = BottomWidget::new_handled(BottomWidgetType::BasicNet, 3)
- .up_neighbour(Some(1))
- .down_neighbour(Some(100))
- .left_neighbour(Some(2));
+ let top = layout.add_container(Container::row(
+ vec![proc_sort, main_proc],
+ LayoutConstraint::CanvasHandled,
+ ));
+ layout.add_container(Container::col(
+ vec![top, proc_search],
+ LayoutConstraint::CanvasHandled,
+ ))
+ };
+
+ let temp = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::Temp, 7)
+ .up_neighbour(Some(100))
+ .left_neighbour(Some(DEFAULT_WIDGET_ID))
+ .right_neighbour(Some(4)),
+ );
- let table =
- BottomWidget::new_handled(BottomWidgetType::BasicTables, 100).up_neighbour(Some(2));
+ layout.add_container(Container::row(
+ vec![disk, proc, temp],
+ LayoutConstraint::CanvasHandled,
+ ))
+ };
- let mut layout = BottomLayout::default();
- // TODO: Add nodes; should we instead back with a hashmap?
-
- // BottomLayout {
- // total_row_height_ratio: 3,
- // rows: vec![
- // BottomRow::new(vec![BottomCol::new(vec![
- // BottomColRow::new(vec![cpu]).canvas_handle_height(true)
- // ])
- // .canvas_handle_width(true)])
- // .canvas_handle_height(true),
- // BottomRow::new(vec![BottomCol::new(vec![BottomColRow::new(vec![
- // mem, net,
- // ])
- // .canvas_handle_height(true)])
- // .canvas_handle_width(true)])
- // .canvas_handle_height(true),
- // BottomRow::new(vec![BottomCol::new(vec![
- // BottomColRow::new(vec![table]).canvas_handle_height(true)
- // ])
- // .canvas_handle_width(true)])
- // .canvas_handle_height(true),
- // BottomRow::new(table_widgets).canvas_handle_height(true),
- // ],
- // }
+ let cpu = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::BasicCpu, 1).down_neighbour(Some(2)),
+ );
+
+ let mem = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::BasicMem, 2)
+ .up_neighbour(Some(1))
+ .down_neighbour(Some(100))
+ .right_neighbour(Some(3)),
+ );
+
+ let net = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::BasicNet, 3)
+ .up_neighbour(Some(1))
+ .down_neighbour(Some(100))
+ .left_neighbour(Some(2)),
+ );
+
+ let net = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::BasicTables, 100).up_neighbour(Some(2)),
+ );
+
+ let middle_bars = layout.add_container(Container::row(
+ vec![mem, net],
+ LayoutConstraint::CanvasHandled,
+ ));
+
+ let table = layout.add_widget(
+ BottomWidget::new_handled(BottomWidgetType::BasicTables, 100).up_neighbour(Some(2)),
+ );
+
+ layout.add_container(Container::col(
+ vec![cpu, middle_bars, table, table_widgets],
+ LayoutConstraint::CanvasHandled,
+ ));
layout
}
@@ -1000,7 +1250,7 @@ impl BottomWidget {
Self::new(
widget_type,
widget_id,
- LayoutConstraint::Ratio { lhs: 1, rhs: 1 },
+ LayoutConstraint::Ratio { a: 1, b: 1 },
)
}
@@ -1036,7 +1286,7 @@ impl BottomWidget {
}
}
-#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
pub enum BottomWidgetType {
#[default]
Empty,
diff --git a/src/canvas.rs b/src/canvas.rs
index 31bff6a7..19a5a3d5 100644
--- a/src/canvas.rs
+++ b/src/canvas.rs
@@ -13,7 +13,7 @@ use tui::{
use crate::{
app::{
self,
- layout_manager::{BottomColRow, BottomLayout, BottomWidget, BottomWidgetType, Node},
+ layout_manager::{BottomColRow, BottomLayout, BottomWidget, BottomWidgetType, NodeId},
App,
},
constants::*,
@@ -72,7 +72,7 @@ pub struct Painter {
#[derive(Debug, Clone, Copy)]
pub enum LayoutConstraint {
/// Denotes that the canvas should follow the given ratio of `lhs:rhs` to determine spacing for the element.
- Ratio { lhs: u32, rhs: u32 },
+ Ratio { a: u32, b: u32 },
/// Denotes that the canvas should let this element grow to take up whatever remaining space is left after
/// sizing the other sibling elements.
@@ -94,7 +94,7 @@ impl Painter {
width: 0,
styled_help_text: Vec::default(),
widget_layout,
- derived_widget_draw_locs: Vec::default(),
+ derived_widget_draw_locs: HashMap::default(),
};
painter.complete_painter_init();
@@ -496,7 +496,7 @@ impl Painter {
constraints.zip(sizes.iter_mut()).enumerate()
{
match constraint {
- LayoutConstraint::Ratio { lhs, rhs } => {
+ LayoutConstraint::Ratio { a: lhs, b: rhs } => {
match direction {
Direction::Horizontal => {
let amount = (((area.width as u32) * lhs) / rhs) as u16;
@@ -612,45 +612,60 @@ impl Painter {
}
// Do a preorder traversal through the tree in, and calculate the draw [`Rect`]s for each widget.
- let root_id = self.widget_layout.len().saturating_sub(1);
- let mut queue = vec![(root_id, terminal_size)];
- while let Some((current_id, rect)) = queue.pop() {
- if let Some(widget) = self.widget_layout.get_node(current_id) {
- match widget {
- Node::Container(container) => {
- let constraints = container.children.iter().map(|child| {
- if let Some(node) = self.widget_layout.get_node(*child) {
- node.constraint()
- } else {
- LayoutConstraint::FlexGrow
- }
- });
-
- let rects =
- get_rects(container.direction().into(), constraints, rect);
-
- // If it's a container, push in reverse order to the stack.
- for child in container
- .children
- .iter()
- .cloned()
- .zip(rects.into_iter())
- .rev()
+ if let Some(root_id) = self.widget_layout.root_id() {
+ let mut queue = vec![(root_id, terminal_size)];
+ while let Some((current_id, rect)) = queue.pop() {
+ match current_id {
+ NodeId::Container(current_id) => {
+ if let Some(container) =
+ self.widget_layout.get_container(current_id)
{
- queue.push(child);
+ let constraints = container.children.iter().map(|child| {
+ match child {
+ NodeId::Container(child) => self
+ .widget_layout
+ .get_container(*child)
+ .map(|c| c.constraint),
+ NodeId::Widget(child) => self
+ .widget_layout
+ .get_widget(*child)
+ .map(|w| w.constraint),
+ }
+ .unwrap_or(LayoutConstraint::FlexGrow)
+ });
+
+ let rects = get_rects(
+ container.direction().into(),
+ constraints,
+ rect,
+ );
+
+ // If it's a container, push in reverse order to the stack.
+ for child in container
+ .children
+ .iter()
+ .cloned()
+ .zip(rects.into_iter())
+ .rev()
+ {
+ queue.push(child);
+ }
}
}
- Node::Widget(widget) => {
- // If we're instead on a widget, we can instead assign the rect to the widget.
- self.derived_widget_draw_locs.insert(current_id, rect);
+ NodeId::Widget(current_id) => {
+ if let Some(widget) = self.widget_layout.get_widget(current_id)
+ {
+ // If we're instead on a widget, we can instead assign the rect to the widget.
+ self.derived_widget_draw_locs.insert(current_id, rect);
+ }
}
}
}
}
} else {
for (id, rect) in &self.derived_widget_draw_locs {
- match &self.widget_layout.get_node(*id) {
- Some(Node::Widget(widget)) => {
+ match self.widget_layout.get_widget(*id) {
+ Some(widget) => {
self.draw_widget(f, app_state, widget, *rect);
}
_ => {
diff --git a/src/options.rs b/src/options.rs
index 1190fb4a..2d9c0e66 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -274,116 +274,110 @@ pub fn build_app(
is_command: is_default_command,
};
- for row in &widget_layout.rows {
- for col in &row.children {
- for col_row in &col.children {
- for widget in &col_row.children {
- widget_map.insert(widget.widget_id, widget.clone());
- if let Some(default_widget_type) = &default_widget_type_option {
- if !is_custom_layout || use_basic_mode {
- match widget.widget_type {
- BasicCpu => {
- if let Cpu = *default_widget_type {
- initial_widget_id = widget.widget_id;
- initial_widget_type = Cpu;
- }
- }
- BasicMem => {
- if let Mem = *default_widget_type {
- initial_widget_id = widget.widget_id;
- initial_widget_type = Cpu;
- }
- }
- BasicNet => {
- if let Net = *default_widget_type {
- initial_widget_id = widget.widget_id;
- initial_