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:
Clement Tsang
2025-10-13 04:53:43 -04:00
committed by GitHub
parent f68974b940
commit beaeb03e90
4 changed files with 116 additions and 409 deletions
+79 -74
View File
@@ -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
View File
@@ -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
View File
@@ -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]]
+8 -6
View File
@@ -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>,