feat: split layout from rendering

* remove layout logic from Terminal
* replace Group with Layout
* add Frame intermediate object
pull/71/head
Florian Dehau 6 years ago
parent cfc90ab7f6
commit 7181970a32

@ -10,7 +10,7 @@ use termion::event;
use termion::input::TermRead;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::style::{Color, Modifier, Style};
use tui::widgets::{BarChart, Block, Borders, Widget};
use tui::Terminal;
@ -124,41 +124,43 @@ fn main() {
}
fn draw(t: &mut Terminal<MouseBackend>, app: &App) {
Group::default()
.direction(Direction::Vertical)
.margin(2)
.sizes(&[Size::Percent(50), Size::Percent(50)])
.render(t, &app.size, |t, chunks| {
{
let mut f = t.get_frame();
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(&app.size);
BarChart::default()
.block(Block::default().title("Data1").borders(Borders::ALL))
.data(&app.data)
.bar_width(9)
.style(Style::default().fg(Color::Yellow))
.value_style(Style::default().fg(Color::Black).bg(Color::Yellow))
.render(&mut f, &chunks[0]);
{
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(&chunks[1]);
BarChart::default()
.block(Block::default().title("Data1").borders(Borders::ALL))
.block(Block::default().title("Data2").borders(Borders::ALL))
.data(&app.data)
.bar_width(9)
.style(Style::default().fg(Color::Yellow))
.value_style(Style::default().fg(Color::Black).bg(Color::Yellow))
.render(t, &chunks[0]);
Group::default()
.direction(Direction::Horizontal)
.sizes(&[Size::Percent(50), Size::Percent(50)])
.render(t, &chunks[1], |t, chunks| {
BarChart::default()
.block(Block::default().title("Data2").borders(Borders::ALL))
.data(&app.data)
.bar_width(5)
.bar_gap(3)
.style(Style::default().fg(Color::Green))
.value_style(Style::default().bg(Color::Green).modifier(Modifier::Bold))
.render(t, &chunks[0]);
BarChart::default()
.block(Block::default().title("Data3").borders(Borders::ALL))
.data(&app.data)
.style(Style::default().fg(Color::Red))
.bar_width(7)
.bar_gap(0)
.value_style(Style::default().bg(Color::Red))
.label_style(Style::default().fg(Color::Cyan).modifier(Modifier::Italic))
.render(t, &chunks[1]);
})
});
.bar_width(5)
.bar_gap(3)
.style(Style::default().fg(Color::Green))
.value_style(Style::default().bg(Color::Green).modifier(Modifier::Bold))
.render(&mut f, &chunks[0]);
BarChart::default()
.block(Block::default().title("Data3").borders(Borders::ALL))
.data(&app.data)
.style(Style::default().fg(Color::Red))
.bar_width(7)
.bar_gap(0)
.value_style(Style::default().bg(Color::Red))
.label_style(Style::default().fg(Color::Cyan).modifier(Modifier::Italic))
.render(&mut f, &chunks[1]);
}
}
t.draw().unwrap();
}

@ -6,7 +6,7 @@ use termion::event;
use termion::input::TermRead;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::style::{Color, Modifier, Style};
use tui::widgets::{Block, Borders, Widget};
use tui::Terminal;
@ -35,49 +35,52 @@ fn main() {
}
fn draw(t: &mut Terminal<MouseBackend>, size: &Rect) {
// Wrapping block for a group
// Just draw the block and the group on the same area and build the group
// with at least a margin of 1
Block::default().borders(Borders::ALL).render(t, size);
Group::default()
.direction(Direction::Vertical)
.margin(4)
.sizes(&[Size::Percent(50), Size::Percent(50)])
.render(t, size, |t, chunks| {
Group::default()
{
let mut f = t.get_frame();
// Wrapping block for a group
// Just draw the block and the group on the same area and build the group
// with at least a margin of 1
Block::default().borders(Borders::ALL).render(&mut f, size);
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(4)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(size);
{
let chunks = Layout::default()
.direction(Direction::Horizontal)
.sizes(&[Size::Percent(50), Size::Percent(50)])
.render(t, &chunks[0], |t, chunks| {
Block::default()
.title("With background")
.title_style(Style::default().fg(Color::Yellow))
.style(Style::default().bg(Color::Green))
.render(t, &chunks[0]);
Block::default()
.title("Styled title")
.title_style(
Style::default()
.fg(Color::White)
.bg(Color::Red)
.modifier(Modifier::Bold),
)
.render(t, &chunks[1]);
});
Group::default()
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(&chunks[0]);
Block::default()
.title("With background")
.title_style(Style::default().fg(Color::Yellow))
.style(Style::default().bg(Color::Green))
.render(&mut f, &chunks[0]);
Block::default()
.title("Styled title")
.title_style(
Style::default()
.fg(Color::White)
.bg(Color::Red)
.modifier(Modifier::Bold),
)
.render(&mut f, &chunks[1]);
}
{
let chunks = Layout::default()
.direction(Direction::Horizontal)
.sizes(&[Size::Percent(50), Size::Percent(50)])
.render(t, &chunks[1], |t, chunks| {
Block::default()
.title("With borders")
.borders(Borders::ALL)
.render(t, &chunks[0]);
Block::default()
.title("With styled borders")
.border_style(Style::default().fg(Color::Cyan))
.borders(Borders::LEFT | Borders::RIGHT)
.render(t, &chunks[1]);
});
});
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(&chunks[1]);
Block::default()
.title("With borders")
.borders(Borders::ALL)
.render(&mut f, &chunks[0]);
Block::default()
.title("With styled borders")
.border_style(Style::default().fg(Color::Cyan))
.borders(Borders::LEFT | Borders::RIGHT)
.render(&mut f, &chunks[1]);
}
}
t.draw().unwrap();
}

@ -10,7 +10,7 @@ use termion::event;
use termion::input::TermRead;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::style::Color;
use tui::widgets::canvas::{Canvas, Line, Map, MapResolution};
use tui::widgets::{Block, Borders, Widget};
@ -148,58 +148,60 @@ fn main() {
}
fn draw(t: &mut Terminal<MouseBackend>, app: &App) {
Group::default()
.direction(Direction::Horizontal)
.sizes(&[Size::Percent(50), Size::Percent(50)])
.render(t, &app.size, |t, chunks| {
Canvas::default()
.block(Block::default().borders(Borders::ALL).title("World"))
.paint(|ctx| {
ctx.draw(&Map {
color: Color::White,
resolution: MapResolution::High,
});
ctx.print(app.x, -app.y, "You are here", Color::Yellow);
})
.x_bounds([-180.0, 180.0])
.y_bounds([-90.0, 90.0])
.render(t, &chunks[0]);
Canvas::default()
.block(Block::default().borders(Borders::ALL).title("List"))
.paint(|ctx| {
ctx.draw(&Line {
x1: f64::from(app.ball.left()),
y1: f64::from(app.ball.top()),
x2: f64::from(app.ball.right()),
y2: f64::from(app.ball.top()),
color: Color::Yellow,
});
ctx.draw(&Line {
x1: f64::from(app.ball.right()),
y1: f64::from(app.ball.top()),
x2: f64::from(app.ball.right()),
y2: f64::from(app.ball.bottom()),
color: Color::Yellow,
});
ctx.draw(&Line {
x1: f64::from(app.ball.right()),
y1: f64::from(app.ball.bottom()),
x2: f64::from(app.ball.left()),
y2: f64::from(app.ball.bottom()),
color: Color::Yellow,
});
ctx.draw(&Line {
x1: f64::from(app.ball.left()),
y1: f64::from(app.ball.bottom()),
x2: f64::from(app.ball.left()),
y2: f64::from(app.ball.top()),
color: Color::Yellow,
});
})
.x_bounds([10.0, 110.0])
.y_bounds([10.0, 110.0])
.render(t, &chunks[1]);
});
{
let mut f = t.get_frame();
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(&app.size);
Canvas::default()
.block(Block::default().borders(Borders::ALL).title("World"))
.paint(|ctx| {
ctx.draw(&Map {
color: Color::White,
resolution: MapResolution::High,
});
ctx.print(app.x, -app.y, "You are here", Color::Yellow);
})
.x_bounds([-180.0, 180.0])
.y_bounds([-90.0, 90.0])
.render(&mut f, &chunks[0]);
Canvas::default()
.block(Block::default().borders(Borders::ALL).title("List"))
.paint(|ctx| {
ctx.draw(&Line {
x1: f64::from(app.ball.left()),
y1: f64::from(app.ball.top()),
x2: f64::from(app.ball.right()),
y2: f64::from(app.ball.top()),
color: Color::Yellow,
});
ctx.draw(&Line {
x1: f64::from(app.ball.right()),
y1: f64::from(app.ball.top()),
x2: f64::from(app.ball.right()),
y2: f64::from(app.ball.bottom()),
color: Color::Yellow,
});
ctx.draw(&Line {
x1: f64::from(app.ball.right()),
y1: f64::from(app.ball.bottom()),
x2: f64::from(app.ball.left()),
y2: f64::from(app.ball.bottom()),
color: Color::Yellow,
});
ctx.draw(&Line {
x1: f64::from(app.ball.left()),
y1: f64::from(app.ball.bottom()),
x2: f64::from(app.ball.left()),
y2: f64::from(app.ball.top()),
color: Color::Yellow,
});
})
.x_bounds([10.0, 110.0])
.y_bounds([10.0, 110.0])
.render(&mut f, &chunks[1]);
}
t.draw().unwrap();
}

@ -122,46 +122,49 @@ fn main() {
}
fn draw(t: &mut Terminal<MouseBackend>, app: &App) {
Chart::default()
.block(
Block::default()
{
let mut f = t.get_frame();
Chart::default()
.block(
Block::default()
.title("Chart")
.title_style(Style::default().fg(Color::Cyan).modifier(Modifier::Bold))
.borders(Borders::ALL),
)
.x_axis(
Axis::default()
)
.x_axis(
Axis::default()
.title("X Axis")
.style(Style::default().fg(Color::Gray))
.labels_style(Style::default().modifier(Modifier::Italic))
.bounds(app.window)
.labels(&[
&format!("{}", app.window[0]),
&format!("{}", (app.window[0] + app.window[1]) / 2.0),
&format!("{}", app.window[1]),
&format!("{}", app.window[0]),
&format!("{}", (app.window[0] + app.window[1]) / 2.0),
&format!("{}", app.window[1]),
]),
)
.y_axis(
Axis::default()
)
.y_axis(
Axis::default()
.title("Y Axis")
.style(Style::default().fg(Color::Gray))
.labels_style(Style::default().modifier(Modifier::Italic))
.bounds([-20.0, 20.0])
.labels(&["-20", "0", "20"]),
)
.datasets(&[
Dataset::default()
.name("data2")
.marker(Marker::Dot)
.style(Style::default().fg(Color::Cyan))
.data(&app.data1),
Dataset::default()
.name("data3")
.marker(Marker::Braille)
.style(Style::default().fg(Color::Yellow))
.data(&app.data2),
])
.render(t, &app.size);
)
.datasets(&[
Dataset::default()
.name("data2")
.marker(Marker::Dot)
.style(Style::default().fg(Color::Cyan))
.data(&app.data1),
Dataset::default()
.name("data3")
.marker(Marker::Braille)
.style(Style::default().fg(Color::Yellow))
.data(&app.data2),
])
.render(&mut f, &app.size);
}
t.draw().unwrap();
}

@ -34,6 +34,6 @@ fn main() {
let mut terminal = Terminal::new(MouseBackend::new().unwrap()).unwrap();
let size = terminal.size().unwrap();
terminal.clear().unwrap();
Label::default().text("Test").render(&mut terminal, &size);
Label::default().text("Test").render(&mut terminal.get_frame(), &size);
terminal.draw().unwrap();
}

@ -15,12 +15,14 @@ use termion::event;
use termion::input::TermRead;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::style::{Color, Modifier, Style};
use tui::widgets::canvas::{Canvas, Line, Map, MapResolution};
use tui::widgets::{Axis, BarChart, Block, Borders, Chart, Dataset, Gauge, Item, List, Marker,
Paragraph, Row, SelectableList, Sparkline, Table, Tabs, Widget};
use tui::Terminal;
use tui::widgets::{
Axis, BarChart, Block, Borders, Chart, Dataset, Gauge, Item, List, Marker, Paragraph, Row,
SelectableList, Sparkline, Table, Tabs, Widget,
};
use tui::{Frame, Terminal};
use util::*;
@ -266,178 +268,178 @@ fn main() {
}
fn draw(t: &mut Terminal<MouseBackend>, app: &App) -> Result<(), io::Error> {
Group::default()
.direction(Direction::Vertical)
.sizes(&[Size::Fixed(3), Size::Min(0)])
.render(t, &app.size, |t, chunks| {
Tabs::default()
.block(Block::default().borders(Borders::ALL).title("Tabs"))
.titles(&app.tabs.titles)
.style(Style::default().fg(Color::Green))
.highlight_style(Style::default().fg(Color::Yellow))
.select(app.tabs.selection)
.render(t, &chunks[0]);
match app.tabs.selection {
0 => {
draw_first_tab(t, app, &chunks[1]);
}
1 => {
draw_second_tab(t, app, &chunks[1]);
}
_ => {}
};
});
try!(t.draw());
Ok(())
{
let mut f = t.get_frame();
let chunks = Layout::default()
.constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
.split(&app.size);
Tabs::default()
.block(Block::default().borders(Borders::ALL).title("Tabs"))
.titles(&app.tabs.titles)
.style(Style::default().fg(Color::Green))
.highlight_style(Style::default().fg(Color::Yellow))
.select(app.tabs.selection)
.render(&mut f, &chunks[0]);
match app.tabs.selection {
0 => {
draw_first_tab(&mut f, app, &chunks[1]);
}
1 => {
draw_second_tab(&mut f, app, &chunks[1]);
}
_ => {}
};
}
t.draw()
}
fn draw_first_tab(t: &mut Terminal<MouseBackend>, app: &App, area: &Rect) {
Group::default()
.direction(Direction::Vertical)
.sizes(&[Size::Fixed(7), Size::Min(7), Size::Fixed(7)])
.render(t, area, |t, chunks| {
draw_gauges(t, app, &chunks[0]);
draw_charts(t, app, &chunks[1]);
draw_text(t, &chunks[2]);
});
fn draw_first_tab(f: &mut Frame<MouseBackend>, app: &App, area: &Rect) {
let chunks = Layout::default()
.constraints(
[
Constraint::Length(7),
Constraint::Min(7),
Constraint::Length(7),
].as_ref(),
)
.split(area);
draw_gauges(f, app, &chunks[0]);
draw_charts(f, app, &chunks[1]);
draw_text(f, &chunks[2]);
}
fn draw_gauges(t: &mut Terminal<MouseBackend>, app: &App, area: &Rect) {
fn draw_gauges(f: &mut Frame<MouseBackend>, app: &App, area: &Rect) {
let chunks = Layout::default()
.constraints([Constraint::Length(2), Constraint::Length(3)].as_ref())
.margin(1)
.split(&area);
Block::default()
.borders(Borders::ALL)
.title("Graphs")
.render(t, area);
Group::default()
.direction(Direction::Vertical)
.margin(1)
.sizes(&[Size::Fixed(2), Size::Fixed(3)])
.render(t, area, |t, chunks| {
Gauge::default()
.block(Block::default().title("Gauge:"))
.style(
Style::default()
.fg(Color::Magenta)
.bg(Color::Black)
.modifier(Modifier::Italic),
)
.label(&format!("{} / 100", app.progress))
.percent(app.progress)
.render(t, &chunks[0]);
Sparkline::default()
.block(Block::default().title("Sparkline:"))
.style(Style::default().fg(Color::Green))
.data(&app.data)
.render(t, &chunks[1]);
});
.render(f, area);
Gauge::default()
.block(Block::default().title("Gauge:"))
.style(
Style::default()
.fg(Color::Magenta)
.bg(Color::Black)
.modifier(Modifier::Italic),
)
.label(&format!("{} / 100", app.progress))
.percent(app.progress)
.render(f, &chunks[0]);
Sparkline::default()
.block(Block::default().title("Sparkline:"))
.style(Style::default().fg(Color::Green))
.data(&app.data)
.render(f, &chunks[1]);
}
fn draw_charts(t: &mut Terminal<MouseBackend>, app: &App, area: &Rect) {
let sizes = if app.show_chart {
vec![Size::Percent(50), Size::Percent(50)]
fn draw_charts(f: &mut Frame<MouseBackend>, app: &App, area: &Rect) {
let constraints = if app.show_chart {
vec![Constraint::Percentage(50), Constraint::Percentage(50)]
} else {
vec![Size::Percent(100)]
vec![Constraint::Percentage(100)]
};
Group::default()
let chunks = Layout::default()
.constraints(constraints)
.direction(Direction::Horizontal)
.sizes(&sizes)
.render(t, area, |t, chunks| {
Group::default()
.direction(Direction::Vertical)
.sizes(&[Size::Percent(50), Size::Percent(50)])
.render(t, &chunks[0], |t, chunks| {
Group::default()
.direction(Direction::Horizontal)
.sizes(&[Size::Percent(50), Size::Percent(50)])
.render(t, &chunks[0], |t, chunks| {
SelectableList::default()
.block(Block::default().borders(Borders::ALL).title("List"))
.items(&app.items)
.select(app.selected)
.highlight_style(
Style::default().fg(Color::Yellow).modifier(Modifier::Bold),
)
.highlight_symbol(">")
.render(t, &chunks[0]);
let info_style = Style::default().fg(Color::White);
let warning_style = Style::default().fg(Color::Yellow);
let error_style = Style::default().fg(Color::Magenta);
let critical_style = Style::default().fg(Color::Red);
let events = app.events.iter().map(|&(evt, level)| {
Item::StyledData(
format!("{}: {}", level, evt),
match level {
"ERROR" => &error_style,
"CRITICAL" => &critical_style,
"WARNING" => &warning_style,
_ => &info_style,
},
)
});
List::new(events)
.block(Block::default().borders(Borders::ALL).title("List"))
.render(t, &chunks[1]);
});
BarChart::default()
.block(Block::default().borders(Borders::ALL).title("Bar chart"))
.data(&app.data4)
.bar_width(3)
.bar_gap(2)
.value_style(
Style::default()
.fg(Color::Black)
.bg(Color::Green)
.modifier(Modifier::Italic),
)
.label_style(Style::default().fg(Color::Yellow))
.style(Style::default().fg(Color::Green))
.render(t, &chunks[1]);
});
if app.show_chart {
Chart::default()
.block(
Block::default()
.title("Chart")
.title_style(Style::default().fg(Color::Cyan).modifier(Modifier::Bold))
.borders(Borders::ALL),
)
.x_axis(
Axis::default()
.title("X Axis")
.style(Style::default().fg(Color::Gray))
.labels_style(Style::default().modifier(Modifier::Italic))
.bounds(app.window)
.labels(&[
&format!("{}", app.window[0]),
&format!("{}", (app.window[0] + app.window[1]) / 2.0),
&format!("{}", app.window[1]),
]),
)
.y_axis(
Axis::default()
.title("Y Axis")
.style(Style::default().fg(Color::Gray))
.labels_style(Style::default().modifier(Modifier::Italic))
.bounds([-20.0, 20.0])
.labels(&["-20", "0", "20"]),
)
.datasets(&[
Dataset::default()
.name("data2")
.marker(Marker::Dot)
.style(Style::default().fg(Color::Cyan))
.data(&app.data2),
Dataset::default()
.name("data3")
.marker(Marker::Braille)
.style(Style::default().fg(Color::Yellow))
.data(&app.data3),
])
.render(t, &chunks[1]);
}
});
.split(&area);
{
let chunks = Layout::default()
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(&chunks[0]);
{
let chunks = Layout::default()
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.direction(Direction::Horizontal)
.split(&chunks[0]);
SelectableList::default()
.block(Block::default().borders(Borders::ALL).title("List"))
.items(&app.items)
.select(app.selected)
.highlight_style(Style::default().fg(Color::Yellow).modifier(Modifier::Bold))
.highlight_symbol(">")
.render(f, &chunks[0]);
let info_style = Style::default().fg(Color::White);
let warning_style = Style::default().fg(Color::Yellow);
let error_style = Style::default().fg(Color::Magenta);
let critical_style = Style::default().fg(Color::Red);
let events = app.events.iter().map(|&(evt, level)| {
Item::StyledData(
format!("{}: {}", level, evt),
match level {
"ERROR" => &error_style,
"CRITICAL" => &critical_style,
"WARNING" => &warning_style,
_ => &info_style,
},
)
});
List::new(events)
.block(Block::default().borders(Borders::ALL).title("List"))
.render(f, &chunks[1]);
}
BarChart::default()
.block(Block::default().borders(Borders::ALL).title("Bar chart"))
.data(&app.data4)
.bar_width(3)
.bar_gap(2)
.value_style(
Style::default()
.fg(Color::Black)
.bg(Color::Green)
.modifier(Modifier::Italic),
)
.label_style(Style::default().fg(Color::Yellow))
.style(Style::default().fg(Color::Green))
.render(f, &chunks[1]);
}
if app.show_chart {
Chart::default()
.block(
Block::default()
.title("Chart")
.title_style(Style::default().fg(Color::Cyan).modifier(Modifier::Bold))
.borders(Borders::ALL),
)
.x_axis(
Axis::default()
.title("X Axis")
.style(Style::default().fg(Color::Gray))
.labels_style(Style::default().modifier(Modifier::Italic))
.bounds(app.window)
.labels(&[
&format!("{}", app.window[0]),
&format!("{}", (app.window[0] + app.window[1]) / 2.0),
&format!("{}", app.window[1]),
]),
)
.y_axis(
Axis::default()
.title("Y Axis")
.style(Style::default().fg(Color::Gray))
.labels_style(Style::default().modifier(Modifier::Italic))
.bounds([-20.0, 20.0])
.labels(&["-20", "0", "20"]),
)
.datasets(&[
Dataset::default()
.name("data2")
.marker(Marker::Dot)
.style(Style::default().fg(Color::Cyan))
.data(&app.data2),
Dataset::default()
.name("data3")
.marker(Marker::Braille)
.style(Style::default().fg(Color::Yellow))
.data(&app.data3),
])
.render(f, &chunks[1]);
}
}
fn draw_text(t: &mut Terminal<MouseBackend>, area: &Rect) {
fn draw_text(f: &mut Frame<MouseBackend>, area: &Rect) {
Paragraph::default()
.block(
Block::default()
@ -457,61 +459,60 @@ fn draw_text(t: &mut Terminal<MouseBackend>, area: &Rect) {
it should display unicode characters properly: , ٩(-̮̮̃-̃)۶ ٩(̮̮̃̃)۶ ٩(̯͡͡)۶ \
٩(-̮̮̃̃).",
)
.render(t, area);
.render(f, area);
}
fn draw_second_tab(t: &mut Terminal<MouseBackend>, app: &App, area: &Rect) {
Group::default()
fn draw_second_tab(f: &mut Frame<MouseBackend>, app: &App, area: &Rect) {
let chunks = Layout::default()
.constraints([Constraint::Percentage(30), Constraint::Percentage(70)].as_ref())
.direction(Direction::Horizontal)
.sizes(&[Size::Percent(30), Size::Percent(70)])
.render(t, area, |t, chunks| {
let up_style = Style::default().fg(Color::Green);
let failure_style = Style::default().fg(Color::Red);
Table::new(
["Server", "Location", "Status"].into_iter(),
app.servers.iter().map(|s| {
let style = if s.status == "Up" {
&up_style
} else {
&failure_style
};
Row::StyledData(vec![s.name, s.location, s.status].into_iter(), style)
}),
).block(Block::default().title("Servers").borders(Borders::ALL))
.header_style(Style::default().fg(Color::Yellow))
.widths(&[15, 15, 10])
.render(t, &chunks[0]);
.split(area);
let up_style = Style::default().fg(Color::Green);
let failure_style = Style::default().fg(Color::Red);
Table::new(
["Server", "Location", "Status"].into_iter(),
app.servers.iter().map(|s| {
let style = if s.status == "Up" {
&up_style
} else {
&failure_style
};
Row::StyledData(vec![s.name, s.location, s.status].into_iter(), style)
}),
).block(Block::default().title("Servers").borders(Borders::ALL))
.header_style(Style::default().fg(Color::Yellow))
.widths(&[15, 15, 10])
.render(f, &chunks[0]);
Canvas::default()
.block(Block::default().title("World").borders(Borders::ALL))
.paint(|ctx| {
ctx.draw(&Map {
color: Color::White,
resolution: MapResolution::High,
Canvas::default()
.block(Block::default().title("World").borders(Borders::ALL))
.paint(|ctx| {
ctx.draw(&Map {
color: Color::White,
resolution: MapResolution::High,
});
ctx.layer();
for (i, s1) in app.servers.iter().enumerate() {
for s2 in &app.servers[i + 1..] {
ctx.draw(&Line {
x1: s1.coords.1,
y1: s1.coords.0,
y2: s2.coords.0,
x2: s2.coords.1,
color: Color::Yellow,
});
ctx.layer();
for (i, s1) in app.servers.iter().enumerate() {
for s2 in &app.servers[i + 1..] {
ctx.draw(&Line {
x1: s1.coords.1,
y1: s1.coords.0,
y2: s2.coords.0,
x2: s2.coords.1,
color: Color::Yellow,
});
}
}
for server in &app.servers {
let color = if server.status == "Up" {
Color::Green
} else {
Color::Red
};
ctx.print(server.coords.1, server.coords.0, "X", color);
}
})
.x_bounds([-180.0, 180.0])
.y_bounds([-90.0, 90.0])
.render(t, &chunks[1]);
}
}
for server in &app.servers {
let color = if server.status == "Up" {
Color::Green
} else {
Color::Red
};
ctx.print(server.coords.1, server.coords.0, "X", color);
}
})
.x_bounds([-180.0, 180.0])
.y_bounds([-90.0, 90.0])
.render(f, &chunks[1]);
}

@ -10,7 +10,7 @@ use termion::event;
use termion::input::TermRead;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::style::{Color, Modifier, Style};
use tui::widgets::{Block, Borders, Gauge, Widget};
use tui::Terminal;
@ -119,39 +119,43 @@ fn main() {
}
fn draw(t: &mut Terminal<MouseBackend>, app: &App) {
Group::default()
.direction(Direction::Vertical)
.margin(2)
.sizes(&[
Size::Percent(25),
Size::Percent(25),
Size::Percent(25),
Size::Percent(25),
])
.render(t, &app.size, |t, chunks| {
Gauge::default()
.block(Block::default().title("Gauge1").borders(Borders::ALL))
.style(Style::default().fg(Color::Yellow))
.percent(app.progress1)
.render(t, &chunks[0]);
Gauge::default()
.block(Block::default().title("Gauge2").borders(Borders::ALL))
.style(Style::default().fg(Color::Magenta).bg(Color::Green))
.percent(app.progress2)
.label(&format!("{}/100", app.progress2))
.render(t, &chunks[1]);
Gauge::default()
.block(Block::default().title("Gauge2").borders(Borders::ALL))
.style(Style::default().fg(Color::Yellow))
.percent(app.progress3)
.render(t, &chunks[2]);
Gauge::default()
.block(Block::default().title("Gauge3").borders(Borders::ALL))
.style(Style::default().fg(Color::Cyan).modifier(Modifier::Italic))
.percent(app.progress4)
.label(&format!("{}/100", app.progress2))
.render(t, &chunks[3]);
});
{
let mut f = t.get_frame();
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints(
[
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Percentage(25),
].as_ref(),
)
.split(&app.size);
Gauge::default()
.block(Block::default().title("Gauge1").borders(Borders::ALL))
.style(Style::default().fg(Color::Yellow))
.percent(app.progress1)
.render(&mut f, &chunks[0]);
Gauge::default()
.block(Block::default().title("Gauge2").borders(Borders::ALL))
.style(Style::default().fg(Color::Magenta).bg(Color::Green))
.percent(app.progress2)
.label(&format!("{}/100", app.progress2))
.render(&mut f, &chunks[1]);
Gauge::default()
.block(Block::default().title("Gauge2").borders(Borders::ALL))
.style(Style::default().fg(Color::Yellow))
.percent(app.progress3)
.render(&mut f, &chunks[2]);
Gauge::default()
.block(Block::default().title("Gauge3").borders(Borders::ALL))
.style(Style::default().fg(Color::Cyan).modifier(Modifier::Italic))
.percent(app.progress4)
.label(&format!("{}/100", app.progress2))
.render(&mut f, &chunks[3]);
}
t.draw().unwrap();
}

@ -11,7 +11,7 @@ use termion::event;
use termion::input::TermRead;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::widgets::{Block, Borders, Widget};
use tui::Terminal;
@ -86,19 +86,28 @@ fn main() {
}
fn draw(t: &mut Terminal<MouseBackend>, app: &App) {
Group::default()
.direction(Direction::Vertical)
.sizes(&[Size::Percent(10), Size::Percent(80), Size::Percent(10)])
.render(t, &app.size, |t, chunks| {
Block::default()
.title("Block")
.borders(Borders::ALL)
.render(t, &chunks[0]);
Block::default()
.title("Block 2")
.borders(Borders::ALL)
.render(t, &chunks[2]);
});
{
let mut f = t.get_frame();
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage(10),
Constraint::Percentage(80),
Constraint::Percentage(10),
].as_ref(),
)
.split(&app.size);
Block::default()
.title("Block")
.borders(Borders::ALL)
.render(&mut f, &chunks[0]);
Block::default()
.title("Block 2")
.borders(Borders::ALL)
.render(&mut f, &chunks[2]);
}
t.draw().unwrap();
}

@ -10,7 +10,7 @@ use termion::event;
use termion::input::TermRead;
use tui::backend::MouseBackend;
use tui::layout::{Corner, Direction, Group, Rect, Size};
use tui::layout::{Constraint, Corner, Direction, Rect, Layout};
use tui::style::{Color, Modifier, Style};
use tui::widgets::{Block, Borders, Item, List, SelectableList, Widget};
use tui::Terminal;
@ -156,37 +156,39 @@ fn main() {
}
fn draw(t: &mut Terminal<MouseBackend>, app: &App) {
Group::default()
.direction(Direction::Horizontal)
.sizes(&[Size::Percent(50), Size::Percent(50)])
.render(t, &app.size, |t, chunks| {
let style = Style::default().fg(Color::Black).bg(Color::White);
SelectableList::default()
.block(Block::default().borders(Borders::ALL).title("List"))
.items(&app.items)
.select(app.selected)
.style(style)
.highlight_style(style.clone().fg(Color::LightGreen).modifier(Modifier::Bold))
.highlight_symbol(">")
.render(t, &chunks[0]);
{
let events = app.events.iter().map(|&(evt, level)| {
Item::StyledData(
format!("{}: {}", level, evt),
match level {
"ERROR" => &app.error_style,
"CRITICAL" => &app.critical_style,
"WARNING" => &app.warning_style,
_ => &app.info_style,
},
{
let mut f = t.get_frame();
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(&app.size);
let style = Style::default().fg(Color::Black).bg(Color::White);
SelectableList::default()
.block(Block::default().borders(Borders::ALL).title("List"))
.items(&app.items)
.select(app.selected)
.style(style)
.highlight_style(style.clone().fg(Color::LightGreen).modifier(Modifier::Bold))
.highlight_symbol(">")
.render(&mut f, &chunks[0]);
{
let events = app.events.iter().map(|&(evt, level)| {
Item::StyledData(
format!("{}: {}", level, evt),
match level {
"ERROR" => &app.error_style,
"CRITICAL" => &app.critical_style,
"WARNING" => &app.warning_style,
_ => &app.info_style,
},
)
});
List::new(events)
.block(Block::default().borders(Borders::ALL).title("List"))
.start_corner(Corner::BottomLeft)
.render(t, &chunks[1]);
}
});
});
List::new(events)
.block(Block::default().borders(Borders::ALL).title("List"))
.start_corner(Corner::BottomLeft)
.render(&mut f, &chunks[1]);
}
}
t.draw().unwrap();
}

@ -6,7 +6,7 @@ use termion::event;
use termion::input::TermRead;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::style::{Alignment, Color, Style};
use tui::widgets::{Block, Paragraph, Widget};
use tui::Terminal;
@ -37,63 +37,57 @@ fn main() {
}
fn draw(t: &mut Terminal<MouseBackend>, size: &Rect) {
Block::default()
.style(Style::default().bg(Color::White))
.render(t, size);
{
let mut f = t.get_frame();
Block::default()
.style(Style::default().bg(Color::White))
.render(&mut f, size);
Group::default()
.direction(Direction::Vertical)
.margin(5)
.sizes(&[Size::Percent(30), Size::Percent(30), Size::Percent(30)])
.render(t, size, |t, chunks| {
Group::default()
.direction(Direction::Horizontal)
.sizes(&[Size::Percent(100)])
.render(t, &chunks[0], |t, chunks| {
Paragraph::default()
.alignment(Alignment::Left)
.text(
"This is a line\n{fg=red This is a line}\n{bg=red This is a \
line}\n{mod=italic This is a line}\n{mod=bold This is a \
line}\n{mod=crossed_out This is a line}\n{mod=invert This is a \
line}\n{mod=underline This is a \
line}\n{bg=green;fg=yellow;mod=italic This is a line}\n",
)
.render(t, &chunks[0]);
});
Group::default()
.direction(Direction::Horizontal)
.sizes(&[Size::Percent(100)])
.render(t, &chunks[1], |t, chunks| {
Paragraph::default()
.alignment(Alignment::Center)
.wrap(true)
.text(
"This is a line\n{fg=red This is a line}\n{bg=red This is a \
line}\n{mod=italic This is a line}\n{mod=bold This is a \
line}\n{mod=crossed_out This is a line}\n{mod=invert This is a \
line}\n{mod=underline This is a \
line}\n{bg=green;fg=yellow;mod=italic This is a line}\n",
)
.render(t, &chunks[0]);
});
Group::default()
.direction(Direction::Horizontal)
.sizes(&[Size::Percent(100)])
.render(t, &chunks[2], |t, chunks| {
Paragraph::default()
.alignment(Alignment::Right)
.wrap(true)
.text(
"This is a line\n{fg=red This is a line}\n{bg=red This is a \
line}\n{mod=italic This is a line}\n{mod=bold This is a \
line}\n{mod=crossed_out This is a line}\n{mod=invert This is a \
line}\n{mod=underline This is a \
line}\n{bg=green;fg=yellow;mod=italic This is a line}\n",
)
.render(t, &chunks[0]);
});
});
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(5)
.constraints(
[
Constraint::Percentage(30),
Constraint::Percentage(30),
Constraint::Percentage(30),
].as_ref(),
)
.split(size);
Paragraph::default()
.alignment(Alignment::Left)
.text(
"This is a line\n{fg=red This is a line}\n{bg=red This is a \
line}\n{mod=italic This is a line}\n{mod=bold This is a \
line}\n{mod=crossed_out This is a line}\n{mod=invert This is a \
line}\n{mod=underline This is a \
line}\n{bg=green;fg=yellow;mod=italic This is a line}\n",
)
.render(&mut f, &chunks[0]);
Paragraph::default()
.alignment(Alignment::Center)
.wrap(true)
.text(
"This is a line\n{fg=red This is a line}\n{bg=red This is a \
line}\n{mod=italic This is a line}\n{mod=bold This is a \
line}\n{mod=crossed_out This is a line}\n{mod=invert This is a \
line}\n{mod=underline This is a \
line}\n{bg=green;fg=yellow;mod=italic This is a line}\n",
)
.render(&mut f, &chunks[1]);
Paragraph::default()
.alignment(Alignment::Right)
.wrap(true)
.text(
"This is a line\n{fg=red This is a line}\n{bg=red This is a \
line}\n{mod=italic This is a line}\n{mod=bold This is a \
line}\n{mod=crossed_out This is a line}\n{mod=invert This is a \
line}\n{mod=underline This is a \
line}\n{bg=green;fg=yellow;mod=italic This is a line}\n",
)
.render(&mut f, &chunks[2]);
}
t.draw().unwrap();
}

@ -5,7 +5,7 @@ use rustbox::Key;
use std::error::Error;
use tui::backend::RustboxBackend;
use tui::layout::{Direction, Group, Size};
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Modifier, Style};
use tui::widgets::{Block, Borders, Paragraph, Widget};
use tui::Terminal;
@ -30,22 +30,19 @@ fn main() {
fn draw(t: &mut Terminal<RustboxBackend>) {
let size = t.size().unwrap();
Group::default()
.direction(Direction::Vertical)
.sizes(&[Size::Percent(100)])
.render(t, &size, |t, chunks| {
Paragraph::default()
.block(
Block::default()
.title("Rustbox backend")
.title_style(Style::default().fg(Color::Yellow).modifier(Modifier::Bold))
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::Magenta)),
{
let mut f = t.get_frame();
Paragraph::default()
.block(
Block::default()
.title("Rustbox backend")
.title_style(Style::default().fg(Color::Yellow).modifier(Modifier::Bold))
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::Magenta)),
)
.text("It {yellow works}!")
.render(t, &chunks[0]);
});
.text("It {yellow works}!")
.render(&mut f, &size);
}
t.draw().unwrap();
}

@ -13,7 +13,7 @@ use termion::event;
use termion::input::TermRead;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::style::{Color, Style};
use tui::widgets::{Block, Borders, Sparkline, Widget};
use tui::Terminal;
@ -119,40 +119,48 @@ fn main() {
}
fn draw(t: &mut Terminal<MouseBackend>, app: &App) {
Group::default()
.direction(Direction::Vertical)
.margin(2)
.sizes(&[Size::Fixed(3), Size::Fixed(3), Size::Fixed(7), Size::Min(0)])
.render(t, &app.size, |t, chunks| {
Sparkline::default()
.block(
Block::default()
.title("Data1")
.borders(Borders::LEFT | Borders::RIGHT),
)
.data(&app.data1)
.style(Style::default().fg(Color::Yellow))
.render(t, &chunks[0]);
Sparkline::default()
.block(
Block::default()
.title("Data2")
.borders(Borders::LEFT | Borders::RIGHT),
)
.data(&app.data2)
.style(Style::default().bg(Color::Green))
.render(t, &chunks[1]);
// Multiline
Sparkline::default()
.block(
Block::default()
.title("Data3")
.borders(Borders::LEFT | Borders::RIGHT),
)
.data(&app.data3)
.style(Style::default().fg(Color::Red))
.render(t, &chunks[2]);
});
{
let mut f = t.get_frame();
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints(
[
Constraint::Length(3),
Constraint::Length(3),
Constraint::Length(7),
Constraint::Min(0),
].as_ref(),
)
.split(&app.size);
Sparkline::default()
.block(
Block::default()
.title("Data1")
.borders(Borders::LEFT | Borders::RIGHT),
)
.data(&app.data1)
.style(Style::default().fg(Color::Yellow))
.render(&mut f, &chunks[0]);
Sparkline::default()
.block(
Block::default()
.title("Data2")
.borders(Borders::LEFT | Borders::RIGHT),
)
.data(&app.data2)
.style(Style::default().bg(Color::Green))
.render(&mut f, &chunks[1]);
// Multiline
Sparkline::default()
.block(
Block::default()
.title("Data3")
.borders(Borders::LEFT | Borders::RIGHT),
)
.data(&app.data3)
.style(Style::default().fg(Color::Red))
.render(&mut f, &chunks[2]);
}
t.draw().unwrap();
}

@ -7,7 +7,7 @@ use termion::event;
use termion::input::TermRead;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::layout::{Constraint, Layout, Rect};
use tui::style::{Color, Modifier, Style};
use tui::widgets::{Block, Borders, Row, Table, Widget};
use tui::Terminal;
@ -47,7 +47,7 @@ fn main() {
terminal.clear().unwrap();
terminal.hide_cursor().unwrap();
app.size = terminal.size().unwrap();
draw(&mut terminal, &app);
draw(&mut terminal, &app).unwrap();
// Input
let stdin = io::stdin();
@ -76,34 +76,35 @@ fn main() {
},
_ => {}
};
draw(&mut terminal, &app);
draw(&mut terminal, &app).unwrap();
}
terminal.show_cursor().unwrap();
terminal.clear().unwrap();
}
fn draw(t: &mut Terminal<MouseBackend>, app: &App) {
let selected_style = Style::default().fg(Color::Yellow).modifier(Modifier::Bold);
let normal_style = Style::default().fg(Color::White);
let header = ["Header1", "Header2", "Header3"];
let rows = app.items.iter().enumerate().map(|(i, item)| {
if i == app.selected {
Row::StyledData(item.into_iter(), &selected_style)
} else {
Row::StyledData(item.into_iter(), &normal_style)
}
});
Group::default()
.direction(Direction::Horizontal)
.sizes(&[Size::Percent(100)])
.margin(5)
.render(t, &app.size, |t, chunks| {
Table::new(header.into_iter(), rows)
.block(Block::default().borders(Borders::ALL).title("Table"))
.widths(&[10, 10, 10])
.render(t, &chunks[0]);
fn draw(t: &mut Terminal<MouseBackend>, app: &App) -> Result<(), io::Error> {
{
let mut frame = t.get_frame();
let selected_style = Style::default().fg(Color::Yellow).modifier(Modifier::Bold);
let normal_style = Style::default().fg(Color::White);
let header = ["Header1", "Header2", "Header3"];
let rows = app.items.iter().enumerate().map(|(i, item)| {
if i == app.selected {
Row::StyledData(item.into_iter(), &selected_style)
} else {
Row::StyledData(item.into_iter(), &normal_style)
}
});
t.draw().unwrap();
let rects = Layout::default()
.constraints([Constraint::Percentage(100)].as_ref())
.margin(5)
.split(&app.size);
Table::new(header.into_iter(), rows)
.block(Block::default().borders(Borders::ALL).title("Table"))
.widths(&[10, 10, 10])
.render(&mut frame, &rects[0]);
}
t.draw()
}

@ -9,7 +9,7 @@ use termion::event;
use termion::input::TermRead;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::style::{Color, Style};
use tui::widgets::{Block, Borders, Tabs, Widget};
use tui::Terminal;
@ -64,50 +64,51 @@ fn main() {
}
fn draw(t: &mut Terminal<MouseBackend>, app: &mut App) {
Block::default()
.style(Style::default().bg(Color::White))
.render(t, &app.size);
{
let mut f = t.get_frame();
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(5)
.constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
.split(&app.size);
Group::default()
.direction(Direction::Vertical)
.margin(5)
.sizes(&[Size::Fixed(3), Size::Min(0)])
.render(t, &app.size, |t, chunks| {
Tabs::default()
.block(Block::default().borders(Borders::ALL).title("Tabs"))
.titles(&app.tabs.titles)
.select(app.tabs.selection)
.style(Style::default().fg(Color::Cyan))
.highlight_style(Style::default().fg(Color::Yellow))
.render(t, &chunks[0]);
match app.tabs.selection {
0 => {
Block::default()
.title("Inner 0")
.borders(Borders::ALL)
.render(t, &chunks[1]);
}
1 => {
Block::default()
.title("Inner 1")
.borders(Borders::ALL)
.render(t, &chunks[1]);
}
2 => {
Block::default()
.title("Inner 2")
.borders(Borders::ALL)
.render(t, &chunks[1]);
}
3 => {
Block::default()
.title("Inner 3")
.borders(Borders::ALL)
.render(t, &chunks[1]);
}
_ => {}
Block::default()
.style(Style::default().bg(Color::White))
.render(&mut f, &app.size);
Tabs::default()
.block(Block::default().borders(Borders::ALL).title("Tabs"))
.titles(&app.tabs.titles)
.select(app.tabs.selection)
.style(Style::default().fg(Color::Cyan))
.highlight_style(Style::default().fg(Color::Yellow))
.render(&mut f, &chunks[0]);
match app.tabs.selection {
0 => {
Block::default()
.title("Inner 0")
.borders(Borders::ALL)
.render(&mut f, &chunks[1]);
}
});
1 => {
Block::default()
.title("Inner 1")
.borders(Borders::ALL)
.render(&mut f, &chunks[1]);
}
2 => {
Block::default()
.title("Inner 2")
.borders(Borders::ALL)
.render(&mut f, &chunks[1]);
}
3 => {
Block::default()
.title("Inner 3")
.borders(Borders::ALL)
.render(&mut f, &chunks[1]);
}
_ => {}
}
}
t.draw().unwrap();
}

@ -20,7 +20,7 @@ use termion::event;
use termion::input::TermRead;
use tui::backend::MouseBackend;
use tui::layout::{Direction, Group, Rect, Size};
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::style::{Color, Style};
use tui::widgets::{Block, Borders, Item, List, Paragraph, Widget};
use tui::Terminal;
@ -108,24 +108,26 @@ fn main() {
}
fn draw(t: &mut Terminal<MouseBackend>, app: &App) {
Group::default()
.direction(Direction::Vertical)
.margin(2)
.sizes(&[Size::Fixed(3), Size::Min(1)])
.render(t, &app.size, |t, chunks| {
Paragraph::default()
.style(Style::default().fg(Color::Yellow))
.block(Block::default().borders(Borders::ALL).title("Input"))
.text(&app.input)
.render(t, &chunks[0]);
List::new(
app.messages
.iter()
.enumerate()
.map(|(i, m)| Item::Data(format!("{}: {}", i, m))),
).block(Block::default().borders(Borders::ALL).title("Messages"))
.render(t, &chunks[1]);
});
{
let mut f = t.get_frame();
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints([Constraint::Length(3), Constraint::Min(1)].as_ref())
.split(&app.size);
Paragraph::default()
.style(Style::default().fg(Color::Yellow))
.block(Block::default().borders(Borders::ALL).title("Input"))
.text(&app.input)
.render(&mut f, &chunks[0]);
List::new(
app.messages
.iter()
.enumerate()
.map(|(i, m)| Item::Data(format!("{}: {}", i, m))),
).block(Block::default().borders(Borders::ALL).title("Messages"))
.render(&mut f, &chunks[1]);
}
t.draw().unwrap();
}

@ -183,7 +183,9 @@ impl Buffer {
/// ```
pub fn index_of(&self, x: u16, y: u16) -> usize {
debug_assert!(
x >= self.area.left() && x < self.area.right() && y >= self.area.top()
x >= self.area.left()
&& x < self.area.right()
&& y >= self.area.top()
&& y < self.area.bottom(),
"Trying to access position outside the buffer: x={}, y={}, area={:?}",
x,

@ -1,12 +1,10 @@
use std::cell::RefCell;
use std::cmp::{max, min};
use std::collections::HashMap;
use cassowary::strength::{REQUIRED, WEAK};
use cassowary::WeightedRelation::*;
use cassowary::{Constraint, Expression, Solver, Variable};
use backend::Backend;
use terminal::Terminal;
use cassowary::{Constraint as CassowaryConstraint, Expression, Solver, Variable};
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
pub enum Corner {
@ -22,199 +20,164 @@ pub enum Direction {
Vertical,
}
/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
/// area they are supposed to render to.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Constraint {
// TODO: enforce range 0 - 100
Percentage(u16),
Length(u16),
Max(u16),
Min(u16),
}
impl Default for Rect {
fn default() -> Rect {
Rect {
x: 0,
y: 0,
width: 0,
height: 0,
}
}
// TODO: enforce constraints size once const generics has landed
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Layout {
direction: Direction,
margin: u16,
constraints: Vec<Constraint>,
}
impl Rect {
pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
Rect {
x: x,
y: y,
width: width,
height: height,
}
}
pub fn area(&self) -> u16 {
self.width * self.height
}
pub fn left(&self) -> u16 {
self.x
}
pub fn right(&self) -> u16 {
self.x + self.width
}
pub fn top(&self) -> u16 {
self.y
}
pub fn bottom(&self) -> u16 {
self.y + self.height
}
thread_local! {
static LAYOUT_CACHE: RefCell<HashMap<(Rect, Layout), Vec<Rect>>> = RefCell::new(HashMap::new());
}
pub fn inner(&self, margin: u16) -> Rect {
if self.width < 2 * margin || self.height < 2 * margin {
Rect::default()
} else {
Rect {
x: self.x + margin,
y: self.y + margin,
width: self.width - 2 * margin,
height: self.height - 2 * margin,
}
impl Default for Layout {
fn default() -> Layout {
Layout {
direction: Direction::Vertical,
margin: 0,
constraints: Vec::new(),
}
}
}
pub fn union(&self, other: &Rect) -> Rect {
let x1 = min(self.x, other.x);
let y1 = min(self.y, other.y);
let x2 = max(self.x + self.width, other.x + other.width);
let y2 = max(self.y + self.height, other.y + other.height);
Rect {
x: x1,
y: y1,
width: x2 - x1,
height: y2 - y1,
}
impl Layout {
pub fn constraints<C>(mut self, constraints: C) -> Layout
where
C: Into<Vec<Constraint>>,
{
self.constraints = constraints.into();
self
}
pub fn intersection(&self, other: &Rect) -> Rect {
let x1 = max(self.x, other.x);
let y1 = max(self.y, other.y);
let x2 = min(self.x + self.width, other.x + other.width);
let y2 = min(self.y + self.height, other.y + other.height);
Rect {
x: x1,
y: y1,
width: x2 - x1,
height: y2 - y1,
}
pub fn margin(mut self, margin: u16) -> Layout {
self.margin = margin;
self
}
pub fn intersects(&self, other: &Rect) -> bool {
self.x < other.x + other.width && self.x + self.width > other.x
&& self.y < other.y + other.height && self.y + self.height > other.y
pub fn direction(mut self, direction: Direction) -> Layout {
self.direction = direction;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Size {
Fixed(u16),
Percent(u16),
Max(u16),
Min(u16),
/// 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
/// ```
/// # extern crate tui;
/// # use tui::layout::{Rect, Constraint, Direction, Layout};
///
/// # fn main() {
/// let chunks = Layout::default()
/// .direction(Direction::Vertical)
/// .constraints([Constraint::Length(5), Constraint::Min(0)].as_ref())
/// .split(&Rect{x: 2, y: 2, width: 10, height: 10});
/// assert_eq!(chunks, vec![Rect{x:2, y: 2, width: 10, height: 5},
/// Rect{x: 2, y: 7, width: 10, height: 5}])
/// # }
///
/// ```
pub fn split(self, area: &Rect) -> Vec<Rect> {
// TODO: Maybe use a fixed size cache ?
LAYOUT_CACHE.with(|c| {
return c
.borrow_mut()
.entry((*area, self.clone()))
.or_insert_with(|| split(area, self))
.clone();
})
}
}
/// 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
/// ```
/// # extern crate tui;
/// # use tui::layout::{Rect, Size, Direction, split};
///
/// # fn main() {
/// let chunks = split(&Rect{x: 2, y: 2, width: 10, height: 10},
/// &Direction::Vertical,
/// 0,
/// &[Size::Fixed(5), Size::Min(0)]);
/// assert_eq!(chunks, vec![Rect{x:2, y: 2, width: 10, height: 5},
/// Rect{x: 2, y: 7, width: 10, height: 5}])
/// # }
///
/// ```
pub fn split(area: &Rect, dir: &Direction, margin: u16, sizes: &[Size]) -> Vec<Rect> {
fn split(area: &Rect, layout: Layout) -> Vec<Rect> {
let mut solver = Solver::new();
let mut vars: HashMap<Variable, (usize, usize)> = HashMap::new();
let elements = sizes
let elements = layout
.constraints
.iter()
.map(|_| Element::new())
.collect::<Vec<Element>>();
let mut results = sizes.iter().map(|_| Rect::default()).collect::<Vec<Rect>>();
let dest_area = area.inner(margin);
let mut results = layout
.constraints
.iter()
.map(|_| Rect::default())
.collect::<Vec<Rect>>();
let dest_area = area.inner(layout.margin);
for (i, e) in elements.iter().enumerate() {
vars.insert(e.x, (i, 0));
vars.insert(e.y, (i, 1));
vars.insert(e.width, (i, 2));
vars.insert(e.height, (i, 3));
}
let mut constraints: Vec<Constraint> = Vec::with_capacity(elements.len() * 4 + sizes.len() * 6);
let mut ccs: Vec<CassowaryConstraint> =
Vec::with_capacity(elements.len() * 4 + layout.constraints.len() * 6);
for elt in &elements {
constraints.push(elt.left() | GE(REQUIRED) | f64::from(dest_area.left()));
constraints.push(elt.top() | GE(REQUIRED) | f64::from(dest_area.top()));
constraints.push(elt.right() | LE(REQUIRED) | f64::from(dest_area.right()));
constraints.push(elt.bottom() | LE(REQUIRED) | f64::from(dest_area.bottom()));
ccs.push(elt.left() | GE(REQUIRED) | f64::from(dest_area.left()));
ccs.push(elt.top() | GE(REQUIRED) | f64::from(dest_area.top()));
ccs.push(elt.right() | LE(REQUIRED) | f64::from(dest_area.right()));
ccs.push(elt.bottom() | LE(REQUIRED) | f64::from(dest_area.bottom()));
}
if let Some(first) = elements.first() {
constraints.push(match *dir {
ccs.push(match layout.direction {
Direction::Horizontal => first.left() | EQ(REQUIRED) | f64::from(dest_area.left()),
Direction::Vertical => first.top() | EQ(REQUIRED) | f64::from(dest_area.top()),
});
}
if let Some(last) = elements.last() {
constraints.push(match *dir {
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 *dir {
match layout.direction {
Direction::Horizontal => {
for pair in elements.windows(2) {
constraints.push((pair[0].x + pair[0].width) | EQ(REQUIRED) | pair[1].x);
ccs.push((pair[0].x + pair[0].width) | EQ(REQUIRED) | pair[1].x);
}
for (i, size) in sizes.iter().enumerate() {
constraints.push(elements[i].y | EQ(REQUIRED) | f64::from(dest_area.y));
constraints.push(elements[i].height | EQ(REQUIRED) | f64::from(dest_area.height));
constraints.push(match *size {
Size::Fixed(v) => elements[i].width | EQ(WEAK) | f64::from(v),
Size::Percent(v) => {
for (i, size) 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)
}
Size::Min(v) => elements[i].width | GE(WEAK) | f64::from(v),
Size::Max(v) => elements[i].width | LE(WEAK) | f64::from(v),
Constraint::Min(v) => elements[i].width | GE(WEAK) | f64::from(v),
Constraint::Max(v) => elements[i].width | LE(WEAK) | f64::from(v),
});
}
}
Direction::Vertical => {
for pair in elements.windows(2) {
constraints.push((pair[0].y + pair[0].height) | EQ(REQUIRED) | pair[1].y);
ccs.push((pair[0].y + pair[0].height) | EQ(REQUIRED) | pair[1].y);
}
for (i, size) in sizes.iter().enumerate() {
constraints.push(elements[i].x | EQ(REQUIRED) | f64::from(dest_area.x));
constraints.push(elements[i].width | EQ(REQUIRED) | f64::from(dest_area.width));
constraints.push(match *size {
Size::Fixed(v) => elements[i].height | EQ(WEAK) | f64::from(v),
Size::Percent(v) => {
for (i, size) 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)
}
Size::Min(v) => elements[i].height | GE(WEAK) | f64::from(v),
Size::Max(v) => elements[i].height | LE(WEAK) | f64::from(v),
Constraint::Min(v) => elements[i].height | GE(WEAK) | f64::from(v),
Constraint::Max(v) => elements[i].height | LE(WEAK) | f64::from(v),
});
}
}
}
solver.add_constraints(&constraints).unwrap();
solver.add_constraints(&ccs).unwrap();
for &(var, value) in solver.fetch_changes() {
let (index, attr) = vars[&var];
let value = if value.is_sign_negative() {
@ -241,7 +204,7 @@ pub fn split(area: &Rect, dir: &Direction, margin: u16, sizes: &[Size]) -> Vec<R
// Fix imprecision by extending the last item a bit if necessary
if let Some(last) = results.last_mut() {
match *dir {
match layout.direction {
Direction::Vertical => {
last.height = dest_area.bottom() - last.y;
}
@ -288,58 +251,100 @@ impl Element {
}
}
/// Describes a layout and may be used to group widgets in a specific area of the terminal
///
/// # Examples
///
/// ```
/// # extern crate tui;
/// use tui::layout::{Group, Direction, Size};
/// # fn main() {
/// Group::default()
/// .direction(Direction::Vertical)
/// .margin(0)
/// .sizes(&[Size::Percent(50), Size::Percent(50)]);
/// # }
/// ```
#[derive(Debug, PartialEq, Clone, Eq, Hash)]
pub struct Group {
pub direction: Direction,
pub margin: u16,
pub sizes: Vec<Size>,
/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
/// area they are supposed to render to.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
}
impl Default for Group {
fn default() -> Group {
Group {
direction: Direction::Horizontal,
margin: 0,
sizes: Vec::new(),
impl Default for Rect {
fn default() -> Rect {
Rect {
x: 0,
y: 0,
width: 0,
height: 0,
}
}
}
impl Group {
pub fn direction(&mut self, direction: Direction) -> &mut Group {
self.direction = direction;
self
impl Rect {
pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
Rect {
x: x,
y: y,
width: width,
height: height,
}
}
pub fn margin(&mut self, margin: u16) -> &mut Group {
self.margin = margin;
self
pub fn area(&self) -> u16 {
self.width * self.height
}
pub fn sizes(&mut self, sizes: &[Size]) -> &mut Group {
self.sizes = Vec::from(sizes);
self
pub fn left(&self) -> u16 {
self.x
}
pub fn render<F, B>(&self, t: &mut Terminal<B>, area: &Rect, f: F)
where
B: Backend,
F: FnOnce(&mut Terminal<B>, &[Rect]),
{
let chunks = t.compute_layout(self, area);
f(t, &chunks);
pub fn right(&self) -> u16 {
self.x + self.width
}
pub fn top(&self) -> u16 {
self.y
}
pub fn bottom(&self) -> u16 {
self.y + self.height
}
pub fn inner(&self, margin: u16) -> Rect {
if self.width < 2 * margin || self.height < 2 * margin {
Rect::default()
} else {
Rect {
x: self.x + margin,
y: self.y + margin,
width: self.width - 2 * margin,
height: self.height - 2 * margin,
}
}
}
pub fn union(&self, other: &Rect) -> Rect {
let x1 = min(self.x, other.x);
let y1 = min(self.y, other.y);
let x2 = max(self.x + self.width, other.x + other.width);
let y2 = max(self.y + self.height, other.y + other.height);
Rect {
x: x1,
y: y1,
width: x2 - x1,
height: y2 - y1,
}
}
pub fn intersection(&self, other: &Rect) -> Rect {
let x1 = max(self.x, other.x);
let y1 = max(self.y, other.y);
let x2 = min(self.x + self.width, other.x + other.width);
let y2 = min(self.y + self.height, other.y + other.height);
Rect {
x: x1,
y: y1,
width: x2 - x1,
height: y2 - y1,
}
}
pub fn intersects(&self, other: &Rect) -> bool {
self.x < other.x + other.width
&& self.x + self.width > other.x
&& self.y < other.y + other.height
&& self.y + self.height > other.y
}
}

@ -69,7 +69,7 @@
//! use tui::Terminal;
//! use tui::backend::RawBackend;
//! use tui::widgets::{Widget, Block, Borders};
//! use tui::layout::{Group, Size, Direction};
//! use tui::layout::{Layout, Constraint, Direction};
//!
//! fn main() {
//! let mut terminal = init().expect("Failed initialization");
@ -85,10 +85,13 @@
//!
//! let size = t.size()?;
//!
//! Block::default()
//! .title("Block")
//! .borders(Borders::ALL)
//! .render(t, &size);
//! {
//! let mut f = t.get_frame();
//! Block::default()
//! .title("Block")
//! .borders(Borders::ALL)
//! .render(&mut f, &size);
//! }
//!
//! t.draw()
//! }
@ -96,10 +99,9 @@
//!
//! ## Layout
//!
//! The library comes with a basic yet useful layout management object called
//! `Group`. As you may see below and in the examples, the library makes heavy
//! use of the builder pattern to provide full customization. And the `Group`
//! object is no exception:
//! The library comes with a basic yet useful layout management object called `Layout`. As you may
//! see below and in the examples, the library makes heavy use of the builder pattern to provide
//! full customization. And `Layout` is no exception:
//!
//! ```rust,no_run
//! extern crate tui;
@ -109,7 +111,7 @@
//! use tui::Terminal;
//! use tui::backend::RawBackend;
//! use tui::widgets::{Widget, Block, Borders};
//! use tui::layout::{Group, Size, Direction};
//! use tui::layout::{Layout, Constraint, Direction};
//!
//! fn main() {
//! let mut terminal = init().expect("Failed initialization");
@ -125,20 +127,28 @@
//!
//! let size = t.size()?;
//!
//! Group::default()
//! {
//! let mut f = t.get_frame();
//! let chunks = Layout::default()
//! .direction(Direction::Vertical)
//! .margin(1)
//! .sizes(&[Size::Percent(10), Size::Percent(80), Size::Percent(10)])
//! .render(t, &size, |t, chunks| {
//! Block::default()
//! .title("Block")
//! .borders(Borders::ALL)
//! .render(t, &chunks[0]);
//! Block::default()
//! .title("Block 2")
//! .borders(Borders::ALL)
//! .render(t, &chunks[2]);
//! });
//! .constraints(
//! [
//! Constraint::Percentage(10),
//! Constraint::Percentage(80),
//! Constraint::Percentage(10)
//! ].as_ref()
//! )
//! .split(&size);
//! Block::default()
//! .title("Block")
//! .borders(Borders::ALL)
//! .render(&mut f, &chunks[0]);
//! Block::default()
//! .title("Block 2")
//! .borders(Borders::ALL)
//! .render(&mut f, &chunks[2]);
//! }
//!
//! t.draw()
//! }
@ -170,4 +180,4 @@ pub mod symbols;
pub mod terminal;
pub mod widgets;
pub use self::terminal::Terminal;
pub use self::terminal::{Frame, Terminal};

@ -1,18 +1,10 @@
use std::collections::HashMap;
use std::io;
use backend::Backend;
use buffer::Buffer;
use layout::{split, Group, Rect};
use layout::Rect;
use widgets::Widget;
/// Holds a computed layout and keeps track of its use between successive draw calls
#[derive(Debug)]
pub struct LayoutEntry {
chunks: Vec<Rect>,
hot: bool,
}
/// Interface to the terminal backed by Termion
#[derive(Debug)]
pub struct Terminal<B>
@ -20,8 +12,6 @@ where
B: Backend,
{
backend: B,
/// Cache to prevent the layout to be computed at each draw call
layout_cache: HashMap<(Group, Rect), LayoutEntry>,
/// Holds the results of the current and previous draw calls. The two are compared at the end
/// of each draw pass to output the necessary updates to the terminal
buffers: [Buffer; 2],
@ -29,6 +19,26 @@ where
current: usize,
}
pub struct Frame<'a, B: 'a>
where
B: Backend,
{
terminal: &'a mut Terminal<B>,
}
impl<'a, B> Frame<'a, B>
where
B: Backend,
{
/// Calls the draw method of a given widget on the current buffer
pub fn render<W>(&mut self, widget: &mut W, area: &Rect)
where
W: Widget,
{
widget.draw(area, self.terminal.current_buffer_mut());
}
}
impl<B> Terminal<B>
where
B: Backend,
@ -36,15 +46,22 @@ where
/// Wrapper around Termion initialization. Each buffer is initialized with a blank string and
/// default colors for the foreground and the background
pub fn new(backend: B) -> Result<Terminal<B>, io::Error> {
let size = try!(backend.size());
let size = backend.size()?;
Ok(Terminal {
backend: backend,
layout_cache: HashMap::new(),
buffers: [Buffer::empty(size), Buffer::empty(size)],
current: 0,
})
}
pub fn get_frame<'a>(&'a mut self) -> Frame<'a, B> {
Frame { terminal: self }
}
pub fn current_buffer_mut<'a>(&'a mut self) -> &'a mut Buffer {
&mut self.buffers[self.current]
}
pub fn backend(&self) -> &B {
&self.backend
}
@ -53,27 +70,6 @@ where
&mut self.backend
}
/// Check if we have already computed a layout for a given group, otherwise it creates one and
/// add it to the layout cache. Moreover the function marks the queried entries so that we can
/// clean outdated ones at the end of the draw call.
pub fn compute_layout(&mut self, group: &Group, area: &Rect) -> Vec<Rect> {
let entry = self.layout_cache
.entry((group.clone(), *area))
.or_insert_with(|| {
let chunks = split(area, &group.direction, group.margin, &group.sizes);
debug!(
"New layout computed:\n* Group = {:?}\n* Chunks = {:?}",
group, chunks
);
LayoutEntry {
chunks: chunks,
hot: true,
}
});
entry.hot = true;
entry.chunks.clone()
}
/// Builds a string representing the minimal escape sequences and characters set necessary to
/// update the UI and writes it to stdout.
pub fn flush(&mut self) -> Result<(), io::Error> {
@ -96,21 +92,12 @@ where
self.backend.draw(content)
}
/// Calls the draw method of a given widget on the current buffer
pub fn render<W>(&mut self, widget: &mut W, area: &Rect)
where
W: Widget,
{
widget.draw(area, &mut self.buffers[self.current]);
}
/// Updates the interface so that internal buffers matches the current size of the terminal.
/// This leads to a full redraw of the screen.
pub fn resize(&mut self, area: Rect) -> Result<(), io::Error> {
self.buffers[self.current].resize(area);
self.buffers[1 - self.current].resize(area);
self.buffers[1 - self.current].reset();
self.layout_cache.clear();
self.buffers[1 - self.current].resize(area);
self.backend.clear()
}
@ -119,20 +106,6 @@ where
// Draw to stdout
self.flush()?;
// Clean layout cache
let hot = self.layout_cache
.drain()
.filter(|&(_, ref v)| v.hot)
.collect::<Vec<((Group, Rect), LayoutEntry)>>();
for (key, value) in hot {
self.layout_cache.insert(key, value);
}
for e in self.layout_cache.values_mut() {
e.hot = false;
}
// Swap buffers
self.buffers[1 - self.current].reset();
self.current = 1 - self.current;

@ -123,13 +123,15 @@ impl<'a> Widget for BarChart<'a> {
self.background(&chart_area, buf, self.style.bg);
let max = self.max
let max = self
.max
.unwrap_or_else(|| self.data.iter().fold(0, |acc, &(_, v)| max(v, acc)));
let max_index = min(
(chart_area.width / (self.bar_width + self.bar_gap)) as usize,
self.data.len(),
);
let mut data = self.data
let mut data = self
.data
.iter()
.take(max_index)
.map(|&(l, v)| (l, v * u64::from(chart_area.height) * 8 / max))
@ -170,7 +172,8 @@ impl<'a> Widget for BarChart<'a> {
let width = value_label.width() as u16;
if width < self.bar_width {
buf.set_string(
chart_area.left() + i as u16 * (self.bar_width + self.bar_gap)
chart_area.left()
+ i as u16 * (self.bar_width + self.bar_gap)
+ (self.bar_width - width) / 2,
chart_area.bottom() - 2,
value_label,

@ -277,7 +277,9 @@ where
// Finally draw the labels
let style = Style::default().bg(self.background_color);
for label in ctx.labels.iter().filter(|l| {
!(l.x < self.x_bounds[0] || l.x > self.x_bounds[1] || l.y < self.y_bounds[0]
!(l.x < self.x_bounds[0]
|| l.x > self.x_bounds[1]
|| l.y < self.y_bounds[0]
|| l.y > self.y_bounds[1])
}) {
let dy = ((self.y_bounds[1] - label.y) * f64::from(canvas_area.height - 1)

@ -428,7 +428,8 @@ where
for dataset in self.datasets {
match dataset.marker {
Marker::Dot => for &(x, y) in dataset.data.iter().filter(|&&(x, y)| {
!(x < self.x_axis.bounds[0] || x > self.x_axis.bounds[1]
!(x < self.x_axis.bounds[0]
|| x > self.x_axis.bounds[1]
|| y < self.y_axis.bounds[0]
|| y > self.y_axis.bounds[1])
}) {

@ -95,7 +95,8 @@ where
self.background(&list_area, buf, self.style.bg);
for (i, item) in self.items
for (i, item) in self
.items
.by_ref()
.enumerate()
.take(list_area.height as usize)
@ -230,7 +231,8 @@ impl<'b> Widget for SelectableList<'b> {
};
// Render items
let items = self.items
let items = self
.items
.iter()
.enumerate()
.map(|(i, item)| {

@ -23,7 +23,7 @@ use backend::Backend;
use buffer::Buffer;
use layout::Rect;
use style::Color;
use terminal::Terminal;
use terminal::Frame;
/// Bitflags that can be composed to set the visible borders essentially on the block widget.
bitflags! {
@ -57,11 +57,11 @@ pub trait Widget {
}
}
/// Helper method that can be chained with a widget's builder methods to render it.
fn render<B>(&mut self, t: &mut Terminal<B>, area: &Rect)
fn render<B>(&mut self, f: &mut Frame<B>, area: &Rect)
where
Self: Sized,
B: Backend,
{
t.render(self, area);
f.render(self, area);
}
}

@ -86,7 +86,8 @@ impl<'a> Widget for Sparkline<'a> {
None => *self.data.iter().max().unwrap_or(&1u64),
};
let max_index = min(spark_area.width as usize, self.data.len());
let mut data = self.data
let mut data = self
.data
.iter()
.take(max_index)
.map(|e| e * u64::from(spark_area.height) * 8 / max)

Loading…
Cancel
Save