summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClementTsang <34804052+ClementTsang@users.noreply.github.com>2023-06-04 14:48:16 -0400
committerClementTsang <34804052+ClementTsang@users.noreply.github.com>2023-06-04 14:48:16 -0400
commita68a7346c3ecc0e58158556fd62ef6725b4baede (patch)
treee7fd9a9321bb4216f5ea34b4e455d6922fbb908b
parent6555d2d7514077fdff37fa55214382afb81ebe7e (diff)
remove code dedup
-rw-r--r--src/app/layout_manager.rs494
-rw-r--r--src/canvas.rs253
-rw-r--r--src/canvas/widgets/process_table.rs12
-rw-r--r--src/options.rs48
-rw-r--r--tests/layout_management_tests.rs2
5 files changed, 402 insertions, 407 deletions
diff --git a/src/app/layout_manager.rs b/src/app/layout_manager.rs
index 4ac882a0..27c0bbe3 100644
--- a/src/app/layout_manager.rs
+++ b/src/app/layout_manager.rs
@@ -1,15 +1,12 @@
use std::collections::BTreeMap;
+use tui::layout::Direction;
+
+use crate::canvas::LayoutConstraint;
use crate::constants::DEFAULT_WIDGET_ID;
use crate::error::{BottomError, Result};
-
-/// Represents a more usable representation of the layout, derived from the
-/// config.
-#[derive(Clone, Debug)]
-pub struct BottomLayout {
- pub rows: Vec<BottomRow>,
- pub total_row_height_ratio: u32,
-}
+use crate::options::layout_options::Row;
+use crate::utils::error;
/// Represents a start and end coordinate in some dimension.
type LineSegment = (u32, u32);
@@ -18,8 +15,276 @@ 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>,
+
+ /// How the container should be sized.
+ pub(crate) constraint: LayoutConstraint,
+
+ /// The direction.
+ direction: ContainerDirection,
+}
+
+impl Container {
+ pub(crate) fn row(children: Vec<usize>, sizing: LayoutConstraint) -> Self {
+ Self {
+ children,
+ constraint: sizing,
+ direction: ContainerDirection::Row,
+ }
+ }
+
+ pub(crate) fn col(children: Vec<usize>, constraint: LayoutConstraint) -> Self {
+ Self {
+ children,
+ constraint,
+ direction: ContainerDirection::Col,
+ }
+ }
+
+ /// Returns the direction of the container.
+ pub fn direction(&self) -> ContainerDirection {
+ self.direction
+ }
+}
+
+/// The direction in which children in a [`BottomContainer`] will be laid out.
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum ContainerDirection {
+ /// Lay out all children horizontally.
+ Row,
+
+ /// Lay out all children vertically.
+ Col,
+}
+
+impl From<ContainerDirection> for Direction {
+ fn from(value: ContainerDirection) -> Self {
+ match value {
+ ContainerDirection::Row => Direction::Horizontal,
+ ContainerDirection::Col => Direction::Vertical,
+ }
+ }
+}
+
+/// 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>,
+}
+
impl BottomLayout {
- pub fn get_movement_mappings(&mut self) {
+ /// 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);
+
+ id
+ }
+
+ /// Get the node with the corresponding ID.
+ pub fn get_node(&self, id: usize) -> Option<&Node> {
+ self.arena.get(id)
+ }
+
+ /// Returns the number of elements in the layout.
+ pub fn len(&self) -> usize {
+ self.arena.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;
+
+ // TODO: Create the thing; use num_widgets to count how many widgets were inserted.
+
+ if num_widgets > 0 {
+ todo!()
+ } else {
+ Err(error::BottomError::ConfigError(
+ "please have at least one widget under the '[[row]]' section.".to_string(),
+ ))
+ }
+ }
+
+ /// Creates a new [`BottomLayout`] following the basic layout.
+ pub fn new_basic(use_battery: bool) -> Self {
+ 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 proc_sort =
+ 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 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));
+
+ let proc_sort =
+ 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 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),
+ ]
+ };
+
+ let cpu = BottomWidget::new_handled(BottomWidgetType::BasicCpu, 1).down_neighbour(Some(2));
+
+ let mem = BottomWidget::new_handled(BottomWidgetType::BasicMem, 2)
+ .up_neighbour(Some(1))
+ .down_neighbour(Some(100))
+ .right_neighbour(Some(3));
+
+ let net = BottomWidget::new_handled(BottomWidgetType::BasicNet, 3)
+ .up_neighbour(Some(1))
+ .down_neighbour(Some(100))
+ .left_neighbour(Some(2));
+
+ let table =
+ BottomWidget::new_handled(BottomWidgetType::BasicTables, 100).up_neighbour(Some(2));
+
+ 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),
+ // ],
+ // }
+
+ layout
+ }
+
+ fn get_movement_mappings(&mut self) {
#[allow(clippy::suspicious_operation_groupings)] // Have to enable this, clippy really doesn't like me doing this with tuples...
fn is_intersecting(a: LineSegment, b: LineSegment) -> bool {
a.0 >= b.0 && a.1 <= b.1
@@ -39,7 +304,7 @@ impl BottomLayout {
}
// Now we need to create the correct mapping for moving from a specific
- // widget to another
+ // widget to another.
let mut layout_mapping: BTreeMap<LineSegment, ColumnMappings> = BTreeMap::new();
let mut total_height = 0;
for row in &self.rows {
@@ -529,201 +794,6 @@ impl BottomLayout {
height_cursor += row.row_height_ratio;
}
}
-
- pub fn init_basic_default(use_battery: bool) -> Self {
- 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 proc_sort =
- 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 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));
-
- let proc_sort =
- 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 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),
- ]
- };
-
- let cpu = BottomWidget::new_handled(BottomWidgetType::BasicCpu, 1).down_neighbour(Some(2));
-
- let mem = BottomWidget::new_handled(BottomWidgetType::BasicMem, 2)
- .up_neighbour(Some(1))
- .down_neighbour(Some(100))
- .right_neighbour(Some(3));
-
- let net = BottomWidget::new_handled(BottomWidgetType::BasicNet, 3)
- .up_neighbour(Some(1))
- .down_neighbour(Some(100))
- .left_neighbour(Some(2));
-
- let table =
- BottomWidget::new_handled(BottomWidgetType::BasicTables, 100).up_neighbour(Some(2));
-
- 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),
- ],
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub(crate) enum BottomLayoutNode {
- /// A container type, containing more [`BottomLayoutNode`] children.
- Container(BottomContainer),
-
- /// A leaf node, containing a [`BottomWidget`].
- Widget(BottomWidget),
-}
-
-/// A "container" that contains more [`BottomLayoutNode`]s.
-#[derive(Debug, Clone)]
-pub(crate) struct BottomContainer {
- /// The children elements.
- pub(crate) children: Vec<BottomLayoutNode>,
-
- /// How the container should be sized.
- pub(crate) sizing: ElementSizing,
-}
-
-/// The direction in which children in a [`BottomContainer`] will be laid out.
-#[derive(Debug, Clone, Copy)]
-pub(crate) enum BottomContainerType {
- /// Lay out all children horizontally.
- Row,
-
- /// Lay out all children vertically.
- Col,
-}
-
-/// How the element sizing should be determined.
-///
-/// FIXME: This should honestly be matched tighter to the canvas system; we currently have two very similar ways of doing things!
-#[derive(Debug, Clone, Copy)]
-pub(crate) enum ElementSizing {
- /// Denotes that the canvas should follow the given ratio of `lhs:rhs` to determine spacing for the element.
- Ratio { lhs: u32, rhs: u32 },
-
- /// Denotes that the canvas should let this element grow to take up whatever remaining space is left after
- /// sizing the other sibling elements.
- FlexGrow,
-
- /// Denotes that the canvas can do whatever it likes to determine spacing for the element.
- CanvasHandled,
-
- /// Denotes that the element should take up 100% of the space.
- Fill,
}
/// Represents a single row in the layout.
@@ -884,7 +954,7 @@ pub struct BottomWidget {
pub widget_id: u64,
/// How the widget should be sized by the canvas.
- pub sizing: ElementSizing,
+ pub constraint: LayoutConstraint,
/// The widget ID to go to when moving to the left.
pub left_neighbour: Option<u64>,
@@ -910,12 +980,12 @@ pub struct BottomWidget {
impl BottomWidget {
pub(crate) fn new(
- widget_type: BottomWidgetType, widget_id: u64, sizing: ElementSizing,
+ widget_type: BottomWidgetType, widget_id: u64, constraint: LayoutConstraint,
) -> Self {
Self {
widget_type,
widget_id,
- sizing,
+ constraint,
left_neighbour: None,
right_neighbour: None,
up_neighbour: None,
@@ -927,11 +997,15 @@ impl BottomWidget {
}
pub(crate) fn new_fill(widget_type: BottomWidgetType, widget_id: u64) -> Self {
- Self::new(widget_type, widget_id, ElementSizing::Fill)
+ Self::new(
+ widget_type,
+ widget_id,
+ LayoutConstraint::Ratio { lhs: 1, rhs: 1 },
+ )
}
pub(crate) fn new_handled(widget_type: BottomWidgetType, widget_id: u64) -> Self {
- Self::new(widget_type, widget_id, ElementSizing::CanvasHandled)
+ Self::new(widget_type, widget_id, LayoutConstraint::CanvasHandled)
}
pub(crate) fn left_neighbour(mut self, left_neighbour: Option<u64>) -> Self {
diff --git a/src/canvas.rs b/src/canvas.rs
index 896418a5..31bff6a7 100644
--- a/src/canvas.rs
+++ b/src/canvas.rs
@@ -1,7 +1,7 @@
use std::str::FromStr;
use canvas_styling::*;
-use itertools::izip;
+use hashbrown::HashMap;
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
@@ -13,7 +13,7 @@ use tui::{
use crate::{
app::{
self,
- layout_manager::{BottomColRow, BottomLayout, BottomWidgetType, ElementSizing},
+ layout_manager::{BottomColRow, BottomLayout, BottomWidget, BottomWidgetType, Node},
App,
},
constants::*,
@@ -63,22 +63,23 @@ pub struct Painter {
height: u16,
width: u16,
styled_help_text: Vec<Line<'static>>,
- is_mac_os: bool, // TODO: This feels out of place...
-
- // TODO: Redo this entire thing.
- row_constraints: Vec<LayoutConstraint>,
- col_constraints: Vec<Vec<LayoutConstraint>>,
- col_row_constraints: Vec<Vec<Vec<LayoutConstraint>>>,
- layout_constraints: Vec<Vec<Vec<Vec<LayoutConstraint>>>>,
- derived_widget_draw_locs: Vec<Vec<Vec<Vec<Rect>>>>,
+
+ derived_widget_draw_locs: HashMap<usize, Rect>,
widget_layout: BottomLayout,
}
// Part of a temporary fix for https://github.com/ClementTsang/bottom/issues/896
-enum LayoutConstraint {
+#[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 },
+
+ /// Denotes that the canvas should let this element grow to take up whatever remaining space is left after
+ /// sizing the other sibling elements.
+ FlexGrow,
+
+ /// Denotes that the canvas can do whatever it likes to determine spacing for the element.
CanvasHandled,
- Grow,
- Ratio(u32, u32),
}
impl Painter {
@@ -87,77 +88,11 @@ impl Painter {
// We want to do this ONCE and reuse; after this we can just construct
// based on the console size.
- let mut row_constraints = Vec::new();
- let mut col_constraints = Vec::new();
- let mut col_row_constraints = Vec::new();
- let mut layout_constraints = Vec::new();
-
- widget_layout.rows.iter().for_each(|row| {
- if row.canvas_handle_height {
- row_constraints.push(LayoutConstraint::CanvasHandled);
- } else {
- row_constraints.push(LayoutConstraint::Ratio(
- row.row_height_ratio,
- widget_layout.total_row_height_ratio,
- ));
- }
-
- let mut new_col_constraints = Vec::new();
- let mut new_widget_constraints = Vec::new();
- let mut new_col_row_constraints = Vec::new();
- row.children.iter().for_each(|col| {
- if col.canvas_handle_width {
- new_col_constraints.push(LayoutConstraint::CanvasHandled);
- } else {
- new_col_constraints.push(LayoutConstraint::Ratio(
- col.col_width_ratio,
- row.total_col_ratio,
- ));
- }
-
- let mut new_new_col_row_constraints = Vec::new();
- let mut new_new_widget_constraints = Vec::new();
- col.children.iter().for_each(|col_row| {
- if col_row.canvas_handle_height {
- new_new_col_row_constraints.push(LayoutConstraint::CanvasHandled);
- } else if col_row.flex_grow {
- new_new_col_row_constraints.push(LayoutConstraint::Grow);
- } else {
- new_new_col_row_constraints.push(LayoutConstraint::Ratio(
- col_row.col_row_height_ratio,
- col.total_col_row_ratio,
- ));
- }
-
- let mut new_new_new_widget_constraints = Vec::new();
- col_row.children.iter().for_each(|widget| {
- new_new_new_widget_constraints.push(match widget.sizing {
- ElementSizing::Ratio { lhs, rhs } => LayoutConstraint::Ratio(lhs, rhs),
- ElementSizing::FlexGrow => LayoutConstraint::Grow,
- ElementSizing::CanvasHandled => LayoutConstraint::CanvasHandled,
- ElementSizing::Fill => LayoutConstraint::Ratio(1, 1),
- });
- });
- new_new_widget_constraints.push(new_new_new_widget_constraints);
- });
- new_col_row_constraints.push(new_new_col_row_constraints);
- new_widget_constraints.push(new_new_widget_constraints);
- });
- col_row_constraints.push(new_col_row_constraints);
- layout_constraints.push(new_widget_constraints);
- col_constraints.push(new_col_constraints);
- });
-
let mut painter = Painter {
colours: styling,
height: 0,
width: 0,
styled_help_text: Vec::default(),
- is_mac_os: cfg!(target_os = "macos"),
- row_constraints,
- col_constraints,
- col_row_constraints,
- layout_constraints,
widget_layout,
derived_widget_draw_locs: Vec::default(),
};
@@ -525,8 +460,8 @@ impl Painter {
}
if self.derived_widget_draw_locs.is_empty() || app_state.is_force_redraw {
- fn get_constraints(
- direction: Direction, constraints: &[LayoutConstraint], area: Rect,
+ fn get_rects<I: ExactSizeIterator<Item = LayoutConstraint>>(
+ direction: Direction, constraints: I, area: Rect,
) -> Vec<Rect> {
// Order of operations:
// - Ratios first + canvas-handled (which is just zero)
@@ -558,21 +493,20 @@ impl Painter {
let mut num_non_ch = 0;
for (itx, (constraint, size)) in
- constraints.iter().zip(sizes.iter_mut()).enumerate()
+ constraints.zip(sizes.iter_mut()).enumerate()
{
match constraint {
- LayoutConstraint::Ratio(a, b) => {
+ LayoutConstraint::Ratio { lhs, rhs } => {
match direction {
Direction::Horizontal => {
- let amount =
- (((area.width as u32) * (*a)) / (*b)) as u16;
+ let amount = (((area.width as u32) * lhs) / rhs) as u16;
bounds.shrink_width(amount);
size.width = amount;
size.height = area.height;
}
Direction::Vertical => {
let amount =
- (((area.height as u32) * (*a)) / (*b)) as u16;
+ (((area.height as u32) * lhs) / rhs) as u16;
bounds.shrink_height(amount);
size.width = area.width;
size.height = amount;
@@ -580,7 +514,7 @@ impl Painter {
}
num_non_ch += 1;
}
- LayoutConstraint::Grow => {
+ LayoutConstraint::FlexGrow => {
// Mark it as grow in the vector and handle in second pass.
grow.push(itx);
num_non_ch += 1;
@@ -624,8 +558,8 @@ impl Painter {
for (size, constraint) in sizes.iter_mut().zip(constraints) {
match constraint {
LayoutConstraint::CanvasHandled => {}
- LayoutConstraint::Grow
- | LayoutConstraint::Ratio(_, _) => {
+ LayoutConstraint::FlexGrow
+ | LayoutConstraint::Ratio { .. } => {
if remaining_width > 0 {
size.width += per_item + 1;
remaining_width -= 1;
@@ -642,8 +576,8 @@ impl Painter {
for (size, constraint) in sizes.iter_mut().zip(constraints) {
match constraint {
LayoutConstraint::CanvasHandled => {}
- LayoutConstraint::Grow
- | LayoutConstraint::Ratio(_, _) => {
+ LayoutConstraint::FlexGrow
+ | LayoutConstraint::Ratio { .. } => {
if remaining_height > 0 {
size.height += per_item + 1;
remaining_height -= 1;
@@ -677,79 +611,53 @@ impl Painter {
.collect()
}
- let draw_locs =
- get_constraints(Direction::Vertical, &self.row_constraints, terminal_size);
-
- self.derived_widget_draw_locs = izip!(
- draw_locs,
- &self.col_constraints,
- &self.col_row_constraints,
- &self.layout_constraints,
- &self.widget_layout.rows
- )
- .map(
- |(
- draw_loc,
- col_constraint,
- col_row_constraint,
- row_constraint_vec,
- cols,
- )| {
- izip!(
- get_constraints(Direction::Horizontal, col_constraint, draw_loc),
- col_row_constraint,
- row_constraint_vec,
- &cols.children
- )
- .map(|(split_loc, constraint, col_constraint_vec, col_rows)| {
- izip!(
- get_constraints(
- Direction::Vertical,
- constraint.as_slice(),
- split_loc
- ),
- col_constraint_vec,
- &col_rows.children
- )
- .map(|(draw_loc, col_row_constraint_vec, widgets)| {
- // Note that col_row_constraint_vec CONTAINS the widget constraints
- let widget_draw_locs = get_constraints(
- Direction::Horizontal,
- col_row_constraint_vec.as_slice(),
- draw_loc,
- );
-
- // Side effect, draw here.
- self.draw_widgets_with_constraints(
- f,
- app_state,
- widgets,
- &widget_draw_locs,
- );
-
- widget_draw_locs
- })
- .collect()
- })
- .collect()
- },
- )
- .collect();
+ // 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()
+ {
+ 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);
+ }
+ }
+ }
+ }
} else {
- self.widget_layout
- .rows
- .iter()
- .flat_map(|row| &row.children)
- .flat_map(|col| &col.children)
- .zip(self.derived_widget_draw_locs.iter().flatten().flatten())
- .for_each(|(widgets, widget_draw_locs)| {
- self.draw_widgets_with_constraints(
- f,
-