Frame: provide consistent size for rendering

This commit is contained in:
Karoline Pauls 2018-12-07 14:43:05 +00:00 committed by Florian Dehau
parent 8522e028f1
commit 228816f5f8
17 changed files with 50 additions and 55 deletions

View file

@ -77,13 +77,12 @@ fn main() -> Result<(), failure::Error> {
let mut app = App::new();
loop {
let size = terminal.size()?;
terminal.draw(|mut f| {
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(size);
.split(f.size());
BarChart::default()
.block(Block::default().title("Data1").borders(Borders::ALL))
.data(&app.data)

View file

@ -31,18 +31,17 @@ fn main() -> Result<(), failure::Error> {
let events = Events::new();
loop {
let size = terminal.size()?;
terminal.draw(|mut f| {
// 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
let size = f.size();
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);
.split(f.size());
{
let chunks = Layout::default()
.direction(Direction::Horizontal)

View file

@ -90,13 +90,11 @@ fn main() -> Result<(), failure::Error> {
let mut app = App::new();
loop {
let size = terminal.size()?;
terminal.draw(|mut f| {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(size);
.split(f.size());
Canvas::default()
.block(Block::default().borders(Borders::ALL).title("World"))
.paint(|ctx| {

View file

@ -71,9 +71,8 @@ fn main() -> Result<(), failure::Error> {
let mut app = App::new();
loop {
let size = terminal.size()?;
terminal.draw(|mut f| {
let size = f.size();
Chart::default()
.block(
Block::default()

View file

@ -13,9 +13,8 @@ fn main() -> Result<(), failure::Error> {
terminal.hide_cursor()?;
loop {
let size = terminal.size()?;
terminal.draw(|mut f| {
let size = f.size();
let text = [
Text::raw("It "),
Text::styled("works", Style::default().fg(Color::Yellow)),

View file

@ -54,9 +54,8 @@ fn main() -> Result<(), failure::Error> {
let events = Events::new();
loop {
let size = terminal.size()?;
terminal.draw(|mut f| {
let size = f.size();
Label::default().text("Test").render(&mut f, size);
})?;

View file

@ -168,13 +168,11 @@ fn main() -> Result<(), failure::Error> {
};
loop {
let size = terminal.size()?;
// Draw UI
terminal.draw(|mut f| {
let chunks = Layout::default()
.constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
.split(size);
.split(f.size());
Tabs::default()
.block(Block::default().borders(Borders::ALL).title("Tabs"))
.titles(&app.tabs.titles)

View file

@ -70,8 +70,6 @@ fn main() -> Result<(), failure::Error> {
let mut app = App::new();
loop {
let size = terminal.size()?;
terminal.draw(|mut f| {
let chunks = Layout::default()
.direction(Direction::Vertical)
@ -85,7 +83,7 @@ fn main() -> Result<(), failure::Error> {
]
.as_ref(),
)
.split(size);
.split(f.size());
Gauge::default()
.block(Block::default().title("Gauge1").borders(Borders::ALL))

View file

@ -34,8 +34,6 @@ fn main() -> Result<(), failure::Error> {
let events = Events::new();
loop {
let size = terminal.size()?;
terminal.draw(|mut f| {
let chunks = Layout::default()
.direction(Direction::Vertical)
@ -47,7 +45,7 @@ fn main() -> Result<(), failure::Error> {
]
.as_ref(),
)
.split(size);
.split(f.size());
Block::default()
.title("Block")

View file

@ -94,13 +94,11 @@ fn main() -> Result<(), failure::Error> {
let mut app = App::new();
loop {
let size = terminal.size()?;
terminal.draw(|mut f| {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(size);
.split(f.size());
let style = Style::default().fg(Color::Black).bg(Color::White);
SelectableList::default()

View file

@ -31,13 +31,13 @@ fn main() -> Result<(), failure::Error> {
let events = Events::new();
loop {
let size = terminal.size()?;
let mut long_line: String = std::iter::repeat('X').take(size.width.into()).collect();
long_line.insert_str(0, "Very long line: ");
long_line.push('\n');
terminal.draw(|mut f| {
let size = f.size();
let mut long_line: String = std::iter::repeat('X').take(size.width.into()).collect();
long_line.insert_str(0, "Very long line: ");
long_line.push('\n');
Block::default()
.style(Style::default().bg(Color::White))
.render(&mut f, size);

View file

@ -30,12 +30,12 @@ fn main() -> Result<(), failure::Error> {
}
fn draw(t: &mut Terminal<RustboxBackend>) -> Result<(), std::io::Error> {
let size = t.size()?;
let text = [
Text::raw("It "),
Text::styled("works", Style::default().fg(Color::Yellow)),
];
t.draw(|mut f| {
let size = f.size();
Paragraph::new(text.iter())
.block(
Block::default()

View file

@ -70,8 +70,6 @@ fn main() -> Result<(), failure::Error> {
let mut app = App::new();
loop {
let size = terminal.size()?;
terminal.draw(|mut f| {
let chunks = Layout::default()
.direction(Direction::Vertical)
@ -85,7 +83,7 @@ fn main() -> Result<(), failure::Error> {
]
.as_ref(),
)
.split(size);
.split(f.size());
Sparkline::default()
.block(
Block::default()

View file

@ -56,8 +56,6 @@ fn main() -> Result<(), failure::Error> {
// Input
loop {
let size = terminal.size()?;
terminal.draw(|mut f| {
let selected_style = Style::default().fg(Color::Yellow).modifier(Modifier::Bold);
let normal_style = Style::default().fg(Color::White);
@ -73,7 +71,7 @@ fn main() -> Result<(), failure::Error> {
let rects = Layout::default()
.constraints([Constraint::Percentage(100)].as_ref())
.margin(5)
.split(size);
.split(f.size());
Table::new(header.into_iter(), rows)
.block(Block::default().borders(Borders::ALL).title("Table"))
.widths(&[10, 10, 10])

View file

@ -42,9 +42,8 @@ fn main() -> Result<(), failure::Error> {
// Main loop
loop {
let size = terminal.size()?;
terminal.draw(|mut f| {
let size = f.size();
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(5)

View file

@ -65,16 +65,13 @@ fn main() -> Result<(), failure::Error> {
let mut app = App::default();
loop {
// Handle resize
let size = terminal.size()?;
// Draw UI
terminal.draw(|mut f| {
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints([Constraint::Length(3), Constraint::Min(1)].as_ref())
.split(size);
.split(f.size());
Paragraph::new([Text::raw(&app.input)].iter())
.style(Style::default().fg(Color::Yellow))
.block(Block::default().borders(Borders::ALL).title("Input"))

View file

@ -19,9 +19,11 @@ where
current: usize,
/// Whether the cursor is currently hidden
hidden_cursor: bool,
prev_size: Option<Rect>,
/// Terminal size used for rendering.
known_size: Rect,
}
/// Represents a consistent terminal interface for rendering.
pub struct Frame<'a, B: 'a>
where
B: Backend,
@ -33,6 +35,11 @@ impl<'a, B> Frame<'a, B>
where
B: Backend,
{
/// Terminal size, guaranteed not to change when rendering.
pub fn size(&self) -> Rect {
self.terminal.known_size
}
/// Calls the draw method of a given widget on the current buffer
pub fn render<W>(&mut self, widget: &mut W, area: Rect)
where
@ -69,10 +76,11 @@ where
buffers: [Buffer::empty(size), Buffer::empty(size)],
current: 0,
hidden_cursor: false,
prev_size: None,
known_size: size,
})
}
/// Get a Frame object which provides a consistent view into the terminal state for rendering.
pub fn get_frame(&mut self) -> Frame<B> {
Frame { terminal: self }
}
@ -111,27 +119,36 @@ where
self.backend.draw(content)
}
/// Updates the interface so that internal buffers matches the current size of the terminal.
/// This leads to a full redraw of the screen.
/// Updates the Terminal so that internal buffers match the requested size. Requested size will
/// be saved so the size can remain consistent when rendering.
/// This leads to a full clear of the screen.
pub fn resize(&mut self, area: Rect) -> io::Result<()> {
self.buffers[self.current].resize(area);
self.buffers[1 - self.current].reset();
self.buffers[1 - self.current].resize(area);
self.prev_size = Some(area);
self.known_size = area;
self.backend.clear()
}
/// Flushes the current internal state and prepares the interface for the next draw call
/// Queries the backend for size and resizes if it doesn't match the previous size.
pub fn autoresize(&mut self) -> io::Result<()> {
let size = self.size()?;
if self.known_size != size {
self.resize(size)?;
}
Ok(())
}
/// Synchronizes terminal size, calls the rendering closure, flushes the current internal state
/// and prepares for the next draw call.
pub fn draw<F>(&mut self, f: F) -> io::Result<()>
where
F: FnOnce(Frame<B>),
{
// Autoresize - otherwise we get glitches if shrinking or potential desync between widgets
// and the terminal (if growing), which may OOB.
let size = self.size()?;
if self.prev_size != Some(size) {
self.resize(size)?;
}
self.autoresize()?;
f(self.get_frame());
// Draw to stdout
@ -159,6 +176,7 @@ where
pub fn clear(&mut self) -> io::Result<()> {
self.backend.clear()
}
/// Queries the real size of the backend.
pub fn size(&self) -> io::Result<Rect> {
self.backend.size()
}