pub struct Terminal<B>where
B: Backend,{ /* private fields */ }
Expand description
An interface to interact and draw Frame
s on the user’s terminal.
This is the main entry point for Ratatui. It is responsible for drawing and maintaining the state of the buffers, cursor and viewport.
The Terminal
is generic over a Backend
implementation which is used to interface with
the underlying terminal library. The Backend
trait is implemented for three popular Rust
terminal libraries: Crossterm, Termion and Termwiz. See the backend
module for more
information.
The Terminal
struct maintains two buffers: the current and the previous.
When the widgets are drawn, the changes are accumulated in the current buffer.
At the end of each draw pass, the two buffers are compared, and only the changes
between these buffers are written to the terminal, avoiding any redundant operations.
After flushing these changes, the buffers are swapped to prepare for the next draw cycle.
The terminal also has a viewport which is the area of the terminal that is currently visible to
the user. It can be either fullscreen, inline or fixed. See Viewport
for more information.
Applications should detect terminal resizes and call Terminal::draw
to redraw the
application with the new size. This will automatically resize the internal buffers to match the
new size for inline and fullscreen viewports. Fixed viewports are not resized automatically.
§Examples
use std::io::stdout;
use ratatui::{prelude::*, widgets::Paragraph};
let backend = CrosstermBackend::new(stdout());
let mut terminal = Terminal::new(backend)?;
terminal.draw(|frame| {
let area = frame.size();
frame.render_widget(Paragraph::new("Hello World!"), area);
frame.set_cursor(0, 0);
})?;
Implementations§
source§impl<B> Terminal<B>where
B: Backend,
impl<B> Terminal<B>where
B: Backend,
sourcepub fn new(backend: B) -> Result<Self>
pub fn new(backend: B) -> Result<Self>
Creates a new Terminal
with the given Backend
with a full screen viewport.
§Example
let backend = CrosstermBackend::new(stdout());
let terminal = Terminal::new(backend)?;
Examples found in repository?
More examples
- examples/block.rs
- examples/colors_rgb.rs
- examples/panic.rs
- examples/hello_world.rs
- examples/demo/termwiz.rs
- examples/demo/termion.rs
- examples/custom_widget.rs
- examples/layout.rs
- examples/popup.rs
- examples/table.rs
- examples/user_input.rs
- examples/calendar.rs
- examples/barchart.rs
- examples/chart.rs
- examples/paragraph.rs
- examples/sparkline.rs
- examples/scrollbar.rs
- examples/demo/crossterm.rs
sourcepub fn with_options(backend: B, options: TerminalOptions) -> Result<Self>
pub fn with_options(backend: B, options: TerminalOptions) -> Result<Self>
Creates a new Terminal
with the given Backend
and TerminalOptions
.
§Example
let backend = CrosstermBackend::new(stdout());
let viewport = Viewport::Fixed(Rect::new(0, 0, 10, 10));
let terminal = Terminal::with_options(backend, TerminalOptions { viewport })?;
Examples found in repository?
More examples
14 15 16 17 18 19 20 21 22 23 24 25 26
pub fn init() -> Result<Terminal<impl Backend>> {
// this size is to match the size of the terminal when running the demo
// using vhs in a 1280x640 sized window (github social preview size)
let options = TerminalOptions {
viewport: Viewport::Fixed(Rect::new(0, 0, 81, 18)),
};
let terminal = Terminal::with_options(CrosstermBackend::new(io::stdout()), options)?;
enable_raw_mode().context("enable raw mode")?;
stdout()
.execute(EnterAlternateScreen)
.wrap_err("enter alternate screen")?;
Ok(terminal)
}
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
fn main() -> Result<(), Box<dyn Error>> {
crossterm::terminal::enable_raw_mode()?;
let stdout = io::stdout();
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::with_options(
backend,
TerminalOptions {
viewport: Viewport::Inline(8),
},
)?;
let (tx, rx) = mpsc::channel();
input_handling(tx.clone());
let workers = workers(tx);
let mut downloads = downloads();
for w in &workers {
let d = downloads.next(w.id).unwrap();
w.tx.send(d).unwrap();
}
run_app(&mut terminal, workers, downloads, rx)?;
crossterm::terminal::disable_raw_mode()?;
terminal.clear()?;
Ok(())
}
sourcepub fn get_frame(&mut self) -> Frame<'_>
pub fn get_frame(&mut self) -> Frame<'_>
Get a Frame object which provides a consistent view into the terminal state for rendering.
sourcepub fn current_buffer_mut(&mut self) -> &mut Buffer
pub fn current_buffer_mut(&mut self) -> &mut Buffer
Gets the current buffer as a mutable reference.
sourcepub fn backend_mut(&mut self) -> &mut B
pub fn backend_mut(&mut self) -> &mut B
Gets the backend as a mutable reference
Examples found in repository?
More examples
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
// create app and run it
let res = run_app(&mut terminal);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
// create app and run it
let res = run_app(&mut terminal);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
// create app and run it
let app = App::new();
let res = run_app(&mut terminal, app);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
// create app and run it
let app = App::new();
let res = run_app(&mut terminal, app);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
sourcepub fn flush(&mut self) -> Result<()>
pub fn flush(&mut self) -> Result<()>
Obtains a difference between the previous and the current buffer and passes it to the current backend for drawing.
Examples found in repository?
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
pub fn run(tick_rate: Duration, enhanced_graphics: bool) -> Result<(), Box<dyn Error>> {
let backend = TermwizBackend::new()?;
let mut terminal = Terminal::new(backend)?;
terminal.hide_cursor()?;
// create app and run it
let app = App::new("Termwiz Demo", enhanced_graphics);
let res = run_app(&mut terminal, app, tick_rate);
terminal.show_cursor()?;
terminal.flush()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
sourcepub fn resize(&mut self, size: Rect) -> Result<()>
pub fn resize(&mut self, size: Rect) -> Result<()>
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.
sourcepub fn autoresize(&mut self) -> Result<()>
pub fn autoresize(&mut self) -> Result<()>
Queries the backend for size and resizes if it doesn’t match the previous size.
Examples found in repository?
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
fn run_app<B: Backend>(
terminal: &mut Terminal<B>,
workers: Vec<Worker>,
mut downloads: Downloads,
rx: mpsc::Receiver<Event>,
) -> Result<(), Box<dyn Error>> {
let mut redraw = true;
loop {
if redraw {
terminal.draw(|f| ui(f, &downloads))?;
}
redraw = true;
match rx.recv()? {
Event::Input(event) => {
if event.code == crossterm::event::KeyCode::Char('q') {
break;
}
}
Event::Resize => {
terminal.autoresize()?;
}
Event::Tick => {}
Event::DownloadUpdate(worker_id, _download_id, progress) => {
let download = downloads.in_progress.get_mut(&worker_id).unwrap();
download.progress = progress;
redraw = false;
}
Event::DownloadDone(worker_id, download_id) => {
let download = downloads.in_progress.remove(&worker_id).unwrap();
terminal.insert_before(1, |buf| {
Paragraph::new(Line::from(vec![
Span::from("Finished "),
Span::styled(
format!("download {download_id}"),
Style::default().add_modifier(Modifier::BOLD),
),
Span::from(format!(
" in {}ms",
download.started_at.elapsed().as_millis()
)),
]))
.render(buf.area, buf);
})?;
match downloads.next(worker_id) {
Some(d) => workers[worker_id].tx.send(d).unwrap(),
None => {
if downloads.in_progress.is_empty() {
terminal.insert_before(1, |buf| {
Paragraph::new("Done !").render(buf.area, buf);
})?;
break;
}
}
};
}
};
}
Ok(())
}
sourcepub fn draw<F>(&mut self, f: F) -> Result<CompletedFrame<'_>>
pub fn draw<F>(&mut self, f: F) -> Result<CompletedFrame<'_>>
Synchronizes terminal size, calls the rendering closure, flushes the current internal state and prepares for the next draw call.
This is the main entry point for drawing to the terminal.
The changes drawn to the frame are applied only to the current Buffer
. After the closure
returns, the current buffer is compared to the previous buffer and only the changes are
applied to the terminal.
§Examples
let backend = CrosstermBackend::new(stdout());
let mut terminal = Terminal::new(backend)?;
terminal.draw(|frame| {
let area = frame.size();
frame.render_widget(Paragraph::new("Hello World!"), area);
frame.set_cursor(0, 0);
})?;
Examples found in repository?
More examples
- examples/hello_world.rs
- examples/ratatui-logo.rs
- examples/colors_rgb.rs
- examples/layout.rs
- examples/demo2/app.rs
- examples/popup.rs
- examples/panic.rs
- examples/barchart.rs
- examples/chart.rs
- examples/paragraph.rs
- examples/sparkline.rs
- examples/table.rs
- examples/calendar.rs
- examples/demo/termion.rs
- examples/custom_widget.rs
- examples/canvas.rs
- examples/demo/crossterm.rs
- examples/user_input.rs
- examples/demo/termwiz.rs
- examples/scrollbar.rs
- examples/inline.rs
sourcepub fn hide_cursor(&mut self) -> Result<()>
pub fn hide_cursor(&mut self) -> Result<()>
Hides the cursor.
Examples found in repository?
More examples
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
pub fn run(tick_rate: Duration, enhanced_graphics: bool) -> Result<(), Box<dyn Error>> {
let backend = TermwizBackend::new()?;
let mut terminal = Terminal::new(backend)?;
terminal.hide_cursor()?;
// create app and run it
let app = App::new("Termwiz Demo", enhanced_graphics);
let res = run_app(&mut terminal, app, tick_rate);
terminal.show_cursor()?;
terminal.flush()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
sourcepub fn show_cursor(&mut self) -> Result<()>
pub fn show_cursor(&mut self) -> Result<()>
Shows the cursor.
Examples found in repository?
More examples
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
pub fn run(tick_rate: Duration, enhanced_graphics: bool) -> Result<(), Box<dyn Error>> {
let backend = TermwizBackend::new()?;
let mut terminal = Terminal::new(backend)?;
terminal.hide_cursor()?;
// create app and run it
let app = App::new("Termwiz Demo", enhanced_graphics);
let res = run_app(&mut terminal, app, tick_rate);
terminal.show_cursor()?;
terminal.flush()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
// create app and run it
let res = run_app(&mut terminal);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
// create app and run it
let res = run_app(&mut terminal);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
// create app and run it
let app = App::new();
let res = run_app(&mut terminal, app);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
// create app and run it
let app = App::new();
let res = run_app(&mut terminal, app);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
sourcepub fn get_cursor(&mut self) -> Result<(u16, u16)>
pub fn get_cursor(&mut self) -> Result<(u16, u16)>
Gets the current cursor position.
This is the position of the cursor after the last draw call and is returned as a tuple of
(x, y)
coordinates.
sourcepub fn clear(&mut self) -> Result<()>
pub fn clear(&mut self) -> Result<()>
Clear the terminal and force a full redraw on the next draw call.
Examples found in repository?
More examples
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
fn main() -> Result<(), Box<dyn Error>> {
crossterm::terminal::enable_raw_mode()?;
let stdout = io::stdout();
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::with_options(
backend,
TerminalOptions {
viewport: Viewport::Inline(8),
},
)?;
let (tx, rx) = mpsc::channel();
input_handling(tx.clone());
let workers = workers(tx);
let mut downloads = downloads();
for w in &workers {
let d = downloads.next(w.id).unwrap();
w.tx.send(d).unwrap();
}
run_app(&mut terminal, workers, downloads, rx)?;
crossterm::terminal::disable_raw_mode()?;
terminal.clear()?;
Ok(())
}
sourcepub fn swap_buffers(&mut self)
pub fn swap_buffers(&mut self)
Clears the inactive buffer and swaps it with the current buffer
sourcepub fn insert_before<F>(&mut self, height: u16, draw_fn: F) -> Result<()>
pub fn insert_before<F>(&mut self, height: u16, draw_fn: F) -> Result<()>
Insert some content before the current inline viewport. This has no effect when the viewport is fullscreen.
This function scrolls down the current viewport by the given height. The newly freed space
is then made available to the draw_fn
closure through a writable Buffer
.
Before:
+-------------------+
| |
| viewport |
| |
+-------------------+
After:
+-------------------+
| buffer |
+-------------------+
+-------------------+
| |
| viewport |
| |
+-------------------+
§Examples
§Insert a single line before the current viewport
terminal.insert_before(1, |buf| {
Paragraph::new(Line::from(vec![
Span::raw("This line will be added "),
Span::styled("before", Style::default().fg(Color::Blue)),
Span::raw(" the current viewport"),
]))
.render(buf.area, buf);
});
Examples found in repository?
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
fn run_app<B: Backend>(
terminal: &mut Terminal<B>,
workers: Vec<Worker>,
mut downloads: Downloads,
rx: mpsc::Receiver<Event>,
) -> Result<(), Box<dyn Error>> {
let mut redraw = true;
loop {
if redraw {
terminal.draw(|f| ui(f, &downloads))?;
}
redraw = true;
match rx.recv()? {
Event::Input(event) => {
if event.code == crossterm::event::KeyCode::Char('q') {
break;
}
}
Event::Resize => {
terminal.autoresize()?;
}
Event::Tick => {}
Event::DownloadUpdate(worker_id, _download_id, progress) => {
let download = downloads.in_progress.get_mut(&worker_id).unwrap();
download.progress = progress;
redraw = false;
}
Event::DownloadDone(worker_id, download_id) => {
let download = downloads.in_progress.remove(&worker_id).unwrap();
terminal.insert_before(1, |buf| {
Paragraph::new(Line::from(vec![
Span::from("Finished "),
Span::styled(
format!("download {download_id}"),
Style::default().add_modifier(Modifier::BOLD),
),
Span::from(format!(
" in {}ms",
download.started_at.elapsed().as_millis()
)),
]))
.render(buf.area, buf);
})?;
match downloads.next(worker_id) {
Some(d) => workers[worker_id].tx.send(d).unwrap(),
None => {
if downloads.in_progress.is_empty() {
terminal.insert_before(1, |buf| {
Paragraph::new("Done !").render(buf.area, buf);
})?;
break;
}
}
};
}
};
}
Ok(())
}
Trait Implementations§
source§impl<B> PartialEq for Terminal<B>
impl<B> PartialEq for Terminal<B>
impl<B> Eq for Terminal<B>
impl<B> StructuralPartialEq for Terminal<B>where
B: Backend,
Auto Trait Implementations§
impl<B> Freeze for Terminal<B>where
B: Freeze,
impl<B> RefUnwindSafe for Terminal<B>where
B: RefUnwindSafe,
impl<B> Send for Terminal<B>where
B: Send,
impl<B> Sync for Terminal<B>where
B: Sync,
impl<B> Unpin for Terminal<B>where
B: Unpin,
impl<B> UnwindSafe for Terminal<B>where
B: UnwindSafe,
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
source§impl<T> IntoEither for T
impl<T> IntoEither for T
source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moresource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more