feat(layout): add support for multiple weighted constraints by chunks

This commit is contained in:
Florian Dehau 2021-08-08 19:24:27 +02:00
parent 3797863e14
commit c64d754f88
20 changed files with 445 additions and 370 deletions

View file

@ -6,7 +6,7 @@ use std::{error::Error, io};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Constraint, Direction, Layout},
layout::{Constraint, Direction, Layout, Unit},
style::{Color, Modifier, Style},
widgets::{BarChart, Block, Borders},
Terminal,
@ -73,7 +73,10 @@ fn main() -> Result<(), Box<dyn Error>> {
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.constraints([
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
])
.split(f.size());
let barchart = BarChart::default()
.block(Block::default().title("Data1").borders(Borders::ALL))
@ -85,7 +88,10 @@ fn main() -> Result<(), Box<dyn Error>> {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.constraints([
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
])
.split(chunks[1]);
let barchart = BarChart::default()

View file

@ -6,7 +6,7 @@ use std::{error::Error, io};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Alignment, Constraint, Direction, Layout},
layout::{Alignment, Constraint, Direction, Layout, Unit},
style::{Color, Modifier, Style},
text::Span,
widgets::{Block, BorderType, Borders},
@ -42,13 +42,19 @@ fn main() -> Result<(), Box<dyn Error>> {
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(4)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.constraints([
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
])
.split(f.size());
// Top two inner blocks
let top_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.constraints([
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
])
.split(chunks[0]);
// Top left inner block with green background
@ -75,7 +81,10 @@ fn main() -> Result<(), Box<dyn Error>> {
// Bottom two inner blocks
let bottom_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.constraints([
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
])
.split(chunks[1]);
// Bottom left block with all default borders

View file

@ -6,7 +6,7 @@ use std::{error::Error, io, time::Duration};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Constraint, Direction, Layout, Rect},
layout::{Constraint, Direction, Layout, Rect, Unit},
style::Color,
widgets::{
canvas::{Canvas, Map, MapResolution, Rectangle},
@ -94,7 +94,10 @@ fn main() -> Result<(), Box<dyn Error>> {
terminal.draw(|f| {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.constraints([
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
])
.split(f.size());
let canvas = Canvas::default()
.block(Block::default().borders(Borders::ALL).title("World"))

View file

@ -9,7 +9,7 @@ use std::{error::Error, io};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Constraint, Direction, Layout},
layout::{Constraint, Direction, Layout, Unit},
style::{Color, Modifier, Style},
symbols,
text::Span,
@ -83,14 +83,11 @@ fn main() -> Result<(), Box<dyn Error>> {
let size = f.size();
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3),
]
.as_ref(),
)
.constraints([
Constraint::eq(Unit::Ratio(1, 3)),
Constraint::eq(Unit::Ratio(1, 3)),
Constraint::eq(Unit::Ratio(1, 3)),
])
.split(size);
let x_labels = vec![
Span::styled(

View file

@ -1,7 +1,8 @@
use crate::demo::App;
use std::iter::FromIterator;
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
layout::{Constraint, Constraints, Direction, Layout, Rect, Unit},
style::{Color, Modifier, Style},
symbols,
text::{Span, Spans},
@ -15,7 +16,13 @@ use tui::{
pub fn draw<B: Backend>(f: &mut Frame<B>, app: &mut App) {
let chunks = Layout::default()
.constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
.constraints([
Constraint::eq(3).weight(10).into(),
Constraints::from_iter([
Constraint::eq(Unit::Percentage(100)),
Constraint::gte(10).weight(10),
]),
])
.split(f.size());
let titles = app
.tabs
@ -41,14 +48,14 @@ where
B: Backend,
{
let chunks = Layout::default()
.constraints(
[
Constraint::Length(9),
Constraint::Min(8),
Constraint::Length(7),
]
.as_ref(),
)
.constraints([
Constraint::eq(9).weight(10).into(),
Constraints::from_iter([
Constraint::eq(Unit::Percentage(100)),
Constraint::gte(8).weight(10),
]),
Constraint::eq(7).weight(10).into(),
])
.split(area);
draw_gauges(f, app, chunks[0]);
draw_charts(f, app, chunks[1]);
@ -60,14 +67,7 @@ where
B: Backend,
{
let chunks = Layout::default()
.constraints(
[
Constraint::Length(2),
Constraint::Length(3),
Constraint::Length(1),
]
.as_ref(),
)
.constraints([Constraint::eq(2), Constraint::eq(3), Constraint::eq(1)])
.margin(1)
.split(area);
let block = Block::default().borders(Borders::ALL).title("Graphs");
@ -114,9 +114,12 @@ where
B: Backend,
{
let constraints = if app.show_chart {
vec![Constraint::Percentage(50), Constraint::Percentage(50)]
vec![
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
]
} else {
vec![Constraint::Percentage(100)]
vec![Constraint::eq(Unit::Percentage(100))]
};
let chunks = Layout::default()
.constraints(constraints)
@ -124,11 +127,17 @@ where
.split(area);
{
let chunks = Layout::default()
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.constraints([
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
])
.split(chunks[0]);
{
let chunks = Layout::default()
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.constraints([
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
])
.direction(Direction::Horizontal)
.split(chunks[0]);
@ -302,7 +311,10 @@ where
B: Backend,
{
let chunks = Layout::default()
.constraints([Constraint::Percentage(30), Constraint::Percentage(70)].as_ref())
.constraints([
Constraint::eq(Unit::Percentage(30)),
Constraint::eq(Unit::Percentage(70)),
])
.direction(Direction::Horizontal)
.split(area);
let up_style = Style::default().fg(Color::Green);
@ -324,11 +336,7 @@ where
.bottom_margin(1),
)
.block(Block::default().title("Servers").borders(Borders::ALL))
.widths(&[
Constraint::Length(15),
Constraint::Length(15),
Constraint::Length(10),
]);
.widths([Constraint::eq(15), Constraint::eq(15), Constraint::eq(10)]);
f.render_widget(table, chunks[0]);
let map = Canvas::default()
@ -382,7 +390,10 @@ where
{
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
.constraints([
Constraint::eq(Unit::Ratio(1, 2)),
Constraint::eq(Unit::Ratio(1, 2)),
])
.split(area);
let colors = [
Color::Reset,
@ -416,10 +427,10 @@ where
.collect();
let table = Table::new(items)
.block(Block::default().title("Colors").borders(Borders::ALL))
.widths(&[
Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3),
.widths([
Constraint::eq(Unit::Ratio(1, 3)),
Constraint::eq(Unit::Ratio(1, 3)),
Constraint::eq(Unit::Ratio(1, 3)),
]);
f.render_widget(table, chunks[0]);
}

View file

@ -6,7 +6,7 @@ use std::{error::Error, io, time::Duration};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Constraint, Direction, Layout},
layout::{Constraint, Direction, Layout, Unit},
style::{Color, Modifier, Style},
text::Span,
widgets::{Block, Borders, Gauge},
@ -69,15 +69,12 @@ fn main() -> Result<(), Box<dyn Error>> {
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints(
[
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Percentage(25),
]
.as_ref(),
)
.constraints([
Constraint::eq(Unit::Percentage(25)),
Constraint::eq(Unit::Percentage(25)),
Constraint::eq(Unit::Percentage(25)),
Constraint::eq(Unit::Percentage(25)),
])
.split(f.size());
let gauge = Gauge::default()

View file

@ -6,7 +6,7 @@ use std::{error::Error, io};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Constraint, Direction, Layout},
layout::{Constraint, Direction, Layout, Unit},
widgets::{Block, Borders},
Terminal,
};
@ -25,14 +25,11 @@ fn main() -> Result<(), Box<dyn Error>> {
terminal.draw(|f| {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage(10),
Constraint::Percentage(80),
Constraint::Percentage(10),
]
.as_ref(),
)
.constraints([
Constraint::eq(Unit::Percentage(10)),
Constraint::eq(Unit::Percentage(80)),
Constraint::eq(Unit::Percentage(10)),
])
.split(f.size());
let block = Block::default().title("Block").borders(Borders::ALL);

View file

@ -9,7 +9,7 @@ use std::{error::Error, io};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Constraint, Corner, Direction, Layout},
layout::{Constraint, Corner, Direction, Layout, Unit},
style::{Color, Modifier, Style},
text::{Span, Spans},
widgets::{Block, Borders, List, ListItem},
@ -114,7 +114,10 @@ fn main() -> Result<(), Box<dyn Error>> {
// Create two chunks with equal horizontal screen space
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.constraints([
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
])
.split(f.size());
// Iterate through all elements in the `items` app and append some debug text to it.

View file

@ -6,7 +6,7 @@ use std::{error::Error, io};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Alignment, Constraint, Direction, Layout},
layout::{Alignment, Constraint, Direction, Layout, Unit},
style::{Color, Modifier, Style},
text::{Span, Spans},
widgets::{Block, Borders, Paragraph, Wrap},
@ -42,12 +42,11 @@ fn main() -> Result<(), Box<dyn Error>> {
.margin(5)
.constraints(
[
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::eq(Unit::Percentage(25)),
Constraint::eq(Unit::Percentage(25)),
Constraint::eq(Unit::Percentage(25)),
Constraint::eq(Unit::Percentage(25)),
]
.as_ref(),
)
.split(size);

View file

@ -6,7 +6,7 @@ use std::{error::Error, io};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Alignment, Constraint, Direction, Layout, Rect},
layout::{Alignment, Constraint, Direction, Layout, Rect, Unit},
style::{Color, Modifier, Style},
text::{Span, Spans},
widgets::{Block, Borders, Clear, Paragraph, Wrap},
@ -18,26 +18,20 @@ use tui::{
fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
let popup_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Percentage(percent_y),
Constraint::Percentage((100 - percent_y) / 2),
]
.as_ref(),
)
.constraints(vec![
Constraint::eq(Unit::Percentage((100 - percent_y) / 2)),
Constraint::eq(Unit::Percentage(percent_y)),
Constraint::eq(Unit::Percentage((100 - percent_y) / 2)),
])
.split(r);
Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2),
]
.as_ref(),
)
.constraints(vec![
Constraint::eq(Unit::Percentage((100 - percent_x) / 2)),
Constraint::eq(Unit::Percentage(percent_x)),
Constraint::eq(Unit::Percentage((100 - percent_x) / 2)),
])
.split(popup_layout[1])[1]
}
@ -57,7 +51,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.constraints([Constraint::eq(Unit::Percentage(50)), Constraint::eq(Unit::Percentage(50))])
.split(size);
let s = "Veeeeeeeeeeeeeeeery loooooooooooooooooong striiiiiiiiiiiiiiiiiiiiiiiiiing. ";

View file

@ -9,7 +9,7 @@ use std::{error::Error, io};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Constraint, Direction, Layout},
layout::{Constraint, Direction, Layout, Unit},
style::{Color, Style},
widgets::{Block, Borders, Sparkline},
Terminal,
@ -68,15 +68,12 @@ fn main() -> Result<(), Box<dyn Error>> {
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints(
[
Constraint::Length(3),
Constraint::Length(3),
Constraint::Length(7),
Constraint::Min(0),
]
.as_ref(),
)
.constraints([
Constraint::eq(3).weight(10),
Constraint::eq(3).weight(10),
Constraint::eq(7).weight(10),
Constraint::eq(Unit::Percentage(100)),
])
.split(f.size());
let sparkline = Sparkline::default()
.block(

View file

@ -2,11 +2,11 @@
mod util;
use crate::util::event::{Event, Events};
use std::{error::Error, io};
use std::{error::Error, io, iter::FromIterator};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Constraint, Layout},
layout::{Constraint, Constraints, Layout, Unit},
style::{Color, Modifier, Style},
widgets::{Block, Borders, Cell, Row, Table, TableState},
Terminal,
@ -89,7 +89,7 @@ fn main() -> Result<(), Box<dyn Error>> {
loop {
terminal.draw(|f| {
let rects = Layout::default()
.constraints([Constraint::Percentage(100)].as_ref())
.constraints(vec![Constraint::eq(Unit::Percentage(100))])
.margin(5)
.split(f.size());
@ -109,18 +109,27 @@ fn main() -> Result<(), Box<dyn Error>> {
.max()
.unwrap_or(0)
+ 1;
let cells = item.iter().map(|c| Cell::from(*c));
Row::new(cells).height(height as u16).bottom_margin(1)
let cells = item
.iter()
.map(|c| Cell::from(*c).style(Style::default().bg(Color::Yellow)));
Row::new(cells).height(height as u16)
});
let t = Table::new(rows)
.header(header)
.block(Block::default().borders(Borders::ALL).title("Table"))
.highlight_style(selected_style)
.highlight_symbol(">> ")
.widths(&[
Constraint::Percentage(50),
Constraint::Length(30),
Constraint::Max(10),
.widths([
Constraint::gte(20).weight(30).into(),
Constraints::from_iter([
Constraint::eq(20).weight(10),
Constraint::gte(10).weight(30),
]),
Constraints::from_iter([
Constraint::eq(Unit::Percentage(100)),
Constraint::gte(30).weight(20),
]),
]);
f.render_stateful_widget(t, rects[0], &mut table.state);
})?;

View file

@ -9,7 +9,7 @@ use std::{error::Error, io};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Constraint, Direction, Layout},
layout::{Constraint, Direction, Layout, Unit},
style::{Color, Modifier, Style},
text::{Span, Spans},
widgets::{Block, Borders, Tabs},
@ -42,7 +42,10 @@ fn main() -> Result<(), Box<dyn Error>> {
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(5)
.constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
.constraints([
Constraint::eq(3).weight(10),
Constraint::eq(Unit::Percentage(100)),
])
.split(size);
let block = Block::default().style(Style::default().bg(Color::White).fg(Color::Black));

View file

@ -18,7 +18,7 @@ use std::{error::Error, io};
use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen};
use tui::{
backend::TermionBackend,
layout::{Constraint, Direction, Layout},
layout::{Constraint, Direction, Layout, Unit},
style::{Color, Modifier, Style},
text::{Span, Spans, Text},
widgets::{Block, Borders, List, ListItem, Paragraph},
@ -71,14 +71,11 @@ fn main() -> Result<(), Box<dyn Error>> {
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints(
[
Constraint::Length(1),
Constraint::Length(3),
Constraint::Min(1),
]
.as_ref(),
)
.constraints([
Constraint::eq(1).weight(10),
Constraint::eq(3).weight(10),
Constraint::eq(Unit::Percentage(100)),
])
.split(f.size());
let (msg, style) = match app.input_mode {

View file

@ -21,28 +21,128 @@ pub enum Direction {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Constraint {
// TODO: enforce range 0 - 100
pub enum Unit {
Length(u16),
Percentage(u16),
Ratio(u32, u32),
Length(u16),
Max(u16),
Min(u16),
}
impl Constraint {
pub fn apply(&self, length: u16) -> u16 {
impl From<u16> for Unit {
fn from(v: u16) -> Unit {
Unit::Length(v)
}
}
impl Unit {
pub(crate) fn apply(&self, length: u16) -> u16 {
match *self {
Constraint::Percentage(p) => length * p / 100,
Constraint::Ratio(num, den) => {
Unit::Percentage(p) => length * p / 100,
Unit::Ratio(num, den) => {
let r = num * u32::from(length) / den;
r as u16
}
Constraint::Length(l) => length.min(l),
Constraint::Max(m) => length.min(m),
Constraint::Min(m) => length.max(m),
Unit::Length(l) => length.min(l),
}
}
fn check(&self) {
match *self {
Unit::Percentage(p) => {
assert!(
p <= 100,
"Percentages should be between 0 and 100 inclusively."
);
}
Unit::Ratio(num, den) => {
assert!(
num <= den,
"Ratio numerator should be less than or equalt to denominator."
);
}
_ => {}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Operator {
Equal,
GreaterThanOrEqual,
LessThanOrEqual,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Constraint {
operator: Operator,
unit: Unit,
weight: u8,
}
impl Constraint {
pub fn gte<T>(u: T) -> Constraint
where
T: Into<Unit>,
{
let u = u.into();
u.check();
Constraint {
operator: Operator::GreaterThanOrEqual,
unit: u,
weight: 0,
}
}
pub fn lte<T>(u: T) -> Constraint
where
T: Into<Unit>,
{
let u = u.into();
u.check();
Constraint {
operator: Operator::LessThanOrEqual,
unit: u,
weight: 0,
}
}
pub fn eq<T>(u: T) -> Constraint
where
T: Into<Unit>,
{
let u = u.into();
u.check();
Constraint {
operator: Operator::Equal,
unit: u,
weight: 0,
}
}
pub fn weight(mut self, weight: u8) -> Constraint {
self.weight = weight;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Constraints(Vec<Constraint>);
impl From<Constraint> for Constraints {
fn from(c: Constraint) -> Constraints {
Constraints(vec![c])
}
}
impl<T> std::iter::FromIterator<T> for Constraints
where
T: Into<Constraint>,
{
fn from_iter<I>(iter: I) -> Constraints
where
I: IntoIterator<Item = T>,
{
Constraints(iter.into_iter().map(|c| c.into()).collect())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -62,10 +162,7 @@ pub enum Alignment {
pub struct Layout {
direction: Direction,
margin: Margin,
constraints: Vec<Constraint>,
/// Whether the last chunk of the computed layout should be expanded to fill the available
/// space.
expand_to_fill: bool,
constraints: Vec<Constraints>,
}
thread_local! {
@ -81,17 +178,17 @@ impl Default for Layout {
vertical: 0,
},
constraints: Vec::new(),
expand_to_fill: true,
}
}
}
impl Layout {
pub fn constraints<C>(mut self, constraints: C) -> Layout
pub fn constraints<I>(mut self, constraints: I) -> Layout
where
C: Into<Vec<Constraint>>,
I: IntoIterator,
I::Item: Into<Constraints>,
{
self.constraints = constraints.into();
self.constraints = constraints.into_iter().map(|c| c.into()).collect();
self
}
@ -118,20 +215,18 @@ impl Layout {
self
}
pub(crate) fn expand_to_fill(mut self, expand_to_fill: bool) -> Layout {
self.expand_to_fill = expand_to_fill;
self
}
/// Wrapper function around the cassowary-rs solver to be able to split a given
/// area into smaller ones based on the preferred widths or heights and the direction.
///
/// # Examples
/// ```
/// # use tui::layout::{Rect, Constraint, Direction, Layout};
/// # use tui::layout::{Rect, Constraint, Direction, Layout, Unit};
/// let chunks = Layout::default()
/// .direction(Direction::Vertical)
/// .constraints([Constraint::Length(5), Constraint::Min(0)].as_ref())
/// .constraints([
/// Constraint::eq(5).weight(10),
/// Constraint::eq(Unit::Percentage(100)).weight(5)
/// ])
/// .split(Rect {
/// x: 2,
/// y: 2,
@ -158,7 +253,7 @@ impl Layout {
///
/// let chunks = Layout::default()
/// .direction(Direction::Horizontal)
/// .constraints([Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)].as_ref())
/// .constraints([Constraint::eq(Unit::Ratio(1, 3)), Constraint::eq(Unit::Ratio(2, 3))])
/// .split(Rect {
/// x: 0,
/// y: 0,
@ -231,57 +326,25 @@ fn split(area: Rect, layout: &Layout) -> Vec<Rect> {
Direction::Vertical => first.top() | EQ(REQUIRED) | f64::from(dest_area.top()),
});
}
if layout.expand_to_fill {
if let Some(last) = elements.last() {
ccs.push(match layout.direction {
Direction::Horizontal => last.right() | EQ(REQUIRED) | f64::from(dest_area.right()),
Direction::Vertical => last.bottom() | EQ(REQUIRED) | f64::from(dest_area.bottom()),
});
}
}
match layout.direction {
Direction::Horizontal => {
for pair in elements.windows(2) {
ccs.push((pair[0].x + pair[0].width) | EQ(REQUIRED) | pair[1].x);
}
for (i, size) in layout.constraints.iter().enumerate() {
for (i, c) in layout.constraints.iter().enumerate() {
ccs.push(elements[i].y | EQ(REQUIRED) | f64::from(dest_area.y));
ccs.push(elements[i].height | EQ(REQUIRED) | f64::from(dest_area.height));
ccs.push(match *size {
Constraint::Length(v) => elements[i].width | EQ(WEAK) | f64::from(v),
Constraint::Percentage(v) => {
elements[i].width | EQ(WEAK) | (f64::from(v * dest_area.width) / 100.0)
}
Constraint::Ratio(n, d) => {
elements[i].width
| EQ(WEAK)
| (f64::from(dest_area.width) * f64::from(n) / f64::from(d))
}
Constraint::Min(v) => elements[i].width | GE(WEAK) | f64::from(v),
Constraint::Max(v) => elements[i].width | LE(WEAK) | f64::from(v),
});
apply_constraints(&mut ccs, c, elements[i].width, dest_area.width);
}
}
Direction::Vertical => {
for pair in elements.windows(2) {
ccs.push((pair[0].y + pair[0].height) | EQ(REQUIRED) | pair[1].y);
}
for (i, size) in layout.constraints.iter().enumerate() {
for (i, c) in layout.constraints.iter().enumerate() {
ccs.push(elements[i].x | EQ(REQUIRED) | f64::from(dest_area.x));
ccs.push(elements[i].width | EQ(REQUIRED) | f64::from(dest_area.width));
ccs.push(match *size {
Constraint::Length(v) => elements[i].height | EQ(WEAK) | f64::from(v),
Constraint::Percentage(v) => {
elements[i].height | EQ(WEAK) | (f64::from(v * dest_area.height) / 100.0)
}
Constraint::Ratio(n, d) => {
elements[i].height
| EQ(WEAK)
| (f64::from(dest_area.height) * f64::from(n) / f64::from(d))
}
Constraint::Min(v) => elements[i].height | GE(WEAK) | f64::from(v),
Constraint::Max(v) => elements[i].height | LE(WEAK) | f64::from(v),
});
apply_constraints(&mut ccs, c, elements[i].height, dest_area.height);
}
}
}
@ -309,23 +372,31 @@ fn split(area: Rect, layout: &Layout) -> Vec<Rect> {
_ => {}
}
}
if layout.expand_to_fill {
// Fix imprecision by extending the last item a bit if necessary
if let Some(last) = results.last_mut() {
match layout.direction {
Direction::Vertical => {
last.height = dest_area.bottom() - last.y;
}
Direction::Horizontal => {
last.width = dest_area.right() - last.x;
}
}
}
}
results
}
fn apply_constraints(
ccs: &mut Vec<CassowaryConstraint>,
constraints: &Constraints,
var: Variable,
total_length: u16,
) {
for c in &constraints.0 {
let weight = WEAK + f64::from(c.weight);
let value = match c.unit {
Unit::Length(v) => f64::from(v),
Unit::Percentage(v) => f64::from(v * total_length) / 100.0,
Unit::Ratio(n, d) => f64::from(total_length) * f64::from(n) / f64::from(d),
};
let operator = match c.operator {
Operator::GreaterThanOrEqual => GE(weight),
Operator::Equal => EQ(weight),
Operator::LessThanOrEqual => LE(weight),
};
ccs.push(var | operator | value);
}
}
/// A container used by the solver inside split
struct Element {
x: Variable,
@ -476,6 +547,12 @@ impl Rect {
mod tests {
use super::*;
#[test]
#[should_panic]
fn constraint_invalid_percentages() {
Constraint::eq(Unit::Percentage(110));
}
#[test]
fn test_vertical_split_by_height() {
let target = Rect {
@ -487,14 +564,12 @@ mod tests {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage(10),
Constraint::Max(5),
Constraint::Min(1),
]
.as_ref(),
)
.constraints([
Constraint::eq(10),
Constraint::eq(10),
Constraint::lte(5),
Constraint::gte(1),
])
.split(target);
assert_eq!(target.height, chunks.iter().map(|r| r.height).sum::<u16>());

View file

@ -111,7 +111,7 @@
//! use tui::Terminal;
//! use tui::backend::TermionBackend;
//! use tui::widgets::{Widget, Block, Borders};
//! use tui::layout::{Layout, Constraint, Direction};
//! use tui::layout::{Layout, Constraint, Direction, Unit};
//!
//! fn main() -> Result<(), io::Error> {
//! let stdout = io::stdout().into_raw_mode()?;
@ -123,10 +123,10 @@
//! .margin(1)
//! .constraints(
//! [
//! Constraint::Percentage(10),
//! Constraint::Percentage(80),
//! Constraint::Percentage(10)
//! ].as_ref()
//! Constraint::eq(Unit::Percentage(10)),
//! Constraint::eq(Unit::Percentage(80)),
//! Constraint::eq(Unit::Percentage(10))
//! ]
//! )
//! .split(f.size());
//! let block = Block::default()
@ -142,10 +142,7 @@
//! }
//! ```
//!
//! This let you describe responsive terminal UI by nesting layouts. You should note that by
//! default the computed layout tries to fill the available space completely. So if for any reason
//! you might need a blank space somewhere, try to pass an additional constraint and don't use the
//! corresponding area.
//! This let you describe responsive terminal UI by nesting layouts.
pub mod backend;
pub mod buffer;

View file

@ -1,6 +1,6 @@
use crate::{
buffer::Buffer,
layout::{Constraint, Rect},
layout::{Rect, Unit},
style::{Color, Style},
symbols,
text::{Span, Spans},
@ -225,7 +225,7 @@ pub struct Chart<'a> {
/// The widget base style
style: Style,
/// Constraints used to determine whether the legend should be shown or not
hidden_legend_constraints: (Constraint, Constraint),
hidden_legend_constraints: (Unit, Unit),
}
impl<'a> Chart<'a> {
@ -236,7 +236,7 @@ impl<'a> Chart<'a> {
y_axis: Axis::default(),
style: Default::default(),
datasets,
hidden_legend_constraints: (Constraint::Ratio(1, 4), Constraint::Ratio(1, 4)),
hidden_legend_constraints: (Unit::Ratio(1, 4), Unit::Ratio(1, 4)),
}
}
@ -266,17 +266,17 @@ impl<'a> Chart<'a> {
///
/// ```
/// # use tui::widgets::Chart;
/// # use tui::layout::Constraint;
/// # use tui::layout::Unit;
/// let constraints = (
/// Constraint::Ratio(1, 3),
/// Constraint::Ratio(1, 4)
/// Unit::Ratio(1, 3),
/// Unit::Ratio(1, 4)
/// );
/// // Hide the legend when either its width is greater than 33% of the total widget width
/// // or if its height is greater than 25% of the total widget height.
/// let _chart: Chart = Chart::new(vec![])
/// .hidden_legend_constraints(constraints);
/// ```
pub fn hidden_legend_constraints(mut self, constraints: (Constraint, Constraint)) -> Chart<'a> {
pub fn hidden_legend_constraints(mut self, constraints: (Unit, Unit)) -> Chart<'a> {
self.hidden_legend_constraints = constraints;
self
}
@ -562,7 +562,7 @@ mod tests {
struct LegendTestCase {
chart_area: Rect,
hidden_legend_constraints: (Constraint, Constraint),
hidden_legend_constraints: (Unit, Unit),
legend_area: Option<Rect>,
}
@ -572,12 +572,12 @@ mod tests {
let cases = [
LegendTestCase {
chart_area: Rect::new(0, 0, 100, 100),
hidden_legend_constraints: (Constraint::Ratio(1, 4), Constraint::Ratio(1, 4)),
hidden_legend_constraints: (Unit::Ratio(1, 4), Unit::Ratio(1, 4)),
legend_area: Some(Rect::new(88, 0, 12, 12)),
},
LegendTestCase {
chart_area: Rect::new(0, 0, 100, 100),
hidden_legend_constraints: (Constraint::Ratio(1, 10), Constraint::Ratio(1, 4)),
hidden_legend_constraints: (Unit::Ratio(1, 10), Unit::Ratio(1, 4)),
legend_area: None,
},
];

View file

@ -1,6 +1,6 @@
use crate::{
buffer::Buffer,
layout::{Constraint, Direction, Layout, Rect},
layout::{Constraint, Constraints, Direction, Layout, Rect, Unit},
style::Style,
text::Text,
widgets::{Block, StatefulWidget, Widget},
@ -178,7 +178,7 @@ impl<'a> Row<'a> {
/// // As any other widget, a Table can be wrapped in a Block.
/// .block(Block::default().title("Table"))
/// // Columns widths are constrained in the same way as Layout...
/// .widths(&[Constraint::Length(5), Constraint::Length(5), Constraint::Length(10)])
/// .widths([Constraint::eq(5), Constraint::eq(5), Constraint::eq(10)])
/// // ...and they can be separated by a fixed spacing.
/// .column_spacing(1)
/// // If you wish to highlight a row in any specific way when it is selected...
@ -193,7 +193,7 @@ pub struct Table<'a> {
/// Base style for the widget
style: Style,
/// Width constraints for each column
widths: &'a [Constraint],
widths: Vec<Constraints>,
/// Space between each column
column_spacing: u16,
/// Style used to render the selected row
@ -214,7 +214,7 @@ impl<'a> Table<'a> {
Self {
block: None,
style: Style::default(),
widths: &[],
widths: Vec::new(),
column_spacing: 1,
highlight_style: Style::default(),
highlight_symbol: None,
@ -233,16 +233,12 @@ impl<'a> Table<'a> {
self
}
pub fn widths(mut self, widths: &'a [Constraint]) -> Self {
let between_0_and_100 = |&w| match w {
Constraint::Percentage(p) => p <= 100,
_ => true,
};
assert!(
widths.iter().all(between_0_and_100),
"Percentages should be between 0 and 100 inclusively."
);
self.widths = widths;
pub fn widths<I>(mut self, widths: I) -> Self
where
I: IntoIterator,
I::Item: Into<Constraints>,
{
self.widths = widths.into_iter().map(|w| w.into()).collect();
self
}
@ -266,16 +262,24 @@ impl<'a> Table<'a> {
self
}
fn get_columns_widths(&self, max_width: u16, has_selection: bool) -> Vec<u16> {
let mut constraints = Vec::with_capacity(self.widths.len() * 2 + 1);
fn get_columns_widths(&self, max_width: u16, has_selection: bool) -> (u16, Vec<u16>) {
let mut constraints: Vec<Constraints> = Vec::with_capacity(self.widths.len() * 2 + 1);
if has_selection {
let highlight_symbol_width =
self.highlight_symbol.map(|s| s.width() as u16).unwrap_or(0);
constraints.push(Constraint::Length(highlight_symbol_width));
constraints.push(
Constraint::eq(Unit::Length(highlight_symbol_width))
.weight(u8::MAX)
.into(),
);
}
for constraint in self.widths {
constraints.push(*constraint);
constraints.push(Constraint::Length(self.column_spacing));
for constraint in &self.widths {
constraints.push(constraint.clone());
constraints.push(
Constraint::eq(Unit::Length(self.column_spacing))
.weight(u8::MAX)
.into(),
);
}
if !self.widths.is_empty() {
constraints.pop();
@ -283,17 +287,18 @@ impl<'a> Table<'a> {
let mut chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints(constraints)
.expand_to_fill(false)
.split(Rect {
x: 0,
y: 0,
width: max_width,
height: 1,
});
if has_selection {
chunks.remove(0);
}
chunks.iter().step_by(2).map(|c| c.width).collect()
let first_column = if has_selection {
chunks.remove(0).width
} else {
0
};
(first_column, chunks.iter().map(|c| c.width).collect())
}
fn get_row_bounds(
@ -381,7 +386,8 @@ impl<'a> StatefulWidget for Table<'a> {
};
let has_selection = state.selected.is_some();
let columns_widths = self.get_columns_widths(table_area.width, has_selection);
let (first_column, columns_widths) =
self.get_columns_widths(table_area.width, has_selection);
let highlight_symbol = self.highlight_symbol.unwrap_or("");
let blank_symbol = " ".repeat(highlight_symbol.width());
let mut current_height = 0;
@ -401,9 +407,14 @@ impl<'a> StatefulWidget for Table<'a> {
);
let mut col = table_area.left();
if has_selection {
col += (highlight_symbol.width() as u16).min(table_area.width);
col += first_column
}
for (width, cell) in columns_widths.iter().zip(header.cells.iter()) {
for (i, (width, cell)) in columns_widths
.iter()
.step_by(2)
.zip(header.cells.iter())
.enumerate()
{
render_cell(
buf,
cell,
@ -414,7 +425,7 @@ impl<'a> StatefulWidget for Table<'a> {
height: max_header_height,
},
);
col += *width + self.column_spacing;
col += *width + columns_widths.get(i * 2 + 1).unwrap_or(&0);
}
current_height += max_header_height;
rows_height = rows_height.saturating_sub(max_header_height);
@ -450,13 +461,18 @@ impl<'a> StatefulWidget for Table<'a> {
&blank_symbol
};
let (col, _) =
buf.set_stringn(col, row, symbol, table_area.width as usize, table_row.style);
buf.set_stringn(col, row, symbol, first_column as usize, table_row.style);
col
} else {
col
};
let mut col = table_row_start_col;
for (width, cell) in columns_widths.iter().zip(table_row.cells.iter()) {
for (i, (width, cell)) in columns_widths
.iter()
.step_by(2)
.zip(table_row.cells.iter())
.enumerate()
{
render_cell(
buf,
cell,
@ -467,7 +483,7 @@ impl<'a> StatefulWidget for Table<'a> {
height: table_row.height,
},
);
col += *width + self.column_spacing;
col += *width + columns_widths.get(i * 2 + 1).unwrap_or(&0);
}
if is_selected {
buf.set_style(table_row_area, self.highlight_style);
@ -492,14 +508,3 @@ impl<'a> Widget for Table<'a> {
StatefulWidget::render(self, area, buf, &mut state);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn table_invalid_percentages() {
Table::new(vec![]).widths(&[Constraint::Percentage(110)]);
}
}

View file

@ -1,7 +1,7 @@
use tui::{
backend::TestBackend,
buffer::Buffer,
layout::{Constraint, Direction, Layout, Rect},
layout::{Constraint, Direction, Layout, Rect, Unit},
style::{Color, Modifier, Style},
symbols,
text::Span,
@ -18,7 +18,10 @@ fn widgets_gauge_renders() {
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.constraints([
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
])
.split(f.size());
let gauge = Gauge::default()
@ -87,7 +90,10 @@ fn widgets_gauge_renders_no_unicode() {
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.constraints([
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
])
.split(f.size());
let gauge = Gauge::default()

View file

@ -1,7 +1,7 @@
use tui::{
backend::TestBackend,
buffer::Buffer,
layout::Constraint,
layout::{Constraint, Unit},
style::{Color, Modifier, Style},
text::{Span, Spans},
widgets::{Block, Borders, Cell, Row, Table, TableState},
@ -25,11 +25,7 @@ fn widgets_table_column_spacing_can_be_changed() {
])
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL))
.widths(&[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
])
.widths([Constraint::eq(5), Constraint::eq(5), Constraint::eq(5)])
.column_spacing(column_spacing);
f.render_widget(table, size);
})
@ -132,11 +128,7 @@ fn widgets_table_columns_widths_can_use_fixed_length_constraints() {
// columns of zero width show nothing
test_case(
&[
Constraint::Length(0),
Constraint::Length(0),
Constraint::Length(0),
],
vec![Constraint::eq(0), Constraint::eq(0), Constraint::eq(0)],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│ │",
@ -153,11 +145,7 @@ fn widgets_table_columns_widths_can_use_fixed_length_constraints() {
// columns of 1 width trim
test_case(
&[
Constraint::Length(1),
Constraint::Length(1),
Constraint::Length(1),
],
vec![Constraint::eq(1), Constraint::eq(1), Constraint::eq(1)],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│H H H │",
@ -174,11 +162,7 @@ fn widgets_table_columns_widths_can_use_fixed_length_constraints() {
// columns of large width just before pushing a column off
test_case(
&[
Constraint::Length(8),
Constraint::Length(8),
Constraint::Length(8),
],
vec![Constraint::eq(8), Constraint::eq(8), Constraint::eq(8)],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
@ -221,10 +205,10 @@ fn widgets_table_columns_widths_can_use_percentage_constraints() {
// columns of zero width show nothing
test_case(
&[
Constraint::Percentage(0),
Constraint::Percentage(0),
Constraint::Percentage(0),
vec![
Constraint::eq(Unit::Percentage(0)),
Constraint::eq(Unit::Percentage(0)),
Constraint::eq(Unit::Percentage(0)),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
@ -242,10 +226,10 @@ fn widgets_table_columns_widths_can_use_percentage_constraints() {
// columns of not enough width trims the data
test_case(
&[
Constraint::Percentage(11),
Constraint::Percentage(11),
Constraint::Percentage(11),
vec![
Constraint::eq(Unit::Percentage(11)),
Constraint::eq(Unit::Percentage(11)),
Constraint::eq(Unit::Percentage(11)),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
@ -263,10 +247,10 @@ fn widgets_table_columns_widths_can_use_percentage_constraints() {
// columns of large width just before pushing a column off
test_case(
&[
Constraint::Percentage(33),
Constraint::Percentage(33),
Constraint::Percentage(33),
vec![
Constraint::eq(Unit::Percentage(33)),
Constraint::eq(Unit::Percentage(33)),
Constraint::eq(Unit::Percentage(33)),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
@ -284,7 +268,10 @@ fn widgets_table_columns_widths_can_use_percentage_constraints() {
// percentages summing to 100 should give equal widths
test_case(
&[Constraint::Percentage(50), Constraint::Percentage(50)],
vec![
Constraint::eq(Unit::Percentage(50)),
Constraint::eq(Unit::Percentage(50)),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 │",
@ -326,10 +313,10 @@ fn widgets_table_columns_widths_can_use_mixed_constraints() {
// columns of zero width show nothing
test_case(
&[
Constraint::Percentage(0),
Constraint::Length(0),
Constraint::Percentage(0),
vec![
Constraint::eq(Unit::Percentage(0)),
Constraint::eq(0),
Constraint::eq(Unit::Percentage(0)),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
@ -347,10 +334,10 @@ fn widgets_table_columns_widths_can_use_mixed_constraints() {
// columns of not enough width trims the data
test_case(
&[
Constraint::Percentage(11),
Constraint::Length(20),
Constraint::Percentage(11),
vec![
Constraint::eq(Unit::Percentage(11)),
Constraint::eq(20),
Constraint::eq(Unit::Percentage(11)),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
@ -368,10 +355,10 @@ fn widgets_table_columns_widths_can_use_mixed_constraints() {
// columns of large width just before pushing a column off
test_case(
&[
Constraint::Percentage(33),
Constraint::Length(10),
Constraint::Percentage(33),
vec![
Constraint::eq(Unit::Percentage(33)),
Constraint::eq(10),
Constraint::eq(Unit::Percentage(33)),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
@ -389,10 +376,10 @@ fn widgets_table_columns_widths_can_use_mixed_constraints() {
// columns of large size (>100% total) hide the last column
test_case(
&[
Constraint::Percentage(60),
Constraint::Length(10),
Constraint::Percentage(60),
vec![
Constraint::eq(Unit::Percentage(60)),
Constraint::eq(10),
Constraint::eq(Unit::Percentage(60)),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
@ -436,10 +423,10 @@ fn widgets_table_columns_widths_can_use_ratio_constraints() {
// columns of zero width show nothing
test_case(
&[
Constraint::Ratio(0, 1),
Constraint::Ratio(0, 1),
Constraint::Ratio(0, 1),
vec![
Constraint::eq(Unit::Ratio(0, 1)),
Constraint::eq(Unit::Ratio(0, 1)),
Constraint::eq(Unit::Ratio(0, 1)),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
@ -457,10 +444,10 @@ fn widgets_table_columns_widths_can_use_ratio_constraints() {
// columns of not enough width trims the data
test_case(
&[
Constraint::Ratio(1, 9),
Constraint::Ratio(1, 9),
Constraint::Ratio(1, 9),
vec![
Constraint::eq(Unit::Ratio(1, 9)),
Constraint::eq(Unit::Ratio(1, 9)),
Constraint::eq(Unit::Ratio(1, 9)),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
@ -478,10 +465,10 @@ fn widgets_table_columns_widths_can_use_ratio_constraints() {
// columns of large width just before pushing a column off
test_case(
&[
Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3),
vec![
Constraint::eq(Unit::Ratio(1, 3)),
Constraint::eq(Unit::Ratio(1, 3)),
Constraint::eq(Unit::Ratio(1, 3)),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
@ -499,7 +486,10 @@ fn widgets_table_columns_widths_can_use_ratio_constraints() {
// percentages summing to 100 should give equal widths
test_case(
&[Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)],
vec![
Constraint::eq(Unit::Ratio(1, 2)),
Constraint::eq(Unit::Ratio(1, 2)),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 │",
@ -532,11 +522,7 @@ fn widgets_table_can_have_rows_with_multi_lines() {
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL))
.highlight_symbol(">> ")
.widths(&[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
])
.widths([Constraint::eq(5), Constraint::eq(5), Constraint::eq(5)])
.column_spacing(1);
f.render_stateful_widget(table, size, state);
})
@ -635,11 +621,7 @@ fn widgets_table_can_have_elements_styled_individually() {
.block(Block::default().borders(Borders::LEFT | Borders::RIGHT))
.highlight_symbol(">> ")
.highlight_style(Style::default().add_modifier(Modifier::BOLD))
.widths(&[
Constraint::Length(6),
Constraint::Length(6),
Constraint::Length(6),
])
.widths([Constraint::eq(6), Constraint::eq(6), Constraint::eq(6)])
.column_spacing(1);
f.render_stateful_widget(table, size, &mut state);
})
@ -696,11 +678,7 @@ fn widgets_table_should_render_even_if_empty() {
let table = Table::new(vec![])
.header(Row::new(vec!["Head1", "Head2", "Head3"]))
.block(Block::default().borders(Borders::LEFT | Borders::RIGHT))
.widths(&[
Constraint::Length(6),
Constraint::Length(6),
Constraint::Length(6),
])
.widths([Constraint::eq(6), Constraint::eq(6), Constraint::eq(6)])
.column_spacing(1);
f.render_widget(table, size);
})
@ -736,11 +714,11 @@ fn widgets_table_columns_dont_panic() {
.block(Block::default().borders(Borders::ALL))
.highlight_symbol(">> ")
.column_spacing(1)
.widths(&[
Constraint::Percentage(15),
Constraint::Percentage(15),
Constraint::Percentage(25),
Constraint::Percentage(45),
.widths([
Constraint::eq(Unit::Percentage(15)),
Constraint::eq(Unit::Percentage(15)),
Constraint::eq(Unit::Percentage(25)),
Constraint::eq(Unit::Percentage(45)),
]);
let mut state = TableState::default();
@ -771,11 +749,7 @@ fn widgets_table_should_clamp_offset_if_rows_are_removed() {
])
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL))
.widths(&[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
])
.widths([Constraint::eq(5), Constraint::eq(5), Constraint::eq(5)])
.column_spacing(1);
f.render_stateful_widget(table, size, &mut state);
})
@ -800,11 +774,7 @@ fn widgets_table_should_clamp_offset_if_rows_are_removed() {
let table = Table::new(vec![Row::new(vec!["Row31", "Row32", "Row33"])])
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL))
.widths(&[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
])
.widths([Constraint::eq(5), Constraint::eq(5), Constraint::eq(5)])
.column_spacing(1);
f.render_stateful_widget(table, size, &mut state);
})