diff --git a/examples/barchart.rs b/examples/barchart.rs index c9cd857c..1c93db6e 100644 --- a/examples/barchart.rs +++ b/examples/barchart.rs @@ -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, 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(); } diff --git a/examples/block.rs b/examples/block.rs index 69b14f75..c664c495 100644 --- a/examples/block.rs +++ b/examples/block.rs @@ -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, 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(); } diff --git a/examples/canvas.rs b/examples/canvas.rs index 724224e7..f1eb441d 100644 --- a/examples/canvas.rs +++ b/examples/canvas.rs @@ -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, 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(); } diff --git a/examples/chart.rs b/examples/chart.rs index 3964e440..bc2e3203 100644 --- a/examples/chart.rs +++ b/examples/chart.rs @@ -122,46 +122,49 @@ fn main() { } fn draw(t: &mut Terminal, 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(); } diff --git a/examples/custom_widget.rs b/examples/custom_widget.rs index 786337d9..2f816acd 100644 --- a/examples/custom_widget.rs +++ b/examples/custom_widget.rs @@ -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(); } diff --git a/examples/demo.rs b/examples/demo.rs index 8f70459b..b942acd1 100644 --- a/examples/demo.rs +++ b/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, 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, 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, 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, app: &App, area: &Rect) { +fn draw_gauges(f: &mut Frame, 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, app: &App, area: &Rect) { - let sizes = if app.show_chart { - vec![Size::Percent(50), Size::Percent(50)] +fn draw_charts(f: &mut Frame, 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, area: &Rect) { +fn draw_text(f: &mut Frame, area: &Rect) { Paragraph::default() .block( Block::default() @@ -457,61 +459,60 @@ fn draw_text(t: &mut Terminal, area: &Rect) { it should display unicode characters properly: 日本国, ٩(-̮̮̃-̃)۶ ٩(●̮̮̃•̃)۶ ٩(͡๏̯͡๏)۶ \ ٩(-̮̮̃•̃).", ) - .render(t, area); + .render(f, area); } -fn draw_second_tab(t: &mut Terminal, app: &App, area: &Rect) { - Group::default() +fn draw_second_tab(f: &mut Frame, 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]); } diff --git a/examples/gauge.rs b/examples/gauge.rs index 68ae609a..42566db0 100644 --- a/examples/gauge.rs +++ b/examples/gauge.rs @@ -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, 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(); } diff --git a/examples/layout.rs b/examples/layout.rs index 291de0c2..46314882 100644 --- a/examples/layout.rs +++ b/examples/layout.rs @@ -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, 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(); } diff --git a/examples/list.rs b/examples/list.rs index 8d75431a..32852133 100644 --- a/examples/list.rs +++ b/examples/list.rs @@ -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, 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(); } diff --git a/examples/paragraph.rs b/examples/paragraph.rs index b042cb1e..0848ca4e 100644 --- a/examples/paragraph.rs +++ b/examples/paragraph.rs @@ -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, 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(); } diff --git a/examples/rustbox.rs b/examples/rustbox.rs index 364913af..9e5612c9 100644 --- a/examples/rustbox.rs +++ b/examples/rustbox.rs @@ -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) { 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(); } diff --git a/examples/sparkline.rs b/examples/sparkline.rs index 3ad5656e..e17a57ff 100644 --- a/examples/sparkline.rs +++ b/examples/sparkline.rs @@ -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, 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(); } diff --git a/examples/table.rs b/examples/table.rs index b4b98451..7e3f7c00 100644 --- a/examples/table.rs +++ b/examples/table.rs @@ -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, 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, 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() } diff --git a/examples/tabs.rs b/examples/tabs.rs index 4748079a..2e43cfce 100644 --- a/examples/tabs.rs +++ b/examples/tabs.rs @@ -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, 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(); } diff --git a/examples/user_input.rs b/examples/user_input.rs index 271280a5..0f741835 100644 --- a/examples/user_input.rs +++ b/examples/user_input.rs @@ -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, 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(); } diff --git a/src/buffer.rs b/src/buffer.rs index 3a31b3cb..4c44d833 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -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, diff --git a/src/layout.rs b/src/layout.rs index c8cf6fd8..d86415bd 100644 --- a/src/layout.rs +++ b/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, +} + +thread_local! { + static LAYOUT_CACHE: RefCell>> = 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(mut self, constraints: C) -> Layout + where + C: Into>, + { + 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 { + // 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 { + let mut solver = Solver::new(); + let mut vars: HashMap = HashMap::new(); + let elements = layout + .constraints + .iter() + .map(|_| Element::new()) + .collect::>(); + let mut results = layout + .constraints + .iter() + .map(|_| Rect::default()) + .collect::>(); + + 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 = + 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 { - let mut solver = Solver::new(); - let mut vars: HashMap = HashMap::new(); - let elements = sizes - .iter() - .map(|_| Element::new()) - .collect::>(); - let mut results = sizes.iter().map(|_| Rect::default()).collect::>(); - 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 = 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, -} - -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(&self, t: &mut Terminal, area: &Rect, f: F) - where - B: Backend, - F: FnOnce(&mut Terminal, &[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 } } diff --git a/src/lib.rs b/src/lib.rs index 3d117d22..1af46fe3 100644 --- a/src/lib.rs +++ b/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}; diff --git a/src/terminal.rs b/src/terminal.rs index 1aa7f6bf..a04b77e3 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -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, - hot: bool, -} - /// Interface to the terminal backed by Termion #[derive(Debug)] pub struct Terminal @@ -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, +} + +impl<'a, B> Frame<'a, B> +where + B: Backend, +{ + /// Calls the draw method of a given widget on the current buffer + pub fn render(&mut self, widget: &mut W, area: &Rect) + where + W: Widget, + { + widget.draw(area, self.terminal.current_buffer_mut()); + } +} + impl Terminal 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, 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 { - 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(&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::>(); - - 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; diff --git a/src/widgets/barchart.rs b/src/widgets/barchart.rs index e493ccf5..43d73a6d 100644 --- a/src/widgets/barchart.rs +++ b/src/widgets/barchart.rs @@ -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, diff --git a/src/widgets/canvas/mod.rs b/src/widgets/canvas/mod.rs index 61c9e519..ffa7764b 100644 --- a/src/widgets/canvas/mod.rs +++ b/src/widgets/canvas/mod.rs @@ -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) diff --git a/src/widgets/chart.rs b/src/widgets/chart.rs index 44fcb3fa..12321c13 100644 --- a/src/widgets/chart.rs +++ b/src/widgets/chart.rs @@ -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]) }) { diff --git a/src/widgets/list.rs b/src/widgets/list.rs index ed03d06e..49766c9c 100644 --- a/src/widgets/list.rs +++ b/src/widgets/list.rs @@ -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)| { diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index dc619c7f..4fd42025 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -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(&mut self, t: &mut Terminal, area: &Rect) + fn render(&mut self, f: &mut Frame, area: &Rect) where Self: Sized, B: Backend, { - t.render(self, area); + f.render(self, area); } } diff --git a/src/widgets/sparkline.rs b/src/widgets/sparkline.rs index 5ac8c7b4..846c4c50 100644 --- a/src/widgets/sparkline.rs +++ b/src/widgets/sparkline.rs @@ -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)