mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-10 07:04:17 +00:00
Frame: provide consistent size for rendering
This commit is contained in:
parent
8522e028f1
commit
228816f5f8
17 changed files with 50 additions and 55 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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);
|
||||
})?;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue