mirror of
https://github.com/ClementTsang/bottom.git
synced 2026-05-05 14:30:47 +00:00
refactor: remove custom layout constraint implementation (#1836)
As noted in #1406, I don't need the custom `Constraint` setup anymore. This also starts the work to refactor some really bad code around layouts.
This commit is contained in:
+79
-74
@@ -1,21 +1,40 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use tui::layout::Constraint;
|
||||
|
||||
use crate::{constants::DEFAULT_WIDGET_ID, options::OptionError};
|
||||
|
||||
// Represents a start and end coordinate in some dimension.
|
||||
type LineSegment = (u16, u16);
|
||||
|
||||
type WidgetMappings = (u16, BTreeMap<LineSegment, u64>);
|
||||
type ColumnRowMappings = (u16, BTreeMap<LineSegment, WidgetMappings>);
|
||||
type ColumnMappings = (u16, BTreeMap<LineSegment, ColumnRowMappings>);
|
||||
|
||||
/// Represents a more usable representation of the layout, derived from the
|
||||
/// config.
|
||||
///
|
||||
/// FIXME: This is kinda gross. Ideally optimize out the hard-coded stuff.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BottomLayout {
|
||||
pub rows: Vec<BottomRow>,
|
||||
pub total_row_height_ratio: u32,
|
||||
pub total_row_height_ratio: u16,
|
||||
}
|
||||
|
||||
// Represents a start and end coordinate in some dimension.
|
||||
type LineSegment = (u32, u32);
|
||||
trait Ratio {
|
||||
fn ratio(&self) -> u16;
|
||||
}
|
||||
|
||||
type WidgetMappings = (u32, BTreeMap<LineSegment, u64>);
|
||||
type ColumnRowMappings = (u32, BTreeMap<LineSegment, WidgetMappings>);
|
||||
type ColumnMappings = (u32, BTreeMap<LineSegment, ColumnRowMappings>);
|
||||
impl Ratio for Constraint {
|
||||
fn ratio(&self) -> u16 {
|
||||
match self {
|
||||
Constraint::Min(min) => std::cmp::max(*min, 1),
|
||||
Constraint::Length(_) => 1,
|
||||
Constraint::Fill(scaling) => *scaling,
|
||||
_ => unreachable!("if this gets hit then you're refactoring layouts"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BottomLayout {
|
||||
pub fn get_movement_mappings(&mut self) {
|
||||
@@ -27,7 +46,7 @@ impl BottomLayout {
|
||||
|| a.0 >= b.0 && a.0 < b.1 && a.1 >= b.1
|
||||
}
|
||||
|
||||
fn get_distance(target: LineSegment, candidate: LineSegment) -> u32 {
|
||||
fn get_distance(target: LineSegment, candidate: LineSegment) -> u16 {
|
||||
if candidate.0 < target.0 {
|
||||
candidate.1 - target.0
|
||||
} else if candidate.1 < target.1 {
|
||||
@@ -39,7 +58,6 @@ impl BottomLayout {
|
||||
|
||||
// Now we need to create the correct mapping for moving from a specific
|
||||
// widget to another
|
||||
|
||||
let mut layout_mapping: BTreeMap<LineSegment, ColumnMappings> = BTreeMap::new();
|
||||
let mut total_height = 0;
|
||||
for row in &self.rows {
|
||||
@@ -56,6 +74,10 @@ impl BottomLayout {
|
||||
let mut col_row_mapping: BTreeMap<LineSegment, u64> = BTreeMap::new();
|
||||
let mut is_valid_col_row = false;
|
||||
for widget in &col_row.children {
|
||||
let widget_ratio = widget
|
||||
.ratio_override
|
||||
.unwrap_or_else(|| widget.constraint.ratio());
|
||||
|
||||
match widget.widget_type {
|
||||
BottomWidgetType::Empty => {}
|
||||
_ => {
|
||||
@@ -63,14 +85,14 @@ impl BottomLayout {
|
||||
col_row_mapping.insert(
|
||||
(
|
||||
widget_width * 100 / col_row.total_widget_ratio,
|
||||
(widget_width + widget.constraint.ratio()) * 100
|
||||
(widget_width + widget_ratio) * 100
|
||||
/ col_row.total_widget_ratio,
|
||||
),
|
||||
widget.widget_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
widget_width += widget.constraint.ratio();
|
||||
widget_width += widget_ratio;
|
||||
}
|
||||
if is_valid_col_row {
|
||||
col_mapping.insert(
|
||||
@@ -141,11 +163,14 @@ impl BottomLayout {
|
||||
continue;
|
||||
}
|
||||
|
||||
let widget_ratio = widget
|
||||
.ratio_override
|
||||
.unwrap_or_else(|| widget.constraint.ratio());
|
||||
|
||||
let widget_width_percentage_start =
|
||||
widget_cursor * 100 / col_row.total_widget_ratio;
|
||||
let widget_width_percentage_end =
|
||||
(widget_cursor + widget.constraint.ratio()) * 100
|
||||
/ col_row.total_widget_ratio;
|
||||
(widget_cursor + widget_ratio) * 100 / col_row.total_widget_ratio;
|
||||
|
||||
if let Some(current_row) = layout_mapping
|
||||
.get(&(row_height_percentage_start, row_height_percentage_end))
|
||||
@@ -521,7 +546,7 @@ impl BottomLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
widget_cursor += widget.constraint.ratio();
|
||||
widget_cursor += widget_ratio;
|
||||
}
|
||||
col_row_cursor += col_row.constraint.ratio();
|
||||
}
|
||||
@@ -684,41 +709,12 @@ impl BottomLayout {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum IntermediaryConstraint {
|
||||
PartialRatio(u32),
|
||||
CanvasHandled { ratio: Option<u32> },
|
||||
Grow { minimum: Option<u32> },
|
||||
}
|
||||
|
||||
impl Default for IntermediaryConstraint {
|
||||
fn default() -> Self {
|
||||
IntermediaryConstraint::PartialRatio(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntermediaryConstraint {
|
||||
pub fn ratio(&self) -> u32 {
|
||||
match self {
|
||||
IntermediaryConstraint::PartialRatio(val) => *val,
|
||||
IntermediaryConstraint::Grow { minimum } => match minimum {
|
||||
Some(val) => *val,
|
||||
None => 1,
|
||||
},
|
||||
IntermediaryConstraint::CanvasHandled { ratio } => match ratio {
|
||||
Some(val) => *val,
|
||||
None => 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a single row in the layout.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BottomRow {
|
||||
pub children: Vec<BottomCol>,
|
||||
pub total_col_ratio: u32,
|
||||
pub constraint: IntermediaryConstraint,
|
||||
pub total_col_ratio: u16,
|
||||
pub constraint: Constraint,
|
||||
}
|
||||
|
||||
impl BottomRow {
|
||||
@@ -726,22 +722,22 @@ impl BottomRow {
|
||||
Self {
|
||||
children,
|
||||
total_col_ratio: 1,
|
||||
constraint: IntermediaryConstraint::default(),
|
||||
constraint: Constraint::Fill(1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn total_col_ratio(mut self, total_col_ratio: u32) -> Self {
|
||||
pub fn total_col_ratio(mut self, total_col_ratio: u16) -> Self {
|
||||
self.total_col_ratio = total_col_ratio;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ratio(mut self, row_height_ratio: u32) -> Self {
|
||||
self.constraint = IntermediaryConstraint::PartialRatio(row_height_ratio);
|
||||
pub fn ratio(mut self, value: u16) -> Self {
|
||||
self.constraint = Constraint::Fill(value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn canvas_handled(mut self) -> Self {
|
||||
self.constraint = IntermediaryConstraint::CanvasHandled { ratio: None };
|
||||
self.constraint = Constraint::Length(0);
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -752,8 +748,8 @@ impl BottomRow {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BottomCol {
|
||||
pub children: Vec<BottomColRow>,
|
||||
pub total_col_row_ratio: u32,
|
||||
pub constraint: IntermediaryConstraint,
|
||||
pub total_col_row_ratio: u16,
|
||||
pub constraint: Constraint,
|
||||
}
|
||||
|
||||
impl BottomCol {
|
||||
@@ -761,22 +757,22 @@ impl BottomCol {
|
||||
Self {
|
||||
children,
|
||||
total_col_row_ratio: 1,
|
||||
constraint: IntermediaryConstraint::default(),
|
||||
constraint: Constraint::Fill(1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn total_col_row_ratio(mut self, total_col_row_ratio: u32) -> Self {
|
||||
pub fn total_col_row_ratio(mut self, total_col_row_ratio: u16) -> Self {
|
||||
self.total_col_row_ratio = total_col_row_ratio;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ratio(mut self, col_width_ratio: u32) -> Self {
|
||||
self.constraint = IntermediaryConstraint::PartialRatio(col_width_ratio);
|
||||
pub fn ratio(mut self, value: u16) -> Self {
|
||||
self.constraint = Constraint::Fill(value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn canvas_handled(mut self) -> Self {
|
||||
self.constraint = IntermediaryConstraint::CanvasHandled { ratio: None };
|
||||
self.constraint = Constraint::Length(0);
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -784,8 +780,8 @@ impl BottomCol {
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct BottomColRow {
|
||||
pub children: Vec<BottomWidget>,
|
||||
pub total_widget_ratio: u32,
|
||||
pub constraint: IntermediaryConstraint,
|
||||
pub total_widget_ratio: u16,
|
||||
pub constraint: Constraint,
|
||||
}
|
||||
|
||||
impl BottomColRow {
|
||||
@@ -793,27 +789,27 @@ impl BottomColRow {
|
||||
Self {
|
||||
children,
|
||||
total_widget_ratio: 1,
|
||||
constraint: IntermediaryConstraint::default(),
|
||||
constraint: Constraint::Fill(1),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn total_widget_ratio(mut self, total_widget_ratio: u32) -> Self {
|
||||
pub(crate) fn total_widget_ratio(mut self, total_widget_ratio: u16) -> Self {
|
||||
self.total_widget_ratio = total_widget_ratio;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ratio(mut self, col_row_height_ratio: u32) -> Self {
|
||||
self.constraint = IntermediaryConstraint::PartialRatio(col_row_height_ratio);
|
||||
pub fn ratio(mut self, value: u16) -> Self {
|
||||
self.constraint = Constraint::Fill(value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn canvas_handled(mut self) -> Self {
|
||||
self.constraint = IntermediaryConstraint::CanvasHandled { ratio: None };
|
||||
self.constraint = Constraint::Length(0);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn grow(mut self, minimum: Option<u32>) -> Self {
|
||||
self.constraint = IntermediaryConstraint::Grow { minimum };
|
||||
pub fn grow(mut self, minimum: Option<u16>) -> Self {
|
||||
self.constraint = Constraint::Min(minimum.unwrap_or(0));
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -844,7 +840,7 @@ impl WidgetDirection {
|
||||
pub struct BottomWidget {
|
||||
pub widget_type: BottomWidgetType,
|
||||
pub widget_id: u64,
|
||||
pub constraint: IntermediaryConstraint,
|
||||
pub constraint: Constraint,
|
||||
pub left_neighbour: Option<u64>,
|
||||
pub right_neighbour: Option<u64>,
|
||||
pub up_neighbour: Option<u64>,
|
||||
@@ -854,10 +850,16 @@ pub struct BottomWidget {
|
||||
pub parent_reflector: Option<(WidgetDirection, u64)>,
|
||||
|
||||
/// Top left corner when drawn, for mouse click detection. (x, y)
|
||||
///
|
||||
/// TODO: Replace this with just an Option<Rect> for top + bottom.
|
||||
pub top_left_corner: Option<(u16, u16)>,
|
||||
|
||||
/// Bottom right corner when drawn, for mouse click detection. (x, y)
|
||||
pub bottom_right_corner: Option<(u16, u16)>,
|
||||
|
||||
/// TODO: REMOVE THIS LATER. This is temporary code to bridge the
|
||||
/// old layout system with a newer system later.
|
||||
ratio_override: Option<u16>,
|
||||
}
|
||||
|
||||
impl BottomWidget {
|
||||
@@ -865,7 +867,7 @@ impl BottomWidget {
|
||||
Self {
|
||||
widget_type,
|
||||
widget_id,
|
||||
constraint: IntermediaryConstraint::default(),
|
||||
constraint: Constraint::Fill(1),
|
||||
left_neighbour: None,
|
||||
right_neighbour: None,
|
||||
up_neighbour: None,
|
||||
@@ -873,6 +875,7 @@ impl BottomWidget {
|
||||
parent_reflector: None,
|
||||
top_left_corner: None,
|
||||
bottom_right_corner: None,
|
||||
ratio_override: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -896,23 +899,25 @@ impl BottomWidget {
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn ratio(mut self, width_ratio: u32) -> Self {
|
||||
self.constraint = IntermediaryConstraint::PartialRatio(width_ratio);
|
||||
pub(crate) fn ratio(mut self, value: u16) -> Self {
|
||||
self.constraint = Constraint::Fill(value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn canvas_handled(mut self) -> Self {
|
||||
self.constraint = IntermediaryConstraint::CanvasHandled { ratio: None };
|
||||
self.constraint = Constraint::Length(0);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn canvas_with_ratio(mut self, ratio: u32) -> Self {
|
||||
self.constraint = IntermediaryConstraint::CanvasHandled { ratio: Some(ratio) };
|
||||
pub fn grow(mut self, minimum: Option<u16>) -> Self {
|
||||
self.constraint = Constraint::Min(minimum.unwrap_or(0));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn grow(mut self, minimum: Option<u32>) -> Self {
|
||||
self.constraint = IntermediaryConstraint::Grow { minimum };
|
||||
/// TODO: REMOVE THIS LATER. This is temporary code to bridge the
|
||||
/// old layout system with a newer system later.
|
||||
pub fn with_ratio_override(mut self, ratio_override: u16) -> Self {
|
||||
self.ratio_override = Some(ratio_override);
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
+27
-327
@@ -8,7 +8,6 @@ pub mod dialogs;
|
||||
mod drawing_utils;
|
||||
mod widgets;
|
||||
|
||||
use itertools::izip;
|
||||
use tui::{
|
||||
Frame, Terminal,
|
||||
backend::Backend,
|
||||
@@ -20,7 +19,7 @@ use tui::{
|
||||
use crate::{
|
||||
app::{
|
||||
App,
|
||||
layout_manager::{BottomColRow, BottomLayout, BottomWidgetType, IntermediaryConstraint},
|
||||
layout_manager::{BottomColRow, BottomLayout, BottomWidgetType},
|
||||
},
|
||||
constants::*,
|
||||
options::config::style::Styles,
|
||||
@@ -29,122 +28,24 @@ use crate::{
|
||||
/// Handles the canvas' state.
|
||||
pub struct Painter {
|
||||
pub styles: Styles,
|
||||
|
||||
/// Used to know whether to invalidate things.
|
||||
previous_height: u16,
|
||||
|
||||
/// Used to know whether to invalidate things.
|
||||
previous_width: u16,
|
||||
|
||||
// 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>>>>,
|
||||
widget_layout: BottomLayout,
|
||||
}
|
||||
|
||||
/// The constraints of a widget relative to its parent.
|
||||
///
|
||||
/// This is used over ratatui's internal representation due to
|
||||
/// <https://github.com/ClementTsang/bottom/issues/896>.
|
||||
pub enum LayoutConstraint {
|
||||
CanvasHandled,
|
||||
Grow,
|
||||
Ratio(u32, u32),
|
||||
/// The layout.
|
||||
layout: BottomLayout,
|
||||
}
|
||||
|
||||
impl Painter {
|
||||
pub fn init(layout: BottomLayout, styling: Styles) -> anyhow::Result<Self> {
|
||||
// Now for modularity; we have to also initialize the base layouts!
|
||||
// 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();
|
||||
|
||||
layout.rows.iter().for_each(|row| {
|
||||
match row.constraint {
|
||||
IntermediaryConstraint::PartialRatio(val) => {
|
||||
row_constraints
|
||||
.push(LayoutConstraint::Ratio(val, layout.total_row_height_ratio));
|
||||
}
|
||||
IntermediaryConstraint::CanvasHandled { .. } => {
|
||||
row_constraints.push(LayoutConstraint::CanvasHandled);
|
||||
}
|
||||
IntermediaryConstraint::Grow { .. } => {
|
||||
row_constraints.push(LayoutConstraint::Grow);
|
||||
}
|
||||
}
|
||||
|
||||
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| {
|
||||
match col.constraint {
|
||||
IntermediaryConstraint::PartialRatio(val) => {
|
||||
new_col_constraints.push(LayoutConstraint::Ratio(val, row.total_col_ratio));
|
||||
}
|
||||
IntermediaryConstraint::CanvasHandled { .. } => {
|
||||
new_col_constraints.push(LayoutConstraint::CanvasHandled);
|
||||
}
|
||||
IntermediaryConstraint::Grow { .. } => {
|
||||
new_col_constraints.push(LayoutConstraint::Grow);
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_new_col_row_constraints = Vec::new();
|
||||
let mut new_new_widget_constraints = Vec::new();
|
||||
col.children.iter().for_each(|col_row| {
|
||||
match col_row.constraint {
|
||||
IntermediaryConstraint::PartialRatio(val) => {
|
||||
new_new_col_row_constraints
|
||||
.push(LayoutConstraint::Ratio(val, col.total_col_row_ratio));
|
||||
}
|
||||
IntermediaryConstraint::CanvasHandled { .. } => {
|
||||
new_new_col_row_constraints.push(LayoutConstraint::CanvasHandled);
|
||||
}
|
||||
IntermediaryConstraint::Grow { .. } => {
|
||||
new_new_col_row_constraints.push(LayoutConstraint::Grow);
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_new_new_widget_constraints = Vec::new();
|
||||
col_row
|
||||
.children
|
||||
.iter()
|
||||
.for_each(|widget| match widget.constraint {
|
||||
IntermediaryConstraint::PartialRatio(val) => {
|
||||
new_new_new_widget_constraints
|
||||
.push(LayoutConstraint::Ratio(val, col_row.total_widget_ratio));
|
||||
}
|
||||
IntermediaryConstraint::CanvasHandled { .. } => {
|
||||
new_new_new_widget_constraints
|
||||
.push(LayoutConstraint::CanvasHandled);
|
||||
}
|
||||
IntermediaryConstraint::Grow { .. } => {
|
||||
new_new_new_widget_constraints.push(LayoutConstraint::Grow);
|
||||
}
|
||||
});
|
||||
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 painter = Painter {
|
||||
styles: styling,
|
||||
previous_height: 0,
|
||||
previous_width: 0,
|
||||
row_constraints,
|
||||
col_constraints,
|
||||
col_row_constraints,
|
||||
layout_constraints,
|
||||
widget_layout: layout,
|
||||
derived_widget_draw_locs: Vec::default(),
|
||||
layout,
|
||||
};
|
||||
|
||||
Ok(painter)
|
||||
@@ -455,235 +356,34 @@ impl Painter {
|
||||
self.draw_frozen_indicator(f, frozen_draw_loc);
|
||||
}
|
||||
|
||||
if self.derived_widget_draw_locs.is_empty() || app_state.is_force_redraw {
|
||||
// TODO: Can I remove this? Does ratatui's layout constraints work properly for
|
||||
// fixing https://github.com/ClementTsang/bottom/issues/896 now?
|
||||
fn get_constraints(
|
||||
direction: Direction, constraints: &[LayoutConstraint], area: Rect,
|
||||
) -> Vec<Rect> {
|
||||
// Order of operations:
|
||||
// - Ratios first + canvas-handled (which is just zero)
|
||||
// - Then any flex-grows to take up remaining space; divide amongst
|
||||
// remaining hand out any remaining space
|
||||
// A two-pass algorithm - get layouts using constraints (first pass),
|
||||
// then pass each layout to the corresponding widget (second pass).
|
||||
// Note that layouts are already cached in ratatui, so we don't need
|
||||
// to do it manually!
|
||||
let base = Layout::vertical(self.layout.rows.iter().map(|r| r.constraint))
|
||||
.split(terminal_size);
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
struct Size {
|
||||
width: u16,
|
||||
height: u16,
|
||||
}
|
||||
for (br, base) in self.layout.rows.iter().zip(base.iter()) {
|
||||
let base =
|
||||
Layout::horizontal(br.children.iter().map(|bc| bc.constraint)).split(*base);
|
||||
|
||||
impl Size {
|
||||
fn shrink_width(&mut self, amount: u16) {
|
||||
self.width -= amount;
|
||||
}
|
||||
for (bc, base) in br.children.iter().zip(base.iter()) {
|
||||
let base = Layout::vertical(bc.children.iter().map(|bcr| bcr.constraint))
|
||||
.split(*base);
|
||||
|
||||
fn shrink_height(&mut self, amount: u16) {
|
||||
self.height -= amount;
|
||||
}
|
||||
}
|
||||
for (widgets, base) in bc.children.iter().zip(base.iter()) {
|
||||
let widget_draw_locs =
|
||||
Layout::horizontal(widgets.children.iter().map(|bw| bw.constraint))
|
||||
.split(*base);
|
||||
|
||||
let mut bounds = Size {
|
||||
width: area.width,
|
||||
height: area.height,
|
||||
};
|
||||
let mut sizes = vec![Size::default(); constraints.len()];
|
||||
let mut grow = vec![];
|
||||
let mut num_non_ch = 0;
|
||||
|
||||
for (itx, (constraint, size)) in
|
||||
constraints.iter().zip(sizes.iter_mut()).enumerate()
|
||||
{
|
||||
match constraint {
|
||||
LayoutConstraint::Ratio(a, b) => {
|
||||
match direction {
|
||||
Direction::Horizontal => {
|
||||
let amount =
|
||||
(((area.width as u32) * (*a)) / (*b)) 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;
|
||||
bounds.shrink_height(amount);
|
||||
size.width = area.width;
|
||||
size.height = amount;
|
||||
}
|
||||
}
|
||||
num_non_ch += 1;
|
||||
}
|
||||
LayoutConstraint::Grow => {
|
||||
// Mark it as grow in the vector and handle in second pass.
|
||||
grow.push(itx);
|
||||
num_non_ch += 1;
|
||||
}
|
||||
LayoutConstraint::CanvasHandled => {
|
||||
// Do nothing in this case. It's already 0.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !grow.is_empty() {
|
||||
match direction {
|
||||
Direction::Horizontal => {
|
||||
let width = bounds.width / grow.len() as u16;
|
||||
bounds.shrink_width(width * grow.len() as u16);
|
||||
for g in grow {
|
||||
sizes[g] = Size {
|
||||
width,
|
||||
height: area.height,
|
||||
};
|
||||
}
|
||||
}
|
||||
Direction::Vertical => {
|
||||
let height = bounds.height / grow.len() as u16;
|
||||
bounds.shrink_height(height * grow.len() as u16);
|
||||
for g in grow {
|
||||
sizes[g] = Size {
|
||||
width: area.width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if num_non_ch > 0 {
|
||||
match direction {
|
||||
Direction::Horizontal => {
|
||||
let per_item = bounds.width / num_non_ch;
|
||||
let mut remaining_width = bounds.width % num_non_ch;
|
||||
for (size, constraint) in sizes.iter_mut().zip(constraints) {
|
||||
match constraint {
|
||||
LayoutConstraint::CanvasHandled => {}
|
||||
LayoutConstraint::Grow
|
||||
| LayoutConstraint::Ratio(_, _) => {
|
||||
if remaining_width > 0 {
|
||||
size.width += per_item + 1;
|
||||
remaining_width -= 1;
|
||||
} else {
|
||||
size.width += per_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Direction::Vertical => {
|
||||
let per_item = bounds.height / num_non_ch;
|
||||
let mut remaining_height = bounds.height % num_non_ch;
|
||||
for (size, constraint) in sizes.iter_mut().zip(constraints) {
|
||||
match constraint {
|
||||
LayoutConstraint::CanvasHandled => {}
|
||||
LayoutConstraint::Grow
|
||||
| LayoutConstraint::Ratio(_, _) => {
|
||||
if remaining_height > 0 {
|
||||
size.height += per_item + 1;
|
||||
remaining_height -= 1;
|
||||
} else {
|
||||
size.height += per_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut curr_x = area.x;
|
||||
let mut curr_y = area.y;
|
||||
sizes
|
||||
.into_iter()
|
||||
.map(|size| {
|
||||
let rect = Rect::new(curr_x, curr_y, size.width, size.height);
|
||||
match direction {
|
||||
Direction::Horizontal => {
|
||||
curr_x += size.width;
|
||||
}
|
||||
Direction::Vertical => {
|
||||
curr_y += size.height;
|
||||
}
|
||||
}
|
||||
|
||||
rect
|
||||
})
|
||||
.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();
|
||||
} 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,
|
||||
&widget_draw_locs,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
||||
+2
-2
@@ -223,10 +223,10 @@ pub(crate) const DEFAULT_BATTERY_LAYOUT: &str = r#"
|
||||
ratio=30
|
||||
[[row.child]]
|
||||
ratio=2
|
||||
type="cpu"
|
||||
type="cpu"
|
||||
[[row.child]]
|
||||
ratio=1
|
||||
type="battery"
|
||||
type="battery"
|
||||
[[row]]
|
||||
ratio=40
|
||||
[[row.child]]
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{app::layout_manager::*, options::OptionResult};
|
||||
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
|
||||
#[serde(rename = "row")]
|
||||
pub struct Row {
|
||||
pub ratio: Option<u32>,
|
||||
pub ratio: Option<u16>,
|
||||
pub child: Option<Vec<RowChildren>>,
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ fn new_cpu(cpu_left_legend: bool, iter_id: &mut u64) -> BottomColRow {
|
||||
if cpu_left_legend {
|
||||
BottomColRow::new(vec![
|
||||
BottomWidget::new(BottomWidgetType::CpuLegend, legend_id)
|
||||
.canvas_with_ratio(3)
|
||||
.canvas_handled()
|
||||
.with_ratio_override(3)
|
||||
.parent_reflector(Some((WidgetDirection::Right, 1))),
|
||||
BottomWidget::new(BottomWidgetType::Cpu, cpu_id).grow(Some(17)),
|
||||
])
|
||||
@@ -29,7 +30,8 @@ fn new_cpu(cpu_left_legend: bool, iter_id: &mut u64) -> BottomColRow {
|
||||
BottomColRow::new(vec![
|
||||
BottomWidget::new(BottomWidgetType::Cpu, cpu_id).grow(Some(17)),
|
||||
BottomWidget::new(BottomWidgetType::CpuLegend, legend_id)
|
||||
.canvas_with_ratio(3)
|
||||
.canvas_handled()
|
||||
.with_ratio_override(3)
|
||||
.parent_reflector(Some((WidgetDirection::Left, 1))),
|
||||
])
|
||||
}
|
||||
@@ -53,7 +55,7 @@ fn new_proc_search(search_id: u64) -> BottomWidget {
|
||||
|
||||
impl Row {
|
||||
pub fn convert_row_to_bottom_row(
|
||||
&self, iter_id: &mut u64, total_height_ratio: &mut u32, default_widget_id: &mut u64,
|
||||
&self, iter_id: &mut u64, total_height_ratio: &mut u16, default_widget_id: &mut u64,
|
||||
default_widget_type: &Option<BottomWidgetType>, default_widget_count: &mut u64,
|
||||
cpu_left_legend: bool,
|
||||
) -> OptionResult<BottomRow> {
|
||||
@@ -222,7 +224,7 @@ impl Row {
|
||||
pub enum RowChildren {
|
||||
Widget(FinalWidget),
|
||||
Col {
|
||||
ratio: Option<u32>,
|
||||
ratio: Option<u16>,
|
||||
child: Vec<FinalWidget>,
|
||||
},
|
||||
}
|
||||
@@ -232,7 +234,7 @@ pub enum RowChildren {
|
||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
|
||||
pub struct FinalWidget {
|
||||
pub ratio: Option<u32>,
|
||||
pub ratio: Option<u16>,
|
||||
#[serde(rename = "type")]
|
||||
pub widget_type: String,
|
||||
pub default: Option<bool>,
|
||||
|
||||
Reference in New Issue
Block a user