mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-10 07:04:17 +00:00
docs: improve widgets documentation
This commit is contained in:
parent
da4d4e1672
commit
5d99b4af00
3 changed files with 183 additions and 11 deletions
22
src/lib.rs
22
src/lib.rs
|
@ -9,7 +9,7 @@
|
|||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! tui = "0.8"
|
||||
//! tui = "0.9"
|
||||
//! termion = "1.5"
|
||||
//! ```
|
||||
//!
|
||||
|
@ -19,8 +19,8 @@
|
|||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! crossterm = "0.14"
|
||||
//! tui = { version = "0.8", default-features = false, features = ['crossterm'] }
|
||||
//! crossterm = "0.17"
|
||||
//! tui = { version = "0.9", default-features = false, features = ['crossterm'] }
|
||||
//! ```
|
||||
//!
|
||||
//! The same logic applies for all other available backends.
|
||||
|
@ -45,16 +45,18 @@
|
|||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! If you had previously chosen `rustbox` as a backend, the terminal can be created in a similar
|
||||
//! If you had previously chosen `crossterm` as a backend, the terminal can be created in a similar
|
||||
//! way:
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! use std::io;
|
||||
//! use tui::Terminal;
|
||||
//! use tui::backend::RustboxBackend;
|
||||
//! use tui::backend::CrosstermBackend;
|
||||
//!
|
||||
//! fn main() -> Result<(), io::Error> {
|
||||
//! let backend = RustboxBackend::new()?;
|
||||
//! let mut terminal = Terminal::new(backend);
|
||||
//! let stdout = io::stdout();
|
||||
//! let backend = CrosstermBackend::new(stdout);
|
||||
//! let mut terminal = Terminal::new(backend)?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
|
@ -64,13 +66,13 @@
|
|||
//!
|
||||
//! ## Building a User Interface (UI)
|
||||
//!
|
||||
//! Every component of your interface will be implementing the `Widget` trait. The library comes
|
||||
//! Every component of your interface will be implementing the `Widget` trait. The library comes
|
||||
//! with a predefined set of widgets that should meet most of your use cases. You are also free to
|
||||
//! implement your own.
|
||||
//!
|
||||
//! Each widget follows a builder pattern API providing a default configuration along with methods
|
||||
//! to customize them. The widget is then registered using its `render` method that take a `Frame`
|
||||
//! instance and an area to draw to.
|
||||
//! to customize them. The widget is then rendered using the [`Frame::render_widget`] which take
|
||||
//! your widget instance an area to draw to.
|
||||
//!
|
||||
//! The following example renders a block of the size of the terminal:
|
||||
//!
|
||||
|
|
|
@ -40,7 +40,24 @@ where
|
|||
self.terminal.known_size
|
||||
}
|
||||
|
||||
/// Calls the draw method of a given widget on the current buffer
|
||||
/// Render a [`Widget`] to the current buffer using [`Widget::render`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use std::io;
|
||||
/// # use tui::Terminal;
|
||||
/// # use tui::backend::TermionBackend;
|
||||
/// # use tui::layout::Rect;
|
||||
/// # use tui::widgets::Block;
|
||||
/// # let stdout = io::stdout();
|
||||
/// # let backend = TermionBackend::new(stdout);
|
||||
/// # let mut terminal = Terminal::new(backend).unwrap();
|
||||
/// let block = Block::default();
|
||||
/// let area = Rect::new(0, 0, 5, 5);
|
||||
/// let mut frame = terminal.get_frame();
|
||||
/// frame.render_widget(block, area);
|
||||
/// ```
|
||||
pub fn render_widget<W>(&mut self, widget: W, area: Rect)
|
||||
where
|
||||
W: Widget,
|
||||
|
@ -48,6 +65,30 @@ where
|
|||
widget.render(area, self.terminal.current_buffer_mut());
|
||||
}
|
||||
|
||||
/// Render a [`StatefulWidget`] to the current buffer using [`StatefulWidget::render`].
|
||||
///
|
||||
/// The last argument should be an instance of the [`StatefulWidget::State`] associated to the
|
||||
/// given [`StatefulWidget`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use std::io;
|
||||
/// # use tui::Terminal;
|
||||
/// # use tui::backend::TermionBackend;
|
||||
/// # use tui::layout::Rect;
|
||||
/// # use tui::widgets::{List, ListState, Text};
|
||||
/// # let stdout = io::stdout();
|
||||
/// # let backend = TermionBackend::new(stdout);
|
||||
/// # let mut terminal = Terminal::new(backend).unwrap();
|
||||
/// let mut state = ListState::default();
|
||||
/// state.select(Some(1));
|
||||
/// let items = vec![Text::raw("Item 1"), Text::raw("Item 2")];
|
||||
/// let list = List::new(items.into_iter());
|
||||
/// let area = Rect::new(0, 0, 5, 5);
|
||||
/// let mut frame = terminal.get_frame();
|
||||
/// frame.render_stateful_widget(list, area, &mut state);
|
||||
/// ```
|
||||
pub fn render_stateful_widget<W>(&mut self, widget: W, area: Rect, state: &mut W::State)
|
||||
where
|
||||
W: StatefulWidget,
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
//! `widgets` is a collection of types that implement [`Widget`] or [`StatefulWidget`] or both.
|
||||
//!
|
||||
//! All widgets are implemented using the builder pattern and are consumable objects. They are not
|
||||
//! meant to be stored but used as *commands* to draw common figures in the UI.
|
||||
//!
|
||||
//! The available widgets are:
|
||||
//! - [`Block`]
|
||||
//! - [`Tabs`]
|
||||
//! - [`List`]
|
||||
//! - [`Table`]
|
||||
//! - [`Paragraph`]
|
||||
//! - [`Chart`]
|
||||
//! - [`BarChart`]
|
||||
//! - [`Gauge`]
|
||||
//! - [`Sparkline`]
|
||||
//! - [`Clear`]
|
||||
|
||||
use bitflags::bitflags;
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
@ -70,6 +87,118 @@ pub trait Widget {
|
|||
fn render(self, area: Rect, buf: &mut Buffer);
|
||||
}
|
||||
|
||||
/// A `StatefulWidget` is a widget that can take advantage of some local state to remember things
|
||||
/// between two draw calls.
|
||||
///
|
||||
/// Most widgets can be drawn directly based on the input parameters. However, some features may
|
||||
/// require some kind of associated state to be implemented.
|
||||
///
|
||||
/// For example, the [`List`] widget can highlight the item currently selected. This can be
|
||||
/// translated in an offset, which is the number of elements to skip in order to have the selected
|
||||
/// item within the viewport currently allocated to this widget. The widget can therefore only
|
||||
/// provide the following behavior: whenever the selected item is out of the viewport scroll to a
|
||||
/// predefined position (making the selected item the last viewable item or the one in the middle
|
||||
/// for example). Nonetheless, if the widget has access to the last computed offset then it can
|
||||
/// implement a natural scrolling experience where the last offset is reused until the selected
|
||||
/// item is out of the viewport.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use std::io;
|
||||
/// # use tui::Terminal;
|
||||
/// # use tui::backend::{Backend, TermionBackend};
|
||||
/// # use tui::widgets::{Widget, List, ListState, Text};
|
||||
///
|
||||
/// // Let's say we have some events to display.
|
||||
/// struct Events {
|
||||
/// // `items` is the state managed by your application.
|
||||
/// items: Vec<String>,
|
||||
/// // `state` is the state that can be modified by the UI. It stores the index of the selected
|
||||
/// // item as well as the offset computed during the previous draw call (used to implement
|
||||
/// // natural scrolling).
|
||||
/// state: ListState
|
||||
/// }
|
||||
///
|
||||
/// impl Events {
|
||||
/// fn new(items: Vec<String>) -> Events {
|
||||
/// Events {
|
||||
/// items,
|
||||
/// state: ListState::default(),
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// pub fn set_items(&mut self, items: Vec<String>) {
|
||||
/// self.items = items;
|
||||
/// // We reset the state as the associated items have changed. This effectively reset
|
||||
/// // the selection as well as the stored offset.
|
||||
/// self.state = ListState::default();
|
||||
/// }
|
||||
///
|
||||
/// // Select the next item. This will not be reflected until the widget is drawn in the
|
||||
/// // `Terminal::draw` callback using `Frame::render_stateful_widget`.
|
||||
/// pub fn next(&mut self) {
|
||||
/// let i = match self.state.selected() {
|
||||
/// Some(i) => {
|
||||
/// if i >= self.items.len() - 1 {
|
||||
/// 0
|
||||
/// } else {
|
||||
/// i + 1
|
||||
/// }
|
||||
/// }
|
||||
/// None => 0,
|
||||
/// };
|
||||
/// self.state.select(Some(i));
|
||||
/// }
|
||||
///
|
||||
/// // Select the previous item. This will not be reflected until the widget is drawn in the
|
||||
/// // `Terminal::draw` callback using `Frame::render_stateful_widget`.
|
||||
/// pub fn previous(&mut self) {
|
||||
/// let i = match self.state.selected() {
|
||||
/// Some(i) => {
|
||||
/// if i == 0 {
|
||||
/// self.items.len() - 1
|
||||
/// } else {
|
||||
/// i - 1
|
||||
/// }
|
||||
/// }
|
||||
/// None => 0,
|
||||
/// };
|
||||
/// self.state.select(Some(i));
|
||||
/// }
|
||||
///
|
||||
/// // Unselect the currently selected item if any. The implementation of `ListState` makes
|
||||
/// // sure that the stored offset is also reset.
|
||||
/// pub fn unselect(&mut self) {
|
||||
/// self.state.select(None);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let stdout = io::stdout();
|
||||
/// let backend = TermionBackend::new(stdout);
|
||||
/// let mut terminal = Terminal::new(backend).unwrap();
|
||||
///
|
||||
/// let mut events = Events::new(vec![
|
||||
/// String::from("Item 1"),
|
||||
/// String::from("Item 2")
|
||||
/// ]);
|
||||
///
|
||||
/// loop {
|
||||
/// terminal.draw(|mut f| {
|
||||
/// // The items managed by the application are transformed to something
|
||||
/// // that is understood by tui.
|
||||
/// let items = events.items.iter().map(Text::raw);
|
||||
/// // The `List` widget is then built with those items.
|
||||
/// let list = List::new(items);
|
||||
/// // Finally the widget is rendered using the associated state. `events.state` is
|
||||
/// // effectively the only thing that we will "remember" from this draw call.
|
||||
/// f.render_stateful_widget(list, f.size(), &mut events.state);
|
||||
/// });
|
||||
///
|
||||
/// // In response to some input events or an external http request or whatever:
|
||||
/// events.next();
|
||||
/// }
|
||||
/// ```
|
||||
pub trait StatefulWidget {
|
||||
type State;
|
||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State);
|
||||
|
|
Loading…
Reference in a new issue