mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-04-08 17:05:59 +02:00
remove code dedup
This commit is contained in:
parent
6555d2d751
commit
a68a7346c3
@ -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 {
|
||||
|
247
src/canvas.rs
247
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);
|
||||
// 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
|
||||
}
|
||||
});
|
||||
|
||||
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,
|
||||
);
|
||||
let rects =
|
||||
get_rects(container.direction().into(), constraints, rect);
|
||||
|
||||
// Side effect, draw here.
|
||||
self.draw_widgets_with_constraints(
|
||||
f,
|
||||
app_state,
|
||||
widgets,
|
||||
&widget_draw_locs,
|
||||
);
|
||||
|
||||
widget_draw_locs
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
// 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,
|
||||
app_state,
|
||||
widgets,
|
||||
widget_draw_locs,
|
||||
);
|
||||
});
|
||||
for (id, rect) in &self.derived_widget_draw_locs {
|
||||
match &self.widget_layout.get_node(*id) {
|
||||
Some(Node::Widget(widget)) => {
|
||||
self.draw_widget(f, app_state, widget, *rect);
|
||||
}
|
||||
_ => {
|
||||
// This should never happen, but if it does, do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})?;
|
||||
@ -800,4 +708,23 @@ impl Painter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_widget<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, widget: &BottomWidget, rect: Rect,
|
||||
) {
|
||||
use BottomWidgetType::*;
|
||||
if rect.width >= 2 && rect.height >= 2 {
|
||||
match &widget.widget_type {
|
||||
Empty => {}
|
||||
Cpu => self.draw_cpu(f, app_state, rect, widget.widget_id),
|
||||
Mem => self.draw_memory_graph(f, app_state, rect, widget.widget_id),
|
||||
Net => self.draw_network(f, app_state, rect, widget.widget_id),
|
||||
Temp => self.draw_temp_table(f, app_state, rect, widget.widget_id),
|
||||
Disk => self.draw_disk_table(f, app_state, rect, widget.widget_id),
|
||||
Proc => self.draw_process_widget(f, app_state, rect, true, widget.widget_id),
|
||||
Battery => self.draw_battery_display(f, app_state, rect, true, widget.widget_id),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,10 +220,14 @@ impl Painter {
|
||||
|
||||
// TODO: [MOUSE] Mouse support for these in search
|
||||
// TODO: [MOVEMENT] Movement support for these in search
|
||||
let (case, whole, regex) = if self.is_mac_os {
|
||||
("Case(F1)", "Whole(F2)", "Regex(F3)")
|
||||
} else {
|
||||
("Case(Alt+C)", "Whole(Alt+W)", "Regex(Alt+R)")
|
||||
let (case, whole, regex) = {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_os = "macos")] {
|
||||
("Case(F1)", "Whole(F2)", "Regex(F3)")
|
||||
} else {
|
||||
("Case(Alt+C)", "Whole(Alt+W)", "Regex(Alt+R)")
|
||||
}
|
||||
}
|
||||
};
|
||||
let option_text = Line::from(vec![
|
||||
Span::styled(case, case_style),
|
||||
|
@ -471,7 +471,7 @@ pub fn get_widget_layout(
|
||||
let bottom_layout = if is_flag_enabled!(basic, matches, config) {
|
||||
default_widget_id = DEFAULT_WIDGET_ID;
|
||||
|
||||
BottomLayout::init_basic_default(get_use_battery(matches, config))
|
||||
BottomLayout::new_basic(get_use_battery(matches, config))
|
||||
} else {
|
||||
let ref_row: Vec<Row>; // Required to handle reference
|
||||
let rows = match &config.row {
|
||||
@ -492,34 +492,24 @@ pub fn get_widget_layout(
|
||||
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
|
||||
let mut total_height_ratio = 0;
|
||||
|
||||
let mut ret_bottom_layout = BottomLayout {
|
||||
rows: 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<_>>>()?,
|
||||
total_row_height_ratio: total_height_ratio,
|
||||
};
|
||||
// let mut ret_bottom_layout = BottomLayout {
|
||||
// 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<_>>>()?,
|
||||
// };
|
||||
|
||||
// Confirm that we have at least ONE widget left - if not, error out!
|
||||
if iter_id > 0 {
|
||||
ret_bottom_layout.get_movement_mappings();
|
||||
// debug!("Bottom layout: {:#?}", ret_bottom_layout);
|
||||
|
||||
ret_bottom_layout
|
||||
} else {
|
||||
return Err(error::BottomError::ConfigError(
|
||||
"please have at least one widget under the '[[row]]' section.".to_string(),
|
||||
));
|
||||
}
|
||||
BottomLayout::from_rows(rows)?
|
||||
};
|
||||
|
||||
Ok((bottom_layout, default_widget_id, default_widget_type))
|
||||
|
@ -8,7 +8,7 @@ use bottom::options::{layout_options::Row, Config};
|
||||
use bottom::utils::error;
|
||||
use toml_edit::de::from_str;
|
||||
|
||||
// TODO: Could move these into the library files rather than external tbh.
|
||||
// FIXME: Move these into the library!
|
||||
|
||||
const PROC_LAYOUT: &str = r##"
|
||||
[[row]]
|
||||
|
Loading…
x
Reference in New Issue
Block a user