aboutsummaryrefslogtreecommitdiffstats
path: root/src/tui/widgets/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tui/widgets/mod.rs')
-rw-r--r--src/tui/widgets/mod.rs159
1 files changed, 159 insertions, 0 deletions
diff --git a/src/tui/widgets/mod.rs b/src/tui/widgets/mod.rs
new file mode 100644
index 00000000..635185e5
--- /dev/null
+++ b/src/tui/widgets/mod.rs
@@ -0,0 +1,159 @@
+//! `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`]
+//! - [`Paragraph`]
+
+mod block;
+mod paragraph;
+mod reflow;
+
+pub use self::block::{Block, BorderType};
+pub use self::paragraph::{Paragraph, Wrap};
+
+use crate::tui::{buffer::Buffer, layout::Rect};
+use bitflags::bitflags;
+
+bitflags! {
+ /// Bitflags that can be composed to set the visible borders essentially on the block widget.
+ pub struct Borders: u32 {
+ /// Show no border (default)
+ const NONE = 0b0000_0001;
+ /// Show the top border
+ const TOP = 0b0000_0010;
+ /// Show the right border
+ const RIGHT = 0b0000_0100;
+ /// Show the bottom border
+ const BOTTOM = 0b000_1000;
+ /// Show the left border
+ const LEFT = 0b0001_0000;
+ /// Show all borders
+ const ALL = Self::TOP.bits | Self::RIGHT.bits | Self::BOTTOM.bits | Self::LEFT.bits;
+ }
+}
+
+/// Base requirements for a Widget
+pub trait Widget {
+ /// Draws the current state of the widget in the given buffer. That is the only method required
+ /// to implement a custom 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, TestBackend};
+/// # use tui::widgets::{Widget, List, ListItem, ListState};
+///
+/// // 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 backend = TestBackend::new(5, 5);
+/// # let mut terminal = Terminal::new(backend).unwrap();
+///
+/// let mut events = Events::new(vec![
+/// String::from("Item 1"),
+/// String::from("Item 2")
+/// ]);
+///
+/// loop {
+/// terminal.draw(|f| {
+/// // The items managed by the application are transformed to something
+/// // that is understood by tui.
+/// let items: Vec<ListItem>= events.items.iter().map(|i| ListItem::new(i.as_ref())).collect();
+/// // 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);
+}