mirror of
https://github.com/ratatui-org/ratatui
synced 2024-09-20 14:42:06 +00:00
feat: split layout from rendering
* remove layout logic from Terminal * replace Group with Layout * add Frame intermediate object
This commit is contained in:
parent
cfc90ab7f6
commit
7181970a32
25 changed files with 1001 additions and 973 deletions
|
@ -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| {
|
||||
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(t, &chunks[0]);
|
||||
Group::default()
|
||||
{
|
||||
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)
|
||||
.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]);
|
||||
})
|
||||
});
|
||||
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
||||
.split(&chunks[1]);
|
||||
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(&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();
|
||||
}
|
||||
|
|
427
examples/demo.rs
427
examples/demo.rs
|
@ -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,
|
||||
},
|
||||
)
|
||||
});
|
||||
List::new(events)
|
||||
.block(Block::default().borders(Borders::ALL).title("List"))
|
||||
.start_corner(Corner::BottomLeft)
|
||||
.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);
|
||||
|
||||
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(&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,
|
||||
|
|
469
src/layout.rs
469
src/layout.rs
|
@ -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,6 +20,237 @@ pub enum Direction {
|
|||
Vertical,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Constraint {
|
||||
// TODO: enforce range 0 - 100
|
||||
Percentage(u16),
|
||||
Length(u16),
|
||||
Max(u16),
|
||||
Min(u16),
|
||||
}
|
||||
|
||||
// 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>,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static LAYOUT_CACHE: RefCell<HashMap<(Rect, Layout), Vec<Rect>>> = RefCell::new(HashMap::new());
|
||||
}
|
||||
|
||||
impl Default for Layout {
|
||||
fn default() -> Layout {
|
||||
Layout {
|
||||
direction: Direction::Vertical,
|
||||
margin: 0,
|
||||
constraints: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub fn constraints<C>(mut self, constraints: C) -> Layout
|
||||
where
|
||||
C: Into<Vec<Constraint>>,
|
||||
{
|
||||
self.constraints = constraints.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn margin(mut self, margin: u16) -> Layout {
|
||||
self.margin = margin;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn direction(mut self, direction: Direction) -> Layout {
|
||||
self.direction = direction;
|
||||
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
|
||||
/// ```
|
||||
/// # 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();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn split(area: &Rect, layout: Layout) -> Vec<Rect> {
|
||||
let mut solver = Solver::new();
|
||||
let mut vars: HashMap<Variable, (usize, usize)> = HashMap::new();
|
||||
let elements = layout
|
||||
.constraints
|
||||
.iter()
|
||||
.map(|_| Element::new())
|
||||
.collect::<Vec<Element>>();
|
||||
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 ccs: Vec<CassowaryConstraint> =
|
||||
Vec::with_capacity(elements.len() * 4 + layout.constraints.len() * 6);
|
||||
for elt in &elements {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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::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) {
|
||||
ccs.push((pair[0].y + pair[0].height) | EQ(REQUIRED) | pair[1].y);
|
||||
}
|
||||
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)
|
||||
}
|
||||
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(&ccs).unwrap();
|
||||
for &(var, value) in solver.fetch_changes() {
|
||||
let (index, attr) = vars[&var];
|
||||
let value = if value.is_sign_negative() {
|
||||
0
|
||||
} else {
|
||||
value as u16
|
||||
};
|
||||
match attr {
|
||||
0 => {
|
||||
results[index].x = value;
|
||||
}
|
||||
1 => {
|
||||
results[index].y = value;
|
||||
}
|
||||
2 => {
|
||||
results[index].width = value;
|
||||
}
|
||||
3 => {
|
||||
results[index].height = value;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
/// A container used by the solver inside split
|
||||
struct Element {
|
||||
x: Variable,
|
||||
y: Variable,
|
||||
width: Variable,
|
||||
height: Variable,
|
||||
}
|
||||
|
||||
impl Element {
|
||||
fn new() -> Element {
|
||||
Element {
|
||||
x: Variable::new(),
|
||||
y: Variable::new(),
|
||||
width: Variable::new(),
|
||||
height: Variable::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn left(&self) -> Variable {
|
||||
self.x
|
||||
}
|
||||
|
||||
fn top(&self) -> Variable {
|
||||
self.y
|
||||
}
|
||||
|
||||
fn right(&self) -> Expression {
|
||||
self.x + self.width
|
||||
}
|
||||
|
||||
fn bottom(&self) -> Expression {
|
||||
self.y + self.height
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
|
@ -113,233 +342,9 @@ impl Rect {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
#[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, 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> {
|
||||
let mut solver = Solver::new();
|
||||
let mut vars: HashMap<Variable, (usize, usize)> = HashMap::new();
|
||||
let elements = sizes
|
||||
.iter()
|
||||
.map(|_| Element::new())
|
||||
.collect::<Vec<Element>>();
|
||||
let mut results = sizes.iter().map(|_| Rect::default()).collect::<Vec<Rect>>();
|
||||
let dest_area = area.inner(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);
|
||||
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()));
|
||||
}
|
||||
if let Some(first) = elements.first() {
|
||||
constraints.push(match *dir {
|
||||
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 {
|
||||
Direction::Horizontal => last.right() | EQ(REQUIRED) | f64::from(dest_area.right()),
|
||||
Direction::Vertical => last.bottom() | EQ(REQUIRED) | f64::from(dest_area.bottom()),
|
||||
});
|
||||
}
|
||||
match *dir {
|
||||
Direction::Horizontal => {
|
||||
for pair in elements.windows(2) {
|
||||
constraints.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) => {
|
||||
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),
|
||||
});
|
||||
}
|
||||
}
|
||||
Direction::Vertical => {
|
||||
for pair in elements.windows(2) {
|
||||
constraints.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) => {
|
||||
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),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
solver.add_constraints(&constraints).unwrap();
|
||||
for &(var, value) in solver.fetch_changes() {
|
||||
let (index, attr) = vars[&var];
|
||||
let value = if value.is_sign_negative() {
|
||||
0
|
||||
} else {
|
||||
value as u16
|
||||
};
|
||||
match attr {
|
||||
0 => {
|
||||
results[index].x = value;
|
||||
}
|
||||
1 => {
|
||||
results[index].y = value;
|
||||
}
|
||||
2 => {
|
||||
results[index].width = value;
|
||||
}
|
||||
3 => {
|
||||
results[index].height = value;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix imprecision by extending the last item a bit if necessary
|
||||
if let Some(last) = results.last_mut() {
|
||||
match *dir {
|
||||
Direction::Vertical => {
|
||||
last.height = dest_area.bottom() - last.y;
|
||||
}
|
||||
Direction::Horizontal => {
|
||||
last.width = dest_area.right() - last.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
/// A container used by the solver inside split
|
||||
struct Element {
|
||||
x: Variable,
|
||||
y: Variable,
|
||||
width: Variable,
|
||||
height: Variable,
|
||||
}
|
||||
|
||||
impl Element {
|
||||
fn new() -> Element {
|
||||
Element {
|
||||
x: Variable::new(),
|
||||
y: Variable::new(),
|
||||
width: Variable::new(),
|
||||
height: Variable::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn left(&self) -> Variable {
|
||||
self.x
|
||||
}
|
||||
|
||||
fn top(&self) -> Variable {
|
||||
self.y
|
||||
}
|
||||
|
||||
fn right(&self) -> Expression {
|
||||
self.x + self.width
|
||||
}
|
||||
|
||||
fn bottom(&self) -> Expression {
|
||||
self.y + self.height
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
impl Default for Group {
|
||||
fn default() -> Group {
|
||||
Group {
|
||||
direction: Direction::Horizontal,
|
||||
margin: 0,
|
||||
sizes: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Group {
|
||||
pub fn direction(&mut self, direction: Direction) -> &mut Group {
|
||||
self.direction = direction;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn margin(&mut self, margin: u16) -> &mut Group {
|
||||
self.margin = margin;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sizes(&mut self, sizes: &[Size]) -> &mut Group {
|
||||
self.sizes = Vec::from(sizes);
|
||||
self
|
||||
}
|
||||
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);
|
||||
self.x < other.x + other.width
|
||||
&& self.x + self.width > other.x
|
||||
&& self.y < other.y + other.height
|
||||
&& self.y + self.height > other.y
|
||||
}
|
||||
}
|
||||
|
|
56
src/lib.rs
56
src/lib.rs
|
@ -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…
Reference in a new issue