diff --git a/README.md b/README.md index 5e7914058..fc9120c36 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Feature](https://github.com/ratatui-org/ratatui/issues/new?labels=enhancement&pr # Ratatui [Ratatui] is a crate for cooking up terminal user interfaces in rust. It is a lightweight -library that provides a set of [widgets] and utilities to build complex rust TUIs. Ratatui was +library that provides a set of widgets and utilities to build complex rust TUIs. Ratatui was forked from the [Tui-rs crate] in 2023 in order to continue its development. ## Installation @@ -55,17 +55,16 @@ Add `ratatui` and `crossterm` as dependencies to your cargo.toml: cargo add ratatui crossterm ``` -Ratatui uses the [Crossterm crate] by default as it works on most platforms. See the -[Installation] section of the [Ratatui Book] for more details on how to use other backends -(Termion / Termwiz). +Ratatui uses [Crossterm] by default as it works on most platforms. See the [Installation] +section of the [Ratatui Book] for more details on how to use other backends ([Termion] / +[Termwiz]). ## Introduction Ratatui is based on the principle of immediate rendering with intermediate buffers. This means that for each frame, your app must render all widgets that are supposed to be part of the UI. This is in contrast to the retained mode style of rendering where widgets are updated and then -automatically redrawn on the next frame. See the -[Rendering](https://ratatui.rs/concepts/rendering/index.html) section of the [Ratatui Book] for +automatically redrawn on the next frame. See the [Rendering] section of the [Ratatui Book] for more info. ## Other documentation @@ -73,8 +72,7 @@ more info. - [Ratatui Book] - explains the library's concepts and provides step-by-step tutorials - [Examples] - a collection of examples that demonstrate how to use the library. - [API Documentation] - the full API documentation for the library on docs.rs. -- [Changelog] - generated by [git-cliff](https://github.com/orhun/git-cliff) utilizing [Conventional - Commits](https://www.conventionalcommits.org/). +- [Changelog] - generated by [git-cliff] utilizing [Conventional Commits]. - [Contributing] - Please read this if you are interested in contributing to the project. ## Quickstart @@ -82,15 +80,12 @@ more info. The following example demonstrates the minimal amount of code necessary to setup a terminal and render "Hello World!". The full code for this example which contains a little more detail is in [hello_world.rs]. For more guidance on different ways to structure your application see the -[Application Patterns](https://ratatui.rs/concepts/application-patterns/index.html) and [Hello -World tutorial](https://ratatui-org.github.io/ratatui-book/tutorial/hello-world/index.html) -sections in the [Ratatui Book] and the various [Examples]. There are also several starter -templates available: +[Application Patterns] and [Hello World tutorial] sections in the [Ratatui Book] and the various +[Examples]. There are also several starter templates available: -- [rust-tui-template](https://github.com/ratatui-org/rust-tui-template). -- [ratatui-async-template](https://ratatui-org.github.io/ratatui-async-template/) (book and - template) -- [simple-tui-rs](https://github.com/pmsanford/simple-tui-rs) +- [rust-tui-template] +- [ratatui-async-template] (book and template) +- [simple-tui-rs] Every application built with `ratatui` needs to implement the following steps: @@ -100,95 +95,70 @@ Every application built with `ratatui` needs to implement the following steps: - Draw the UI - Restore the terminal state -```rust -use std::io; - -fn main() -> io::Result<()> { - let mut terminal = init_terminal()?; - let mut should_quit = false; - while !should_quit { - terminal.draw(ui)?; - should_quit = handle_events()?; - } - restore_terminal()?; - Ok(()) -} - -``` - The library contains a [`prelude`] module that re-exports the most commonly used traits and -types for convenience. Most examples in the documentation will use it instead of showing the +types for convenience. Most examples in the documentation will use this instead of showing the full path of each type. ### Initialize and restore the terminal -The [`Terminal`] type is a light abstraction over a choice of [`Backend`](backend::Backend) -implementations that provides functionality to draw each frame, clear the screen, hide the -cursor, etc. It is the main entry point of the library. It is parametrized over any type -implementing the [`Backend`](backend::Backend) trait. The [`backend`] module provides a few -implementations for the most common terminal libraries. +The [`Terminal`] type is the main entry point for any Ratatui application. It is a light +abstraction over a choice of [`Backend`] implementations that provides functionality to draw +each frame, clear the screen, hide the cursor, etc. It is parametrized over any type that +implements the [`Backend`] trait which has implementations for [Crossterm], [Termion] and +[Termwiz]. Most applications should enter the Alternate Screen when starting and leave it when exiting and -also enable raw mode to disable line buffering and enable reading key events. +also enable raw mode to disable line buffering and enable reading key events. See the [`backend` +module] and the [Backends] section of the [Ratatui Book] for more info. + +### Drawing the UI + +The drawing logic is delegated to a closure that takes a [`Frame`] instance as argument. The +[`Frame`] provides the size of the area to draw to and allows the app to render any [`Widget`] +using the provided [`render_widget`] method. See the [Widgets] section of the [Ratatui Book] for +more info. + +### Handling events + +Ratatui does not include any input handling. Instead event handling can be implemented by +calling backend library methods directly. See the [Handling Events] section of the [Ratatui +Book] for more info. For example, if you are using [Crossterm], you can use the +[`crossterm::event`] module to handle events. + +### Example ```rust -use std::io::{self, stdout, Stdout}; +use std::io::{self, stdout}; use crossterm::{ + event::{self, Event, KeyCode}, ExecutableCommand, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen} }; -use ratatui::prelude::*; +use ratatui::{prelude::*, widgets::*}; -fn init_terminal() -> io::Result>> { +fn main() -> io::Result<()> { enable_raw_mode()?; stdout().execute(EnterAlternateScreen)?; - let backend = CrosstermBackend::new(stdout()); - Terminal::new(backend) -} + let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; + + let mut should_quit = false; + while !should_quit { + terminal.draw(ui)?; + should_quit = handle_events()?; + } -fn restore_terminal() -> io::Result<()> { disable_raw_mode()?; stdout().execute(LeaveAlternateScreen)?; Ok(()) } -``` - -See the [`backend`] module and the [Backends](https://ratatui.rs/concepts/backends/index.html) -section of the [Ratatui Book] for more info. - -### Drawing the UI -The drawing logic is delegated to a closure that takes a [`Frame`] instance as argument. The -[`Frame`] provides the size of the area to draw to and allows the app to render any [`Widget`] -using the provided [`render_widget`] method. - -```rust -use std::io; -use ratatui::prelude::*; -use ratatui::widgets::{Block, Borders, Paragraph, Widget}; - -fn ui(f: &mut Frame) { - let area = f.size(); - let block = Block::default() - .title("Greeting") - .borders(Borders::ALL); - let text = Paragraph::new("Hello World!").block(block); +fn ui(frame: &mut Frame) { + frame.render_widget( + Paragraph::new("Hello World!") + .block(Block::default().title("Greeting").borders(Borders::ALL)), + frame.size(), + ); } -``` - -See the [Widgets](https://ratatui.rs/how-to/widgets/index.html) section of the [Ratatui Book] -for more info. - -### Handling events - -Ratatui does not include any input handling. Instead event handling can be implemented by -calling backend library methods directly. For example, if you are using the [Crossterm crate], -you can use the [`crossterm::event`] module to handle events. See the [Crossterm documentation] -for more details. - -```rust -use std::io; -use crossterm::event::{self, Event, KeyCode}; fn handle_events() -> io::Result { if event::poll(std::time::Duration::from_millis(50))? { @@ -202,100 +172,128 @@ fn handle_events() -> io::Result { } ``` -See the [Handling Events](https://ratatui.rs/concepts/event_handling.html) section of the -[Ratatui Book] for more info. +Running this example produces the following output: -### Layout +![docsrs-hello](https://github.com/ratatui-org/ratatui/assets/381361/9afccfe3-5f33-42e9-9a55-2d143af3b128) -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. The [`Layout`] struct allows you to split the available space into multiple -areas and then render widgets in each area. +## Layout -```rust -use ratatui::{prelude::*, widgets::*}; +The library comes with a basic yet useful layout management object called [`Layout`] which +allows you to split the available space into multiple areas and then render widgets in each +area. This lets you describe a responsive terminal UI by nesting layouts. See the [Layout] +section of the [Ratatui Book] for more info. -fn ui(frame: &mut Frame) { - let areas = Layout::default() - .direction(Direction::Vertical) - .margin(1) - .constraints(vec![ - Constraint::Length(1), - Constraint::Min(0), - Constraint::Length(1) - ]) - .split(frame.size()); - frame.render_widget(Paragraph::new("Title Bar"), areas[0]); - frame.render_widget(Paragraph::new("Status Bar"), areas[2]); - - let areas = Layout::default() - .direction(Direction::Horizontal) - .constraints(vec![ - Constraint::Percentage(50), - Constraint::Percentage(50), - ]) - .split(areas[1]); - frame.render_widget(Block::default().borders(Borders::ALL).title("Left"), areas[0]); - frame.render_widget(Block::default().borders(Borders::ALL).title("Right"), areas[1]); -} +```rust +let areas = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![ + Constraint::Length(1), + Constraint::Min(0), + Constraint::Length(1), + ]) + .split(frame.size()); +frame.render_widget(Paragraph::new("Title Bar"), areas[0]); +frame.render_widget(Paragraph::new("Status Bar"), areas[2]); + +let areas = Layout::default() + .direction(Direction::Horizontal) + .constraints(vec![Constraint::Percentage(50), Constraint::Percentage(50)]) + .split(areas[1]); +frame.render_widget( + Block::default().borders(Borders::ALL).title("Left"), + areas[0], +); +frame.render_widget( + Block::default().borders(Borders::ALL).title("Right"), + areas[1], +); ``` -This lets you describe responsive terminal UI by nesting layouts. You should note that by -default the computed layout tries to fill the available space completely. So if for any reason -you might need a blank space somewhere, pass an additional constraint as the last constraint -and don't use the corresponding area. +Running this example produces the following output: -See the [Layout](https://ratatui.rs/how-to/layout/index.html) section of the [Ratatui Book] for -more info. +![docsrs-layout](https://github.com/ratatui-org/ratatui/assets/381361/a18da2a3-1bf4-4939-a5e1-06f3e32bacd1) -### Text and styling +## Text and styling -Ratatui provides a [`Text`] struct that can be used to display text with basic styling. It -contains a list of [`Line`]s that can be created from a string or a [`Span`]. A [`Span`] is a -string with a specific style. [`Text`], [`Line`] and [`Span`] are the building blocks of the -library and are used in many places. +The [`Text`], [`Line`] and [`Span`] types are the building blocks of the library and are used in +many places. [`Text`] is a list of [`Line`]s and a [`Line`] is a list of [`Span`]s. A [`Span`] +is a string with a specific style. -Ratatui provides a basic styling system that allows you to customize the look and feel of your -application. The [`style` module] provides a [`Style`] struct that can be used to customize the -foreground and background colors and the text attributes of any text and various parts of each -widget. The [`style` module] also provides a [`Stylize`] trait that allows short-hand syntax to -apply a style to widgets and text. +The [`style` module] provides types that represent the various styling options. The most +important one is [`Style`] which represents the foreground and background colors and the text +attributes of a [`Span`]. The [`style` module] also provides a [`Stylize`] trait that allows +short-hand syntax to apply a style to widgets and text. See the [Styling Text] section of the +[Ratatui Book] for more info. ```rust use ratatui::{prelude::*, widgets::*}; -fn ui(frame: &mut Frame) { - // note that there are conversion methods that significantly simplify this code - let style = Style::default().fg(Color::Red).bg(Color::White).add_modifier(Modifier::BOLD); - // or shorter - let style = Style::new().red().on_white().bold(); - let span = Span::styled("Hello World!", style); - let line = Line::from(span); - let text = Text::from(vec![line]); - frame.render_widget(Paragraph::new(text), frame.size()); - // or using the short-hand syntax and implicit conversions - frame.render_widget(Paragraph::new("Hello World!".red().on_white().bold()), frame.size()); - - // to style the whole widget - frame.render_widget(Paragraph::new("Hello World!").style(style), frame.size()); - // or using the short-hand syntax - frame.render_widget(Paragraph::new("Hello World!").red().on_white(), frame.size()); -} +let areas = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![ + Constraint::Length(1), + Constraint::Length(1), + Constraint::Length(1), + Constraint::Length(1), + Constraint::Min(0), + ]) + .split(frame.size()); + +let span1 = Span::raw("Hello "); +let span2 = Span::styled( + "World", + Style::new() + .fg(Color::Green) + .bg(Color::White) + .add_modifier(Modifier::BOLD), +); +let span3 = "!".red().on_light_yellow().italic(); + +let line = Line::from(vec![span1, span2, span3]); +let text: Text = Text::from(vec![line]); + +frame.render_widget(Paragraph::new(text), areas[0]); +// or using the short-hand syntax and implicit conversions +frame.render_widget( + Paragraph::new("Hello World!".red().on_white().bold()), + areas[1], +); + +// to style the whole widget instead of just the text +frame.render_widget( + Paragraph::new("Hello World!").style(Style::new().red().on_white()), + areas[2], +); +// or using the short-hand syntax +frame.render_widget(Paragraph::new("Hello World!").blue().on_yellow(), areas[3]); ``` -See the [Styling Text](https://ratatui.rs/how-to/render/style-text.html) section of the [Ratatui -Book] for more info. +Running this example produces the following output: + +![docsrs-styling](https://github.com/ratatui-org/ratatui/assets/381361/c16024f7-3d36-4f66-973c-5892b69bca7f) [Ratatui Book]: https://ratatui.rs [Installation]: https://ratatui.rs/installation.html +[Rendering]: https://ratatui.rs/concepts/rendering/index.html +[Application Patterns]: https://ratatui.rs/concepts/application_patterns/index.html +[Hello World tutorial]: https://ratatui.rs/tutorial/hello_world.html +[Backends]: https://ratatui.rs/concepts/backends/index.html +[Widgets]: https://ratatui.rs/how-to/widgets/index.html +[Handling Events]: https://ratatui.rs/concepts/event_handling.html +[Layout]: https://ratatui.rs/how-to/layout/index.html +[Styling Text]: https://ratatui.rs/how-to/render/style-text.html +[rust-tui-template]: https://github.com/ratatui-org/rust-tui-template +[ratatui-async-template]: https://ratatui-org.github.io/ratatui-async-template/ +[simple-tui-rs]: https://github.com/pmsanford/simple-tui-rs [Examples]: https://github.com/ratatui-org/ratatui/tree/main/examples +[git-cliff]: https://github.com/orhun/git-cliff +[Conventional Commits]: https://www.conventionalcommits.org [API Documentation]: https://docs.rs/ratatui [Changelog]: https://github.com/ratatui-org/ratatui/blob/main/CHANGELOG.md [Contributing]: https:://github.com/ratatui-org/ratatui/blob/main/CONTRIBUTING.md [`Frame`]: terminal::Frame [`render_widget`]: terminal::Frame::render_widget [`Widget`]: widgets::Widget -[widgets]: widgets [`Layout`]: layout::Layout [`Text`]: text::Text [`Line`]: text::Line @@ -303,14 +301,13 @@ Book] for more info. [`Style`]: style::Style [`style` module]: style [`Stylize`]: style::Stylize -[`CrossTermBackend`]: backend::CrosstermBackend -[`TermionBackend`]: backend::TermionBackend -[`TermwizBackend`]: backend::TermwizBackend -[`calendar`]: widgets::calendar::Monthly +[`Backend`]: backend::Backend +[`backend` module]: backend [`crossterm::event`]: https://docs.rs/crossterm/latest/crossterm/event/index.html [Ratatui]: https://ratatui.rs -[Crossterm crate]: https://crates.io/crates/crossterm -[Crossterm documentation]: https://docs.rs/crossterm +[Crossterm]: https://crates.io/crates/crossterm +[Termion]: https://crates.io/crates/termion +[Termwiz]: https://crates.io/crates/termwiz [Tui-rs crate]: https://crates.io/crates/tui [hello_world.rs]: https://github.com/ratatui-org/ratatui/blob/main/examples/hello_world.rs [Crate Badge]: https://img.shields.io/crates/v/ratatui?logo=rust&style=flat-square diff --git a/examples/docsrs.rs b/examples/docsrs.rs new file mode 100644 index 000000000..8029a0764 --- /dev/null +++ b/examples/docsrs.rs @@ -0,0 +1,120 @@ +use std::io::{self, stdout}; + +use crossterm::{ + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, + ExecutableCommand, +}; +use ratatui::{prelude::*, widgets::*}; + +/// Example code for libr.rs +/// +/// When cargo-rdme supports doc comments that import from code, this will be imported +/// rather than copied to the lib.rs file. +fn main() -> io::Result<()> { + let arg = std::env::args().nth(1).unwrap_or_default(); + enable_raw_mode()?; + stdout().execute(EnterAlternateScreen)?; + let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; + + let mut should_quit = false; + while !should_quit { + terminal.draw(match arg.as_str() { + "hello_world" => hello_world, + "layout" => layout, + "styling" => styling, + _ => hello_world, + })?; + should_quit = handle_events()?; + } + + disable_raw_mode()?; + stdout().execute(LeaveAlternateScreen)?; + Ok(()) +} + +fn hello_world(frame: &mut Frame) { + frame.render_widget( + Paragraph::new("Hello World!") + .block(Block::default().title("Greeting").borders(Borders::ALL)), + frame.size(), + ); +} + +use crossterm::event::{self, Event, KeyCode}; +fn handle_events() -> io::Result { + if event::poll(std::time::Duration::from_millis(50))? { + if let Event::Key(key) = event::read()? { + if key.kind == event::KeyEventKind::Press && key.code == KeyCode::Char('q') { + return Ok(true); + } + } + } + Ok(false) +} + +fn layout(frame: &mut Frame) { + let areas = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![ + Constraint::Length(1), + Constraint::Min(0), + Constraint::Length(1), + ]) + .split(frame.size()); + frame.render_widget(Paragraph::new("Title Bar"), areas[0]); + frame.render_widget(Paragraph::new("Status Bar"), areas[2]); + + let areas = Layout::default() + .direction(Direction::Horizontal) + .constraints(vec![Constraint::Percentage(50), Constraint::Percentage(50)]) + .split(areas[1]); + frame.render_widget( + Block::default().borders(Borders::ALL).title("Left"), + areas[0], + ); + frame.render_widget( + Block::default().borders(Borders::ALL).title("Right"), + areas[1], + ); +} + +fn styling(frame: &mut Frame) { + let areas = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![ + Constraint::Length(1), + Constraint::Length(1), + Constraint::Length(1), + Constraint::Length(1), + Constraint::Min(0), + ]) + .split(frame.size()); + + let span1 = Span::raw("Hello "); + let span2 = Span::styled( + "World", + Style::new() + .fg(Color::Green) + .bg(Color::White) + .add_modifier(Modifier::BOLD), + ); + let span3 = "!".red().on_light_yellow().italic(); + + let line = Line::from(vec![span1, span2, span3]); + let text: Text = Text::from(vec![line]); + + frame.render_widget(Paragraph::new(text), areas[0]); + // or using the short-hand syntax and implicit conversions + frame.render_widget( + Paragraph::new("Hello World!".red().on_white().bold()), + areas[1], + ); + + // to style the whole widget instead of just the text + frame.render_widget( + Paragraph::new("Hello World!").style(Style::new().red().on_white()), + areas[2], + ); + // or using the short-hand syntax + frame.render_widget(Paragraph::new("Hello World!").blue().on_yellow(), areas[3]); +} diff --git a/examples/docsrs.tape b/examples/docsrs.tape new file mode 100644 index 000000000..f5708d504 --- /dev/null +++ b/examples/docsrs.tape @@ -0,0 +1,39 @@ +# This is a vhs script. See https://github.com/charmbracelet/vhs for more info. +# To run this script, install vhs and run `vhs ./examples/demo.tape` +# NOTE: Requires VHS 0.6.1 or later for Screenshot support +Output "target/docsrs.gif" +Set Theme "OceanicMaterial" +# The reason for this strange size is that the social preview image for this +# demo is 1280x64 with 80 pixels of padding on each side. We want a version +# without the padding for README.md, etc. +Set Width 640 +Set Height 160 +Set Padding 0 +Hide +Type "cargo run --example docsrs --features crossterm" +Enter +Sleep 2s +Show +Sleep 1s +Screenshot "target/docsrs-hello.png" +Sleep 1s +Hide +Type "q" +Type "cargo run --example docsrs --features crossterm -- layout" +Enter +Sleep 2s +Show +Sleep 1s +Screenshot "target/docsrs-layout.png" +Sleep 1s +Hide +Type "q" +Type "cargo run --example docsrs --features crossterm -- styling" +Enter +Sleep 2s +Show +Sleep 1s +Screenshot "target/docsrs-styling.png" +Sleep 1s +Hide +Type "q" diff --git a/src/lib.rs b/src/lib.rs index e7d58c853..a3b94e884 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ //! # Ratatui //! //! [Ratatui] is a crate for cooking up terminal user interfaces in rust. It is a lightweight -//! library that provides a set of [widgets] and utilities to build complex rust TUIs. Ratatui was +//! library that provides a set of widgets and utilities to build complex rust TUIs. Ratatui was //! forked from the [Tui-rs crate] in 2023 in order to continue its development. //! //! ## Installation @@ -34,17 +34,16 @@ //! cargo add ratatui crossterm //! ``` //! -//! Ratatui uses the [Crossterm crate] by default as it works on most platforms. See the -//! [Installation] section of the [Ratatui Book] for more details on how to use other backends -//! (Termion / Termwiz). +//! Ratatui uses [Crossterm] by default as it works on most platforms. See the [Installation] +//! section of the [Ratatui Book] for more details on how to use other backends ([Termion] / +//! [Termwiz]). //! //! ## Introduction //! //! Ratatui is based on the principle of immediate rendering with intermediate buffers. This means //! that for each frame, your app must render all widgets that are supposed to be part of the UI. //! This is in contrast to the retained mode style of rendering where widgets are updated and then -//! automatically redrawn on the next frame. See the -//! [Rendering](https://ratatui.rs/concepts/rendering/index.html) section of the [Ratatui Book] for +//! automatically redrawn on the next frame. See the [Rendering] section of the [Ratatui Book] for //! more info. //! //! ## Other documentation @@ -52,8 +51,7 @@ //! - [Ratatui Book] - explains the library's concepts and provides step-by-step tutorials //! - [Examples] - a collection of examples that demonstrate how to use the library. //! - [API Documentation] - the full API documentation for the library on docs.rs. -//! - [Changelog] - generated by [git-cliff](https://github.com/orhun/git-cliff) utilizing [Conventional -//! Commits](https://www.conventionalcommits.org/). +//! - [Changelog] - generated by [git-cliff] utilizing [Conventional Commits]. //! - [Contributing] - Please read this if you are interested in contributing to the project. //! //! ## Quickstart @@ -61,15 +59,12 @@ //! The following example demonstrates the minimal amount of code necessary to setup a terminal and //! render "Hello World!". The full code for this example which contains a little more detail is in //! [hello_world.rs]. For more guidance on different ways to structure your application see the -//! [Application Patterns](https://ratatui.rs/concepts/application-patterns/index.html) and [Hello -//! World tutorial](https://ratatui-org.github.io/ratatui-book/tutorial/hello-world/index.html) -//! sections in the [Ratatui Book] and the various [Examples]. There are also several starter -//! templates available: +//! [Application Patterns] and [Hello World tutorial] sections in the [Ratatui Book] and the various +//! [Examples]. There are also several starter templates available: //! -//! - [rust-tui-template](https://github.com/ratatui-org/rust-tui-template). -//! - [ratatui-async-template](https://ratatui-org.github.io/ratatui-async-template/) (book and -//! template) -//! - [simple-tui-rs](https://github.com/pmsanford/simple-tui-rs) +//! - [rust-tui-template] +//! - [ratatui-async-template] (book and template) +//! - [simple-tui-rs] //! //! Every application built with `ratatui` needs to implement the following steps: //! @@ -79,103 +74,70 @@ //! - Draw the UI //! - Restore the terminal state //! -//! ```rust,no_run -//! use std::io; -//! # use std::io::Stdout; -//! # use ratatui::prelude::*; -//! -//! fn main() -> io::Result<()> { -//! let mut terminal = init_terminal()?; -//! let mut should_quit = false; -//! while !should_quit { -//! terminal.draw(ui)?; -//! should_quit = handle_events()?; -//! } -//! restore_terminal()?; -//! Ok(()) -//! } -//! -//! # fn init_terminal() -> io::Result>> { -//! # unimplemented!() -//! # } -//! # fn ui(f: &mut Frame) {} -//! # fn handle_events() -> io::Result { Ok(false) } -//! # fn restore_terminal() -> io::Result<()> { Ok(()) } -//! ``` -//! //! The library contains a [`prelude`] module that re-exports the most commonly used traits and -//! types for convenience. Most examples in the documentation will use it instead of showing the +//! types for convenience. Most examples in the documentation will use this instead of showing the //! full path of each type. //! //! ### Initialize and restore the terminal //! -//! The [`Terminal`] type is a light abstraction over a choice of [`Backend`](backend::Backend) -//! implementations that provides functionality to draw each frame, clear the screen, hide the -//! cursor, etc. It is the main entry point of the library. It is parametrized over any type -//! implementing the [`Backend`](backend::Backend) trait. The [`backend`] module provides a few -//! implementations for the most common terminal libraries. +//! The [`Terminal`] type is the main entry point for any Ratatui application. It is a light +//! abstraction over a choice of [`Backend`] implementations that provides functionality to draw +//! each frame, clear the screen, hide the cursor, etc. It is parametrized over any type that +//! implements the [`Backend`] trait which has implementations for [Crossterm], [Termion] and +//! [Termwiz]. //! //! Most applications should enter the Alternate Screen when starting and leave it when exiting and -//! also enable raw mode to disable line buffering and enable reading key events. +//! also enable raw mode to disable line buffering and enable reading key events. See the [`backend` +//! module] and the [Backends] section of the [Ratatui Book] for more info. +//! +//! ### Drawing the UI +//! +//! The drawing logic is delegated to a closure that takes a [`Frame`] instance as argument. The +//! [`Frame`] provides the size of the area to draw to and allows the app to render any [`Widget`] +//! using the provided [`render_widget`] method. See the [Widgets] section of the [Ratatui Book] for +//! more info. +//! +//! ### Handling events +//! +//! Ratatui does not include any input handling. Instead event handling can be implemented by +//! calling backend library methods directly. See the [Handling Events] section of the [Ratatui +//! Book] for more info. For example, if you are using [Crossterm], you can use the +//! [`crossterm::event`] module to handle events. +//! +//! ### Example //! //! ```rust,no_run -//! use std::io::{self, stdout, Stdout}; +//! use std::io::{self, stdout}; //! use crossterm::{ +//! event::{self, Event, KeyCode}, //! ExecutableCommand, //! terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen} //! }; -//! use ratatui::prelude::*; +//! use ratatui::{prelude::*, widgets::*}; //! -//! fn init_terminal() -> io::Result>> { +//! fn main() -> io::Result<()> { //! enable_raw_mode()?; //! stdout().execute(EnterAlternateScreen)?; -//! let backend = CrosstermBackend::new(stdout()); -//! Terminal::new(backend) -//! } +//! let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; +//! +//! let mut should_quit = false; +//! while !should_quit { +//! terminal.draw(ui)?; +//! should_quit = handle_events()?; +//! } //! -//! fn restore_terminal() -> io::Result<()> { //! disable_raw_mode()?; //! stdout().execute(LeaveAlternateScreen)?; //! Ok(()) //! } -//! ``` -//! -//! See the [`backend`] module and the [Backends](https://ratatui.rs/concepts/backends/index.html) -//! section of the [Ratatui Book] for more info. -//! -//! ### Drawing the UI //! -//! The drawing logic is delegated to a closure that takes a [`Frame`] instance as argument. The -//! [`Frame`] provides the size of the area to draw to and allows the app to render any [`Widget`] -//! using the provided [`render_widget`] method. -//! -//! ```rust,no_run -//! use std::io; -//! use ratatui::prelude::*; -//! use ratatui::widgets::{Block, Borders, Paragraph, Widget}; -//! -//! fn ui(f: &mut Frame) { -//! let area = f.size(); -//! let block = Block::default() -//! .title("Greeting") -//! .borders(Borders::ALL); -//! let text = Paragraph::new("Hello World!").block(block); +//! fn ui(frame: &mut Frame) { +//! frame.render_widget( +//! Paragraph::new("Hello World!") +//! .block(Block::default().title("Greeting").borders(Borders::ALL)), +//! frame.size(), +//! ); //! } -//! ``` -//! -//! See the [Widgets](https://ratatui.rs/how-to/widgets/index.html) section of the [Ratatui Book] -//! for more info. -//! -//! ### Handling events -//! -//! Ratatui does not include any input handling. Instead event handling can be implemented by -//! calling backend library methods directly. For example, if you are using the [Crossterm crate], -//! you can use the [`crossterm::event`] module to handle events. See the [Crossterm documentation] -//! for more details. -//! -//! ```rust,no_run -//! use std::io; -//! use crossterm::event::{self, Event, KeyCode}; //! //! fn handle_events() -> io::Result { //! if event::poll(std::time::Duration::from_millis(50))? { @@ -189,102 +151,151 @@ //! } //! ``` //! -//! See the [Handling Events](https://ratatui.rs/concepts/event_handling.html) section of the -//! [Ratatui Book] for more info. +//! Running this example produces the following output: //! -//! ### Layout +//! ![docsrs-hello](https://github.com/ratatui-org/ratatui/assets/381361/9afccfe3-5f33-42e9-9a55-2d143af3b128) //! -//! 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. The [`Layout`] struct allows you to split the available space into multiple -//! areas and then render widgets in each area. +//! ## Layout //! -//! ```rust,no_run -//! use ratatui::{prelude::*, widgets::*}; +//! The library comes with a basic yet useful layout management object called [`Layout`] which +//! allows you to split the available space into multiple areas and then render widgets in each +//! area. This lets you describe a responsive terminal UI by nesting layouts. See the [Layout] +//! section of the [Ratatui Book] for more info. //! -//! fn ui(frame: &mut Frame) { -//! let areas = Layout::default() -//! .direction(Direction::Vertical) -//! .margin(1) -//! .constraints(vec![ -//! Constraint::Length(1), -//! Constraint::Min(0), -//! Constraint::Length(1) -//! ]) -//! .split(frame.size()); -//! frame.render_widget(Paragraph::new("Title Bar"), areas[0]); -//! frame.render_widget(Paragraph::new("Status Bar"), areas[2]); -//! -//! let areas = Layout::default() -//! .direction(Direction::Horizontal) -//! .constraints(vec![ -//! Constraint::Percentage(50), -//! Constraint::Percentage(50), -//! ]) -//! .split(areas[1]); -//! frame.render_widget(Block::default().borders(Borders::ALL).title("Left"), areas[0]); -//! frame.render_widget(Block::default().borders(Borders::ALL).title("Right"), areas[1]); -//! } +//! ```rust,no_run +//! # use ratatui::{prelude::*, widgets::*}; +//! # fn ui(frame: &mut Frame) { +//! let areas = Layout::default() +//! .direction(Direction::Vertical) +//! .constraints(vec![ +//! Constraint::Length(1), +//! Constraint::Min(0), +//! Constraint::Length(1), +//! ]) +//! .split(frame.size()); +//! frame.render_widget(Paragraph::new("Title Bar"), areas[0]); +//! frame.render_widget(Paragraph::new("Status Bar"), areas[2]); +//! +//! let areas = Layout::default() +//! .direction(Direction::Horizontal) +//! .constraints(vec![Constraint::Percentage(50), Constraint::Percentage(50)]) +//! .split(areas[1]); +//! frame.render_widget( +//! Block::default().borders(Borders::ALL).title("Left"), +//! areas[0], +//! ); +//! frame.render_widget( +//! Block::default().borders(Borders::ALL).title("Right"), +//! areas[1], +//! ); +//! # } //! ``` //! -//! This lets you describe responsive terminal UI by nesting layouts. You should note that by -//! default the computed layout tries to fill the available space completely. So if for any reason -//! you might need a blank space somewhere, pass an additional constraint as the last constraint -//! and don't use the corresponding area. +//! Running this example produces the following output: //! -//! See the [Layout](https://ratatui.rs/how-to/layout/index.html) section of the [Ratatui Book] for -//! more info. +//! ![docsrs-layout](https://github.com/ratatui-org/ratatui/assets/381361/a18da2a3-1bf4-4939-a5e1-06f3e32bacd1) //! -//! ### Text and styling +//! ## Text and styling //! -//! Ratatui provides a [`Text`] struct that can be used to display text with basic styling. It -//! contains a list of [`Line`]s that can be created from a string or a [`Span`]. A [`Span`] is a -//! string with a specific style. [`Text`], [`Line`] and [`Span`] are the building blocks of the -//! library and are used in many places. +//! The [`Text`], [`Line`] and [`Span`] types are the building blocks of the library and are used in +//! many places. [`Text`] is a list of [`Line`]s and a [`Line`] is a list of [`Span`]s. A [`Span`] +//! is a string with a specific style. //! -//! Ratatui provides a basic styling system that allows you to customize the look and feel of your -//! application. The [`style` module] provides a [`Style`] struct that can be used to customize the -//! foreground and background colors and the text attributes of any text and various parts of each -//! widget. The [`style` module] also provides a [`Stylize`] trait that allows short-hand syntax to -//! apply a style to widgets and text. +//! The [`style` module] provides types that represent the various styling options. The most +//! important one is [`Style`] which represents the foreground and background colors and the text +//! attributes of a [`Span`]. The [`style` module] also provides a [`Stylize`] trait that allows +//! short-hand syntax to apply a style to widgets and text. See the [Styling Text] section of the +//! [Ratatui Book] for more info. //! //! ```rust,no_run //! use ratatui::{prelude::*, widgets::*}; //! -//! fn ui(frame: &mut Frame) { -//! // note that there are conversion methods that significantly simplify this code -//! let style = Style::default().fg(Color::Red).bg(Color::White).add_modifier(Modifier::BOLD); -//! // or shorter -//! let style = Style::new().red().on_white().bold(); -//! let span = Span::styled("Hello World!", style); -//! let line = Line::from(span); -//! let text = Text::from(vec![line]); -//! frame.render_widget(Paragraph::new(text), frame.size()); -//! // or using the short-hand syntax and implicit conversions -//! frame.render_widget(Paragraph::new("Hello World!".red().on_white().bold()), frame.size()); -//! -//! // to style the whole widget -//! frame.render_widget(Paragraph::new("Hello World!").style(style), frame.size()); -//! // or using the short-hand syntax -//! frame.render_widget(Paragraph::new("Hello World!").red().on_white(), frame.size()); -//! } +//! # fn ui(frame: &mut Frame) { +//! let areas = Layout::default() +//! .direction(Direction::Vertical) +//! .constraints(vec![ +//! Constraint::Length(1), +//! Constraint::Length(1), +//! Constraint::Length(1), +//! Constraint::Length(1), +//! Constraint::Min(0), +//! ]) +//! .split(frame.size()); +//! +//! let span1 = Span::raw("Hello "); +//! let span2 = Span::styled( +//! "World", +//! Style::new() +//! .fg(Color::Green) +//! .bg(Color::White) +//! .add_modifier(Modifier::BOLD), +//! ); +//! let span3 = "!".red().on_light_yellow().italic(); +//! +//! let line = Line::from(vec![span1, span2, span3]); +//! let text: Text = Text::from(vec![line]); +//! +//! frame.render_widget(Paragraph::new(text), areas[0]); +//! // or using the short-hand syntax and implicit conversions +//! frame.render_widget( +//! Paragraph::new("Hello World!".red().on_white().bold()), +//! areas[1], +//! ); +//! +//! // to style the whole widget instead of just the text +//! frame.render_widget( +//! Paragraph::new("Hello World!").style(Style::new().red().on_white()), +//! areas[2], +//! ); +//! // or using the short-hand syntax +//! frame.render_widget(Paragraph::new("Hello World!").blue().on_yellow(), areas[3]); +//! # } //! ``` //! -//! See the [Styling Text](https://ratatui.rs/how-to/render/style-text.html) section of the [Ratatui -//! Book] for more info. +//! Running this example produces the following output: +//! +//! ![docsrs-styling](https://github.com/ratatui-org/ratatui/assets/381361/c16024f7-3d36-4f66-973c-5892b69bca7f) #![cfg_attr(feature = "document-features", doc = "\n## Features")] #![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +#![cfg_attr( + feature = "document-features", + doc = "[`CrossTermBackend`]: backend::CrosstermBackend" +)] +#![cfg_attr( + feature = "document-features", + doc = "[`TermionBackend`]: backend::TermionBackend" +)] +#![cfg_attr( + feature = "document-features", + doc = "[`TermwizBackend`]: backend::TermwizBackend" +)] +#![cfg_attr( + feature = "document-features", + doc = "[`calendar`]: widgets::calendar::Monthly" +)] //! //! [Ratatui Book]: https://ratatui.rs //! [Installation]: https://ratatui.rs/installation.html +//! [Rendering]: https://ratatui.rs/concepts/rendering/index.html +//! [Application Patterns]: https://ratatui.rs/concepts/application_patterns/index.html +//! [Hello World tutorial]: https://ratatui.rs/tutorial/hello_world.html +//! [Backends]: https://ratatui.rs/concepts/backends/index.html +//! [Widgets]: https://ratatui.rs/how-to/widgets/index.html +//! [Handling Events]: https://ratatui.rs/concepts/event_handling.html +//! [Layout]: https://ratatui.rs/how-to/layout/index.html +//! [Styling Text]: https://ratatui.rs/how-to/render/style-text.html +//! [rust-tui-template]: https://github.com/ratatui-org/rust-tui-template +//! [ratatui-async-template]: https://ratatui-org.github.io/ratatui-async-template/ +//! [simple-tui-rs]: https://github.com/pmsanford/simple-tui-rs //! [Examples]: https://github.com/ratatui-org/ratatui/tree/main/examples +//! [git-cliff]: https://github.com/orhun/git-cliff +//! [Conventional Commits]: https://www.conventionalcommits.org //! [API Documentation]: https://docs.rs/ratatui //! [Changelog]: https://github.com/ratatui-org/ratatui/blob/main/CHANGELOG.md //! [Contributing]: https:://github.com/ratatui-org/ratatui/blob/main/CONTRIBUTING.md //! [`Frame`]: terminal::Frame //! [`render_widget`]: terminal::Frame::render_widget //! [`Widget`]: widgets::Widget -//! [widgets]: widgets //! [`Layout`]: layout::Layout //! [`Text`]: text::Text //! [`Line`]: text::Line @@ -292,14 +303,13 @@ //! [`Style`]: style::Style //! [`style` module]: style //! [`Stylize`]: style::Stylize -//! [`CrossTermBackend`]: backend::CrosstermBackend -//! [`TermionBackend`]: backend::TermionBackend -//! [`TermwizBackend`]: backend::TermwizBackend -//! [`calendar`]: widgets::calendar::Monthly +//! [`Backend`]: backend::Backend +//! [`backend` module]: backend //! [`crossterm::event`]: https://docs.rs/crossterm/latest/crossterm/event/index.html //! [Ratatui]: https://ratatui.rs -//! [Crossterm crate]: https://crates.io/crates/crossterm -//! [Crossterm documentation]: https://docs.rs/crossterm +//! [Crossterm]: https://crates.io/crates/crossterm +//! [Termion]: https://crates.io/crates/termion +//! [Termwiz]: https://crates.io/crates/termwiz //! [Tui-rs crate]: https://crates.io/crates/tui //! [hello_world.rs]: https://github.com/ratatui-org/ratatui/blob/main/examples/hello_world.rs //! [Crate Badge]: https://img.shields.io/crates/v/ratatui?logo=rust&style=flat-square