fully migrate over custom layout creation

This commit is contained in:
ClementTsang 2023-06-05 01:44:40 -04:00
parent a68a7346c3
commit 000ea7cce4
No known key found for this signature in database
GPG Key ID: DC3B7867D8D97095
5 changed files with 1006 additions and 968 deletions

View File

@ -5,7 +5,7 @@ use tui::layout::Direction;
use crate::canvas::LayoutConstraint; use crate::canvas::LayoutConstraint;
use crate::constants::DEFAULT_WIDGET_ID; use crate::constants::DEFAULT_WIDGET_ID;
use crate::error::{BottomError, Result}; use crate::error::{BottomError, Result};
use crate::options::layout_options::Row; use crate::options::layout_options::{Row, RowChildren};
use crate::utils::error; use crate::utils::error;
/// Represents a start and end coordinate in some dimension. /// 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 ColumnRowMappings = (u32, BTreeMap<LineSegment, WidgetMappings>);
type ColumnMappings = (u32, BTreeMap<LineSegment, ColumnRowMappings>); 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. /// A "container" that contains more [`Node`]s.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Container { pub(crate) struct Container {
/// The children elements. /// The children elements.
pub(crate) children: Vec<usize>, pub(crate) children: Vec<NodeId>,
/// How the container should be sized. /// How the container should be sized.
pub(crate) constraint: LayoutConstraint, pub(crate) constraint: LayoutConstraint,
@ -47,7 +29,7 @@ pub(crate) struct Container {
} }
impl Container { impl Container {
pub(crate) fn row(children: Vec<usize>, sizing: LayoutConstraint) -> Self { pub(crate) fn row(children: Vec<NodeId>, sizing: LayoutConstraint) -> Self {
Self { Self {
children, children,
constraint: sizing, 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 { Self {
children, children,
constraint, 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 /// Represents a more usable representation of the layout, derived from the
/// config. /// config.
/// ///
/// Internally represented by an arena-backed tree. /// Internally represented by an arena-backed tree.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct BottomLayout { pub struct BottomLayout {
arena: Vec<Node>, containers: Vec<Container>,
widgets: Vec<BottomWidget>,
} }
impl BottomLayout { impl BottomLayout {
/// Add a node to the layout arena. The ID is returned. /// Add a container to the layout arena. The ID is returned.
pub fn add_node(&mut self, node: Node) -> usize { pub fn add_container(&mut self, container: Container) -> NodeId {
let id = self.arena.len(); let id = self.containers.len();
self.arena.push(node); self.containers.push(container);
id NodeId::Container(id)
}
/// Add a node to the layout arena. The ID is returned.
pub fn add_widget(&mut self, widget: BottomWidget) -> NodeId {
let id = self.widgets.len();
self.widgets.push(widget);
NodeId::Widget(id)
} }
/// Get the node with the corresponding ID. /// Get the node with the corresponding ID.
pub fn get_node(&self, id: usize) -> Option<&Node> { pub fn get_container(&self, id: usize) -> Option<&Container> {
self.arena.get(id) self.containers.get(id)
} }
/// Returns the number of elements in the layout. /// Get the node with the corresponding ID.
pub fn get_widget(&self, id: usize) -> Option<&BottomWidget> {
self.widgets.get(id)
}
/// 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 { pub fn len(&self) -> usize {
self.arena.len() self.widgets.len() + self.containers.len()
} }
/// Creates a new [`BottomLayout`] given a slice of [`Row`]s. /// Returns the number of widgets in the layout.
pub fn from_rows(rows: &[Row]) -> error::Result<Self> { pub fn widgets_len(&self) -> usize {
let mut num_widgets = 0; 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.
if num_widgets > 0 { let outer_col_total_ratio = rows.iter().map(|row| row.ratio.unwrap_or(1)).sum();
todo!() 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;
}
}
}
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 { } else {
Err(error::BottomError::ConfigError( Err(error::BottomError::ConfigError(
"please have at least one widget under the '[[row]]' section.".to_string(), "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. /// Creates a new [`BottomLayout`] following the basic layout.
pub fn new_basic(use_battery: bool) -> Self { pub fn new_basic(use_battery: bool) -> Self {
let mut layout = BottomLayout::default();
let table_widgets = if use_battery { let table_widgets = if use_battery {
let disk_widget = BottomWidget::new_handled(BottomWidgetType::Disk, 4) let disk = layout.add_widget(
BottomWidget::new_handled(BottomWidgetType::Disk, 4)
.up_neighbour(Some(100)) .up_neighbour(Some(100))
.left_neighbour(Some(8)) .left_neighbour(Some(8))
.right_neighbour(Some(DEFAULT_WIDGET_ID + 2)); .right_neighbour(Some(DEFAULT_WIDGET_ID + 2)),
);
let proc_sort = let proc = {
let proc_sort = layout.add_widget(
BottomWidget::new_handled(BottomWidgetType::ProcSort, DEFAULT_WIDGET_ID + 2) BottomWidget::new_handled(BottomWidgetType::ProcSort, DEFAULT_WIDGET_ID + 2)
.up_neighbour(Some(100)) .up_neighbour(Some(100))
.down_neighbour(Some(DEFAULT_WIDGET_ID + 1)) .down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
.left_neighbour(Some(4)) .left_neighbour(Some(4))
.right_neighbour(Some(DEFAULT_WIDGET_ID)) .right_neighbour(Some(DEFAULT_WIDGET_ID))
.parent_reflector(Some((WidgetDirection::Right, 2))); .parent_reflector(Some((WidgetDirection::Right, 2))),
);
let proc = BottomWidget::new_handled(BottomWidgetType::Proc, DEFAULT_WIDGET_ID) let main_proc = layout.add_widget(
BottomWidget::new_handled(BottomWidgetType::Proc, DEFAULT_WIDGET_ID)
.up_neighbour(Some(100)) .up_neighbour(Some(100))
.down_neighbour(Some(DEFAULT_WIDGET_ID + 1)) .down_neighbour(Some(DEFAULT_WIDGET_ID + 1))
.left_neighbour(Some(DEFAULT_WIDGET_ID + 2)) .left_neighbour(Some(DEFAULT_WIDGET_ID + 2))
.right_neighbour(Some(7)); .right_neighbour(Some(7)),
);
let proc_search = let proc_search = layout.add_widget(
BottomWidget::new_handled(BottomWidgetType::ProcSearch, DEFAULT_WIDGET_ID + 1) BottomWidget::new_handled(BottomWidgetType::ProcSearch, DEFAULT_WIDGET_ID + 1)
.up_neighbour(Some(DEFAULT_WIDGET_ID)) .up_neighbour(Some(DEFAULT_WIDGET_ID))
.left_neighbour(Some(4)) .left_neighbour(Some(4))
.right_neighbour(Some(7)) .right_neighbour(Some(7))
.parent_reflector(Some((WidgetDirection::Up, 1))); .parent_reflector(Some((WidgetDirection::Up, 1))),
);
let temp = BottomWidget::new_handled(BottomWidgetType::Temp, 7) let top = layout.add_container(Container::row(
.up_neighbour(Some(100)) vec![proc_sort, main_proc],
.left_neighbour(Some(DEFAULT_WIDGET_ID)) LayoutConstraint::CanvasHandled,
.right_neighbour(Some(8)); ));
layout.add_container(Container::col(
let battery = BottomWidget::new_handled(BottomWidgetType::Battery, 8) vec![top, proc_search],
.up_neighbour(Some(100)) LayoutConstraint::CanvasHandled,
.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 temp = layout.add_widget(
BottomWidget::new_handled(BottomWidgetType::Temp, 7)
.up_neighbour(Some(100))
.left_neighbour(Some(DEFAULT_WIDGET_ID))
.right_neighbour(Some(8)),
);
let mem = BottomWidget::new_handled(BottomWidgetType::BasicMem, 2) let battery = layout.add_widget(
BottomWidget::new_handled(BottomWidgetType::Battery, 8)
.up_neighbour(Some(100))
.left_neighbour(Some(7))
.right_neighbour(Some(4)),
);
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 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))
.left_neighbour(Some(DEFAULT_WIDGET_ID))
.right_neighbour(Some(4)),
);
layout.add_container(Container::row(
vec![disk, proc, temp],
LayoutConstraint::CanvasHandled,
))
};
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)) .up_neighbour(Some(1))
.down_neighbour(Some(100)) .down_neighbour(Some(100))
.right_neighbour(Some(3)); .right_neighbour(Some(3)),
);
let net = BottomWidget::new_handled(BottomWidgetType::BasicNet, 3) let net = layout.add_widget(
BottomWidget::new_handled(BottomWidgetType::BasicNet, 3)
.up_neighbour(Some(1)) .up_neighbour(Some(1))
.down_neighbour(Some(100)) .down_neighbour(Some(100))
.left_neighbour(Some(2)); .left_neighbour(Some(2)),
);
let table = let net = layout.add_widget(
BottomWidget::new_handled(BottomWidgetType::BasicTables, 100).up_neighbour(Some(2)); BottomWidget::new_handled(BottomWidgetType::BasicTables, 100).up_neighbour(Some(2)),
);
let mut layout = BottomLayout::default(); let middle_bars = layout.add_container(Container::row(
// TODO: Add nodes; should we instead back with a hashmap? vec![mem, net],
LayoutConstraint::CanvasHandled,
));
// BottomLayout { let table = layout.add_widget(
// total_row_height_ratio: 3, BottomWidget::new_handled(BottomWidgetType::BasicTables, 100).up_neighbour(Some(2)),
// rows: vec![ );
// BottomRow::new(vec![BottomCol::new(vec![
// BottomColRow::new(vec![cpu]).canvas_handle_height(true) layout.add_container(Container::col(
// ]) vec![cpu, middle_bars, table, table_widgets],
// .canvas_handle_width(true)]) LayoutConstraint::CanvasHandled,
// .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 layout
} }
@ -1000,7 +1250,7 @@ impl BottomWidget {
Self::new( Self::new(
widget_type, widget_type,
widget_id, 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 { pub enum BottomWidgetType {
#[default] #[default]
Empty, Empty,

View File

@ -13,7 +13,7 @@ use tui::{
use crate::{ use crate::{
app::{ app::{
self, self,
layout_manager::{BottomColRow, BottomLayout, BottomWidget, BottomWidgetType, Node}, layout_manager::{BottomColRow, BottomLayout, BottomWidget, BottomWidgetType, NodeId},
App, App,
}, },
constants::*, constants::*,
@ -72,7 +72,7 @@ pub struct Painter {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum LayoutConstraint { pub enum LayoutConstraint {
/// Denotes that the canvas should follow the given ratio of `lhs:rhs` to determine spacing for the element. /// 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 /// Denotes that the canvas should let this element grow to take up whatever remaining space is left after
/// sizing the other sibling elements. /// sizing the other sibling elements.
@ -94,7 +94,7 @@ impl Painter {
width: 0, width: 0,
styled_help_text: Vec::default(), styled_help_text: Vec::default(),
widget_layout, widget_layout,
derived_widget_draw_locs: Vec::default(), derived_widget_draw_locs: HashMap::default(),
}; };
painter.complete_painter_init(); painter.complete_painter_init();
@ -496,7 +496,7 @@ impl Painter {
constraints.zip(sizes.iter_mut()).enumerate() constraints.zip(sizes.iter_mut()).enumerate()
{ {
match constraint { match constraint {
LayoutConstraint::Ratio { lhs, rhs } => { LayoutConstraint::Ratio { a: lhs, b: rhs } => {
match direction { match direction {
Direction::Horizontal => { Direction::Horizontal => {
let amount = (((area.width as u32) * lhs) / rhs) as u16; let amount = (((area.width as u32) * lhs) / rhs) as u16;
@ -612,22 +612,33 @@ impl Painter {
} }
// Do a preorder traversal through the tree in, and calculate the draw [`Rect`]s for each widget. // 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); if let Some(root_id) = self.widget_layout.root_id() {
let mut queue = vec![(root_id, terminal_size)]; let mut queue = vec![(root_id, terminal_size)];
while let Some((current_id, rect)) = queue.pop() { while let Some((current_id, rect)) = queue.pop() {
if let Some(widget) = self.widget_layout.get_node(current_id) { match current_id {
match widget { NodeId::Container(current_id) => {
Node::Container(container) => { if let Some(container) =
self.widget_layout.get_container(current_id)
{
let constraints = container.children.iter().map(|child| { let constraints = container.children.iter().map(|child| {
if let Some(node) = self.widget_layout.get_node(*child) { match child {
node.constraint() NodeId::Container(child) => self
} else { .widget_layout
LayoutConstraint::FlexGrow .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 = let rects = get_rects(
get_rects(container.direction().into(), constraints, rect); container.direction().into(),
constraints,
rect,
);
// If it's a container, push in reverse order to the stack. // If it's a container, push in reverse order to the stack.
for child in container for child in container
@ -640,17 +651,21 @@ impl Painter {
queue.push(child); queue.push(child);
} }
} }
Node::Widget(widget) => { }
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. // 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); self.derived_widget_draw_locs.insert(current_id, rect);
} }
} }
} }
} }
}
} else { } else {
for (id, rect) in &self.derived_widget_draw_locs { for (id, rect) in &self.derived_widget_draw_locs {
match &self.widget_layout.get_node(*id) { match self.widget_layout.get_widget(*id) {
Some(Node::Widget(widget)) => { Some(widget) => {
self.draw_widget(f, app_state, widget, *rect); self.draw_widget(f, app_state, widget, *rect);
} }
_ => { _ => {

View File

@ -274,10 +274,8 @@ pub fn build_app(
is_command: is_default_command, is_command: is_default_command,
}; };
for row in &widget_layout.rows { // Determine the initial widget ID/type + initialize states.
for col in &row.children { for widget in widget_layout.widgets_iter() {
for col_row in &col.children {
for widget in &col_row.children {
widget_map.insert(widget.widget_id, widget.clone()); widget_map.insert(widget.widget_id, widget.clone());
if let Some(default_widget_type) = &default_widget_type_option { if let Some(default_widget_type) = &default_widget_type_option {
if !is_custom_layout || use_basic_mode { if !is_custom_layout || use_basic_mode {
@ -371,19 +369,15 @@ pub fn build_app(
); );
} }
Battery => { Battery => {
battery_state_map battery_state_map.insert(widget.widget_id, BatteryWidgetState::default());
.insert(widget.widget_id, BatteryWidgetState::default());
} }
_ => {} _ => {}
} }
} }
}
}
}
let basic_table_widget_state = if use_basic_mode { let basic_table_widget_state = if use_basic_mode {
Some(match initial_widget_type { Some(match initial_widget_type {
Proc | Disk | Temp => BasicTableWidgetState { Proc | Disk | Temp | Battery => BasicTableWidgetState {
currently_displayed_widget_type: initial_widget_type, currently_displayed_widget_type: initial_widget_type,
currently_displayed_widget_id: initial_widget_id, currently_displayed_widget_id: initial_widget_id,
widget_id: 100, widget_id: 100,
@ -464,14 +458,15 @@ pub fn get_widget_layout(
) -> error::Result<(BottomLayout, u64, Option<BottomWidgetType>)> { ) -> error::Result<(BottomLayout, u64, Option<BottomWidgetType>)> {
let left_legend = is_flag_enabled!(left_legend, matches, config); let left_legend = is_flag_enabled!(left_legend, matches, config);
let (default_widget_type, mut default_widget_count) = let (default_widget_type, default_widget_count) =
get_default_widget_and_count(matches, config)?; get_default_widget_and_count(matches, config)?;
let mut default_widget_id = 1;
let bottom_layout = if is_flag_enabled!(basic, matches, config) { if is_flag_enabled!(basic, matches, config) {
default_widget_id = DEFAULT_WIDGET_ID; Ok((
BottomLayout::new_basic(get_use_battery(matches, config)),
BottomLayout::new_basic(get_use_battery(matches, config)) DEFAULT_WIDGET_ID,
default_widget_type,
))
} else { } else {
let ref_row: Vec<Row>; // Required to handle reference let ref_row: Vec<Row>; // Required to handle reference
let rows = match &config.row { let rows = match &config.row {
@ -489,30 +484,11 @@ pub fn get_widget_layout(
} }
}; };
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs* let (layout, default_widget_id) =
let mut total_height_ratio = 0; BottomLayout::from_rows(rows, default_widget_type, default_widget_count, left_legend)?;
// let mut ret_bottom_layout = BottomLayout { Ok((layout, default_widget_id, default_widget_type))
// arena: todo!(), }
// arena: rows
// .iter()
// .map(|row| {
// row.convert_row_to_bottom_row(
// &mut iter_id,
// &mut total_height_ratio,
// &mut default_widget_id,
// &default_widget_type,
// &mut default_widget_count,
// left_legend,
// )
// })
// .collect::<error::Result<Vec<_>>>()?,
// };
BottomLayout::from_rows(rows)?
};
Ok((bottom_layout, default_widget_id, default_widget_type))
} }
fn get_update_rate_in_milliseconds(matches: &ArgMatches, config: &Config) -> error::Result<u64> { fn get_update_rate_in_milliseconds(matches: &ArgMatches, config: &Config) -> error::Result<u64> {

View File

@ -1,8 +1,5 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::app::layout_manager::*;
use crate::error::Result;
/// Represents a row. This has a length of some sort (optional) and a vector /// Represents a row. This has a length of some sort (optional) and a vector
/// of children. /// of children.
#[derive(Clone, Deserialize, Debug, Serialize)] #[derive(Clone, Deserialize, Debug, Serialize)]
@ -12,206 +9,6 @@ pub struct Row {
pub child: Option<Vec<RowChildren>>, pub child: Option<Vec<RowChildren>>,
} }
fn new_cpu(left_legend: bool, iter_id: &mut u64) -> BottomColRow {
let cpu_id = *iter_id;
*iter_id += 1;
let legend_id = *iter_id;
if left_legend {
BottomColRow::new(vec![
BottomWidget::new_handled(BottomWidgetType::CpuLegend, legend_id)
.parent_reflector(Some((WidgetDirection::Right, 1))),
BottomWidget::new_handled(BottomWidgetType::Cpu, cpu_id),
])
} else {
BottomColRow::new(vec![
BottomWidget::new_handled(BottomWidgetType::Cpu, cpu_id),
BottomWidget::new_handled(BottomWidgetType::CpuLegend, legend_id)
.parent_reflector(Some((WidgetDirection::Left, 1))),
])
}
}
fn new_proc_sort(sort_id: u64) -> BottomWidget {
BottomWidget::new_handled(BottomWidgetType::ProcSort, sort_id)
.parent_reflector(Some((WidgetDirection::Right, 2)))
}
fn new_proc(proc_id: u64) -> BottomWidget {
BottomWidget::new_fill(BottomWidgetType::Proc, proc_id)
}
fn new_proc_search(search_id: u64) -> BottomWidget {
BottomWidget::new_fill(BottomWidgetType::ProcSearch, search_id)
.parent_reflector(Some((WidgetDirection::Up, 1)))
}
impl Row {
pub fn convert_row_to_bottom_row(
&self, iter_id: &mut u64, total_height_ratio: &mut u32, default_widget_id: &mut u64,
default_widget_type: &Option<BottomWidgetType>, default_widget_count: &mut u64,
left_legend: bool,
) -> Result<BottomRow> {
// TODO: In the future we want to also add percentages.
// But for MVP, we aren't going to bother.
let row_ratio = self.ratio.unwrap_or(1);
let mut children = Vec::new();
*total_height_ratio += row_ratio;
let mut total_col_ratio = 0;
if let Some(row_children) = &self.child {
for row_child in row_children {
match row_child {
RowChildren::Widget(widget) => {
*iter_id += 1;
let width_ratio = widget.ratio.unwrap_or(1);
total_col_ratio += width_ratio;
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;
}
}
}
children.push(match widget_type {
BottomWidgetType::Cpu => {
BottomCol::new(vec![new_cpu(left_legend, iter_id)])
.col_width_ratio(width_ratio)
}
BottomWidgetType::Proc => {
let proc_id = *iter_id;
let proc_search_id = *iter_id + 1;
*iter_id += 2;
BottomCol::new(vec![
BottomColRow::new(vec![
new_proc_sort(*iter_id),
new_proc(proc_id),
])
.total_widget_ratio(3)
.flex_grow(true),
BottomColRow::new(vec![new_proc_search(proc_search_id)])
.canvas_handle_height(true),
])
.total_col_row_ratio(2)
.col_width_ratio(width_ratio)
}
_ => BottomCol::new(vec![BottomColRow::new(vec![
BottomWidget::new_fill(widget_type, *iter_id),
])])
.col_width_ratio(width_ratio),
});
}
RowChildren::Col { ratio, child } => {
let col_width_ratio = ratio.unwrap_or(1);
total_col_ratio += col_width_ratio;
let mut total_col_row_ratio = 0;
let mut contains_proc = false;
let mut col_row_children: Vec<BottomColRow> = Vec::new();
for widget in child {
let widget_type = widget.widget_type.parse::<BottomWidgetType>()?;
*iter_id += 1;
let col_row_height_ratio = widget.ratio.unwrap_or(1);
total_col_row_ratio += col_row_height_ratio;
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;
}
}
}
match widget_type {
BottomWidgetType::Cpu => {
col_row_children.push(
new_cpu(left_legend, iter_id)
.col_row_height_ratio(col_row_height_ratio),
);
}
BottomWidgetType::Proc => {
contains_proc = true;
let proc_id = *iter_id;
let proc_search_id = *iter_id + 1;
*iter_id += 2;
col_row_children.push(
BottomColRow::new(vec![
new_proc_sort(*iter_id),
new_proc(proc_id),
])
.col_row_height_ratio(col_row_height_ratio)
.total_widget_ratio(3),
);
col_row_children.push(
BottomColRow::new(vec![new_proc_search(proc_search_id)])
.canvas_handle_height(true)
.col_row_height_ratio(col_row_height_ratio),
);
}
_ => col_row_children.push(
BottomColRow::new(vec![BottomWidget::new_fill(
widget_type,
*iter_id,
)])
.col_row_height_ratio(col_row_height_ratio),
),
}
}
if contains_proc {
// Must adjust ratios to work with proc
total_col_row_ratio *= 2;
for child in &mut col_row_children {
// Multiply all non-proc or proc-search ratios by 2
if !child.children.is_empty() {
match child.children[0].widget_type {
BottomWidgetType::ProcSearch => {}
_ => child.col_row_height_ratio *= 2,
}
}
}
}
children.push(
BottomCol::new(col_row_children)
.total_col_row_ratio(total_col_row_ratio)
.col_width_ratio(col_width_ratio),
);
}
}
}
}
Ok(BottomRow::new(children)
.total_col_ratio(total_col_ratio)
.row_height_ratio(row_ratio))
}
}
/// Represents a child of a Row - either a Col (column) or a FinalWidget. /// Represents a child of a Row - either a Col (column) or a FinalWidget.
/// ///
/// A Col can also have an optional length and children. We only allow columns /// A Col can also have an optional length and children. We only allow columns

View File

@ -1,469 +1,469 @@
//! Mocks layout management, so we can check if we broke anything. // //! Mocks layout management, so we can check if we broke anything.
use bottom::app::layout_manager::{BottomLayout, BottomWidgetType}; // use bottom::app::layout_manager::{BottomLayout, BottomWidgetType};
#[cfg(feature = "battery")] // #[cfg(feature = "battery")]
use bottom::constants::DEFAULT_BATTERY_LAYOUT; // use bottom::constants::DEFAULT_BATTERY_LAYOUT;
use bottom::constants::{DEFAULT_LAYOUT, DEFAULT_WIDGET_ID}; // use bottom::constants::{DEFAULT_LAYOUT, DEFAULT_WIDGET_ID};
use bottom::options::{layout_options::Row, Config}; // use bottom::options::{layout_options::Row, Config};
use bottom::utils::error; // use bottom::utils::error;
use toml_edit::de::from_str; // use toml_edit::de::from_str;
// FIXME: Move these into the library! // // FIXME: Move these tests into the library!
const PROC_LAYOUT: &str = r##" // const PROC_LAYOUT: &str = r##"
[[row]] // [[row]]
[[row.child]] // [[row.child]]
type="proc" // type="proc"
[[row]] // [[row]]
[[row.child]] // [[row.child]]
type="proc" // type="proc"
[[row.child]] // [[row.child]]
type="proc" // type="proc"
[[row]] // [[row]]
[[row.child]] // [[row.child]]
type="proc" // type="proc"
[[row.child]] // [[row.child]]
type="proc" // type="proc"
"##; // "##;
fn test_create_layout( // fn test_create_layout(
rows: &[Row], default_widget_id: u64, default_widget_type: Option<BottomWidgetType>, // rows: &[Row], default_widget_id: u64, default_widget_type: Option<BottomWidgetType>,
default_widget_count: u64, left_legend: bool, // default_widget_count: u64, left_legend: bool,
) -> BottomLayout { // ) -> BottomLayout {
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs* // let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
let mut total_height_ratio = 0; // let mut total_height_ratio = 0;
let mut default_widget_count = default_widget_count; // let mut default_widget_count = default_widget_count;
let mut default_widget_id = default_widget_id; // let mut default_widget_id = default_widget_id;
let mut ret_bottom_layout = BottomLayout { // let mut ret_bottom_layout = BottomLayout {
rows: rows // rows: rows
.iter() // .iter()
.map(|row| { // .map(|row| {
row.convert_row_to_bottom_row( // row.convert_row_to_bottom_row(
&mut iter_id, // &mut iter_id,
&mut total_height_ratio, // &mut total_height_ratio,
&mut default_widget_id, // &mut default_widget_id,
&default_widget_type, // &default_widget_type,
&mut default_widget_count, // &mut default_widget_count,
left_legend, // left_legend,
) // )
}) // })
.collect::<error::Result<Vec<_>>>() // .collect::<error::Result<Vec<_>>>()
.unwrap(), // .unwrap(),
total_row_height_ratio: total_height_ratio, // total_row_height_ratio: total_height_ratio,
}; // };
ret_bottom_layout.get_movement_mappings(); // ret_bottom_layout.get_movement_mappings();
ret_bottom_layout // ret_bottom_layout
} // }
#[test] // #[test]
/// Tests the default setup. // /// Tests the default setup.
fn test_default_movement() { // fn test_default_movement() {
let rows = from_str::<Config>(DEFAULT_LAYOUT).unwrap().row.unwrap(); // let rows = from_str::<Config>(DEFAULT_LAYOUT).unwrap().row.unwrap();
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, false); // let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, false);
// Simple tests for the top CPU widget // // Simple tests for the top CPU widget
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].down_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].down_neighbour,
Some(3) // Some(3)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].right_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].right_neighbour,
Some(2) // Some(2)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].left_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].left_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].up_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].up_neighbour,
None // None
); // );
// Test CPU legend // // Test CPU legend
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].down_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].down_neighbour,
Some(4) // Some(4)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].right_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].right_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].left_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].left_neighbour,
Some(1) // Some(1)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].up_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].up_neighbour,
None // None
); // );
// Test memory->temp, temp->disk, disk->memory mappings // // Test memory->temp, temp->disk, disk->memory mappings
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[0].children[0].right_neighbour, // ret_bottom_layout.rows[1].children[0].children[0].children[0].right_neighbour,
Some(4) // Some(4)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[1].children[0].children[0].down_neighbour, // ret_bottom_layout.rows[1].children[1].children[0].children[0].down_neighbour,
Some(5) // Some(5)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[1].children[1].children[0].left_neighbour, // ret_bottom_layout.rows[1].children[1].children[1].children[0].left_neighbour,
Some(3) // Some(3)
); // );
// Test disk -> processes, processes -> process sort, process sort -> network // // Test disk -> processes, processes -> process sort, process sort -> network
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[1].children[1].children[0].down_neighbour, // ret_bottom_layout.rows[1].children[1].children[1].children[0].down_neighbour,
Some(7) // Some(7)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[0].children[1].left_neighbour, // ret_bottom_layout.rows[2].children[1].children[0].children[1].left_neighbour,
Some(9) // Some(9)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[0].children[0].left_neighbour, // ret_bottom_layout.rows[2].children[1].children[0].children[0].left_neighbour,
Some(6) // Some(6)
); // );
} // }
#[cfg(feature = "battery")] // #[cfg(feature = "battery")]
#[test] // #[test]
/// Tests battery movement in the default setup. // /// Tests battery movement in the default setup.
fn test_default_battery_movement() { // fn test_default_battery_movement() {
let rows = from_str::<Config>(DEFAULT_BATTERY_LAYOUT) // let rows = from_str::<Config>(DEFAULT_BATTERY_LAYOUT)
.unwrap() // .unwrap()
.row // .row
.unwrap(); // .unwrap();
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, false); // let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, false);
// Simple tests for the top CPU widget // // Simple tests for the top CPU widget
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].down_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].down_neighbour,
Some(4) // Some(4)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].right_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].right_neighbour,
Some(2) // Some(2)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].left_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].left_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].up_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].up_neighbour,
None // None
); // );
// Test CPU legend // // Test CPU legend
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].down_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].down_neighbour,
Some(5) // Some(5)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].right_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].right_neighbour,
Some(3) // Some(3)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].left_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].left_neighbour,
Some(1) // Some(1)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].up_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].up_neighbour,
None // None
); // );
} // }
#[test] // #[test]
/// Tests using left_legend. // /// Tests using left_legend.
fn test_left_legend() { // fn test_left_legend() {
let rows = from_str::<Config>(DEFAULT_LAYOUT).unwrap().row.unwrap(); // let rows = from_str::<Config>(DEFAULT_LAYOUT).unwrap().row.unwrap();
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, true); // let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, true);
// Legend // // Legend
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].down_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].down_neighbour,
Some(3) // Some(3)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].right_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].right_neighbour,
Some(1) // Some(1)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].left_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].left_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].up_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].up_neighbour,
None // None
); // );
// Widget // // Widget
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].down_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].down_neighbour,
Some(3) // Some(3)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].right_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].right_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].left_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].left_neighbour,
Some(2) // Some(2)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].up_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].up_neighbour,
None // None
); // );
} // }
#[test] // #[test]
/// Tests explicit default widget. // /// Tests explicit default widget.
fn test_default_widget_in_layout() { // fn test_default_widget_in_layout() {
let proc_layout = r##" // let proc_layout = r##"
[[row]] // [[row]]
[[row.child]] // [[row.child]]
type="proc" // type="proc"
[[row]] // [[row]]
[[row.child]] // [[row.child]]
type="proc" // type="proc"
[[row.child]] // [[row.child]]
type="proc" // type="proc"
[[row]] // [[row]]
[[row.child]] // [[row.child]]
type="proc" // type="proc"
default=true // default=true
[[row.child]] // [[row.child]]
type="proc" // type="proc"
"##; // "##;
let rows = from_str::<Config>(proc_layout).unwrap().row.unwrap(); // let rows = from_str::<Config>(proc_layout).unwrap().row.unwrap();
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs* // let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
let mut total_height_ratio = 0; // let mut total_height_ratio = 0;
let mut default_widget_count = 1; // let mut default_widget_count = 1;
let mut default_widget_id = DEFAULT_WIDGET_ID; // let mut default_widget_id = DEFAULT_WIDGET_ID;
let default_widget_type = None; // let default_widget_type = None;
let left_legend = false; // let left_legend = false;
let mut ret_bottom_layout = BottomLayout { // let mut ret_bottom_layout = BottomLayout {
rows: rows // rows: rows
.iter() // .iter()
.map(|row| { // .map(|row| {
row.convert_row_to_bottom_row( // row.convert_row_to_bottom_row(
&mut iter_id, // &mut iter_id,
&mut total_height_ratio, // &mut total_height_ratio,
&mut default_widget_id, // &mut default_widget_id,
&default_widget_type, // &default_widget_type,
&mut default_widget_count, // &mut default_widget_count,
left_legend, // left_legend,
) // )
}) // })
.collect::<error::Result<Vec<_>>>() // .collect::<error::Result<Vec<_>>>()
.unwrap(), // .unwrap(),
total_row_height_ratio: total_height_ratio, // total_row_height_ratio: total_height_ratio,
}; // };
ret_bottom_layout.get_movement_mappings(); // ret_bottom_layout.get_movement_mappings();
assert_eq!(default_widget_id, 10); // assert_eq!(default_widget_id, 10);
} // }
#[test] // #[test]
/// Tests default widget by setting type and count. // /// Tests default widget by setting type and count.
fn test_default_widget_by_option() { // fn test_default_widget_by_option() {
let rows = from_str::<Config>(PROC_LAYOUT).unwrap().row.unwrap(); // let rows = from_str::<Config>(PROC_LAYOUT).unwrap().row.unwrap();
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs* // let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
let mut total_height_ratio = 0; // let mut total_height_ratio = 0;
let mut default_widget_count = 3; // let mut default_widget_count = 3;
let mut default_widget_id = DEFAULT_WIDGET_ID; // let mut default_widget_id = DEFAULT_WIDGET_ID;
let default_widget_type = Some(BottomWidgetType::Proc); // let default_widget_type = Some(BottomWidgetType::Proc);
let left_legend = false; // let left_legend = false;
let mut ret_bottom_layout = BottomLayout { // let mut ret_bottom_layout = BottomLayout {
rows: rows // rows: rows
.iter() // .iter()
.map(|row| { // .map(|row| {
row.convert_row_to_bottom_row( // row.convert_row_to_bottom_row(
&mut iter_id, // &mut iter_id,
&mut total_height_ratio, // &mut total_height_ratio,
&mut default_widget_id, // &mut default_widget_id,
&default_widget_type, // &default_widget_type,
&mut default_widget_count, // &mut default_widget_count,
left_legend, // left_legend,
) // )
}) // })
.collect::<error::Result<Vec<_>>>() // .collect::<error::Result<Vec<_>>>()
.unwrap(), // .unwrap(),
total_row_height_ratio: total_height_ratio, // total_row_height_ratio: total_height_ratio,
}; // };
ret_bottom_layout.get_movement_mappings(); // ret_bottom_layout.get_movement_mappings();
assert_eq!(default_widget_id, 7); // assert_eq!(default_widget_id, 7);
} // }
#[test] // #[test]
fn test_proc_custom_layout() { // fn test_proc_custom_layout() {
let rows = from_str::<Config>(PROC_LAYOUT).unwrap().row.unwrap(); // let rows = from_str::<Config>(PROC_LAYOUT).unwrap().row.unwrap();
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, false); // let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, false);
// First proc widget // // First proc widget
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].down_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].down_neighbour,
Some(2) // Some(2)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].left_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].left_neighbour,
Some(3) // Some(3)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].right_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].right_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[1].up_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[1].up_neighbour,
None // None
); // );
// Its search // // Its search
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[1].children[0].down_neighbour, // ret_bottom_layout.rows[0].children[0].children[1].children[0].down_neighbour,
Some(4) // Some(4)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[1].children[0].left_neighbour, // ret_bottom_layout.rows[0].children[0].children[1].children[0].left_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[1].children[0].right_neighbour, // ret_bottom_layout.rows[0].children[0].children[1].children[0].right_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[1].children[0].up_neighbour, // ret_bottom_layout.rows[0].children[0].children[1].children[0].up_neighbour,
Some(1) // Some(1)
); // );
// Its sort // // Its sort
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].down_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].down_neighbour,
Some(2) // Some(2)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].left_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].left_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].right_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].right_neighbour,
Some(1) // Some(1)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[0].children[0].children[0].children[0].up_neighbour, // ret_bottom_layout.rows[0].children[0].children[0].children[0].up_neighbour,
None // None
); // );
// Let us now test the second row's first widget... // // Let us now test the second row's first widget...
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[0].children[1].down_neighbour, // ret_bottom_layout.rows[1].children[0].children[0].children[1].down_neighbour,
Some(5) // Some(5)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[0].children[1].left_neighbour, // ret_bottom_layout.rows[1].children[0].children[0].children[1].left_neighbour,
Some(6) // Some(6)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[0].children[1].right_neighbour, // ret_bottom_layout.rows[1].children[0].children[0].children[1].right_neighbour,
Some(9) // Some(9)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[0].children[1].up_neighbour, // ret_bottom_layout.rows[1].children[0].children[0].children[1].up_neighbour,
Some(2) // Some(2)
); // );
// Sort // // Sort
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[0].children[0].down_neighbour, // ret_bottom_layout.rows[1].children[0].children[0].children[0].down_neighbour,
Some(5) // Some(5)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[0].children[0].left_neighbour, // ret_bottom_layout.rows[1].children[0].children[0].children[0].left_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[0].children[0].right_neighbour, // ret_bottom_layout.rows[1].children[0].children[0].children[0].right_neighbour,
Some(4) // Some(4)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[0].children[0].up_neighbour, // ret_bottom_layout.rows[1].children[0].children[0].children[0].up_neighbour,
Some(2) // Some(2)
); // );
// Search // // Search
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[1].children[0].down_neighbour, // ret_bottom_layout.rows[1].children[0].children[1].children[0].down_neighbour,
Some(10) // Some(10)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[1].children[0].left_neighbour, // ret_bottom_layout.rows[1].children[0].children[1].children[0].left_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[1].children[0].right_neighbour, // ret_bottom_layout.rows[1].children[0].children[1].children[0].right_neighbour,
Some(8) // Some(8)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[1].children[0].children[1].children[0].up_neighbour, // ret_bottom_layout.rows[1].children[0].children[1].children[0].up_neighbour,
Some(4) // Some(4)
); // );
// Third row, second // // Third row, second
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[0].children[1].down_neighbour, // ret_bottom_layout.rows[2].children[1].children[0].children[1].down_neighbour,
Some(14) // Some(14)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[0].children[1].left_neighbour, // ret_bottom_layout.rows[2].children[1].children[0].children[1].left_neighbour,
Some(15) // Some(15)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[0].children[1].right_neighbour, // ret_bottom_layout.rows[2].children[1].children[0].children[1].right_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[0].children[1].up_neighbour, // ret_bottom_layout.rows[2].children[1].children[0].children[1].up_neighbour,
Some(8) // Some(8)
); // );
// Sort // // Sort
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[0].children[0].down_neighbour, // ret_bottom_layout.rows[2].children[1].children[0].children[0].down_neighbour,
Some(14) // Some(14)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[0].children[0].left_neighbour, // ret_bottom_layout.rows[2].children[1].children[0].children[0].left_neighbour,
Some(10) // Some(10)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[0].children[0].right_neighbour, // ret_bottom_layout.rows[2].children[1].children[0].children[0].right_neighbour,
Some(13) // Some(13)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[0].children[0].up_neighbour, // ret_bottom_layout.rows[2].children[1].children[0].children[0].up_neighbour,
Some(8) // Some(8)
); // );
// Search // // Search
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[1].children[0].down_neighbour, // ret_bottom_layout.rows[2].children[1].children[1].children[0].down_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[1].children[0].left_neighbour, // ret_bottom_layout.rows[2].children[1].children[1].children[0].left_neighbour,
Some(11) // Some(11)
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[1].children[0].right_neighbour, // ret_bottom_layout.rows[2].children[1].children[1].children[0].right_neighbour,
None // None
); // );
assert_eq!( // assert_eq!(
ret_bottom_layout.rows[2].children[1].children[1].children[0].up_neighbour, // ret_bottom_layout.rows[2].children[1].children[1].children[0].up_neighbour,
Some(13) // Some(13)
); // );
} // }