docs(scrollbar): complete scrollbar documentation (#823)

This commit is contained in:
Valentin271 2024-01-15 18:37:55 +01:00 committed by GitHub
parent c959bd2881
commit 48b0380cb3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 114 additions and 24 deletions

View file

@ -62,22 +62,22 @@ fn run_app<B: Backend>(
if let Event::Key(key) = event::read()? { if let Event::Key(key) = event::read()? {
match key.code { match key.code {
KeyCode::Char('q') => return Ok(()), KeyCode::Char('q') => return Ok(()),
KeyCode::Char('j') => { KeyCode::Char('j') | KeyCode::Down => {
app.vertical_scroll = app.vertical_scroll.saturating_add(1); app.vertical_scroll = app.vertical_scroll.saturating_add(1);
app.vertical_scroll_state = app.vertical_scroll_state =
app.vertical_scroll_state.position(app.vertical_scroll); app.vertical_scroll_state.position(app.vertical_scroll);
} }
KeyCode::Char('k') => { KeyCode::Char('k') | KeyCode::Up => {
app.vertical_scroll = app.vertical_scroll.saturating_sub(1); app.vertical_scroll = app.vertical_scroll.saturating_sub(1);
app.vertical_scroll_state = app.vertical_scroll_state =
app.vertical_scroll_state.position(app.vertical_scroll); app.vertical_scroll_state.position(app.vertical_scroll);
} }
KeyCode::Char('h') => { KeyCode::Char('h') | KeyCode::Left => {
app.horizontal_scroll = app.horizontal_scroll.saturating_sub(1); app.horizontal_scroll = app.horizontal_scroll.saturating_sub(1);
app.horizontal_scroll_state = app.horizontal_scroll_state =
app.horizontal_scroll_state.position(app.horizontal_scroll); app.horizontal_scroll_state.position(app.horizontal_scroll);
} }
KeyCode::Char('l') => { KeyCode::Char('l') | KeyCode::Right => {
app.horizontal_scroll = app.horizontal_scroll.saturating_add(1); app.horizontal_scroll = app.horizontal_scroll.saturating_add(1);
app.horizontal_scroll_state = app.horizontal_scroll_state =
app.horizontal_scroll_state.position(app.horizontal_scroll); app.horizontal_scroll_state.position(app.horizontal_scroll);

View file

@ -1,3 +1,4 @@
#![warn(missing_docs)]
use strum::{Display, EnumString}; use strum::{Display, EnumString};
use super::StatefulWidget; use super::StatefulWidget;
@ -6,7 +7,11 @@ use crate::{
symbols::scrollbar::{Set, DOUBLE_HORIZONTAL, DOUBLE_VERTICAL}, symbols::scrollbar::{Set, DOUBLE_HORIZONTAL, DOUBLE_VERTICAL},
}; };
/// An enum representing the direction of scrolling in a Scrollbar widget. /// An enum representing a scrolling direction.
///
/// This is used with [`ScrollbarState::scroll`].
///
/// It is useful for example when you want to store in which direction to scroll.
#[derive(Debug, Default, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Default, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ScrollDirection { pub enum ScrollDirection {
/// Forward scroll direction, usually corresponds to scrolling downwards or rightwards. /// Forward scroll direction, usually corresponds to scrolling downwards or rightwards.
@ -44,37 +49,52 @@ pub enum ScrollDirection {
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ScrollbarState { pub struct ScrollbarState {
// The total length of the scrollable content. /// The total length of the scrollable content.
content_length: usize, content_length: usize,
// The current position within the scrollable content. /// The current position within the scrollable content.
position: usize, position: usize,
// The length of content in current viewport. /// The length of content in current viewport.
viewport_content_length: usize, viewport_content_length: usize,
} }
impl ScrollbarState { impl ScrollbarState {
/// Constructs a new ScrollbarState with the specified content length. /// Constructs a new ScrollbarState with the specified content length.
///
/// `content_length` is the total number of element, that can be scrolled. See
/// [`ScrollbarState`] for more details.
pub fn new(content_length: usize) -> Self { pub fn new(content_length: usize) -> Self {
Self { Self {
content_length, content_length,
..Default::default() ..Default::default()
} }
} }
/// Sets the scroll position of the scrollbar and returns the modified ScrollbarState.
/// Sets the scroll position of the scrollbar.
///
/// This represents the number of scrolled items.
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn position(mut self, position: usize) -> Self { pub fn position(mut self, position: usize) -> Self {
self.position = position; self.position = position;
self self
} }
/// Sets the length of the scrollable content and returns the modified ScrollbarState. /// Sets the length of the scrollable content.
///
/// This is the number of scrollable items. If items have a length of one, then this is the
/// same as the number of scrollable cells.
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn content_length(mut self, content_length: usize) -> Self { pub fn content_length(mut self, content_length: usize) -> Self {
self.content_length = content_length; self.content_length = content_length;
self self
} }
/// Sets the length of the viewport content and returns the modified ScrollbarState. /// Sets the items' size.
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn viewport_content_length(mut self, viewport_content_length: usize) -> Self { pub fn viewport_content_length(mut self, viewport_content_length: usize) -> Self {
self.viewport_content_length = viewport_content_length; self.viewport_content_length = viewport_content_length;
@ -91,7 +111,7 @@ impl ScrollbarState {
self.position = self self.position = self
.position .position
.saturating_add(1) .saturating_add(1)
.clamp(0, self.content_length.saturating_sub(1)) .min(self.content_length.saturating_sub(1))
} }
/// Sets the scroll position to the start of the scrollable content. /// Sets the scroll position to the start of the scrollable content.
@ -104,7 +124,7 @@ impl ScrollbarState {
self.position = self.content_length.saturating_sub(1) self.position = self.content_length.saturating_sub(1)
} }
/// Changes the scroll position based on the provided ScrollDirection. /// Changes the scroll position based on the provided [`ScrollDirection`].
pub fn scroll(&mut self, direction: ScrollDirection) { pub fn scroll(&mut self, direction: ScrollDirection) {
match direction { match direction {
ScrollDirection::Forward => { ScrollDirection::Forward => {
@ -117,19 +137,33 @@ impl ScrollbarState {
} }
} }
/// Scrollbar Orientation /// This is the position of the scrollbar around a given area.
///
/// ```plain
/// HorizontalTop
/// ┌───────┐
/// VerticalLeft│ │VerticalRight
/// └───────┘
/// HorizontalBottom
/// ```
#[derive(Debug, Default, Display, EnumString, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Default, Display, EnumString, Clone, Eq, PartialEq, Hash)]
pub enum ScrollbarOrientation { pub enum ScrollbarOrientation {
/// Positions the scrollbar on the right, scrolling vertically
#[default] #[default]
VerticalRight, VerticalRight,
/// Positions the scrollbar on the left, scrolling vertically
VerticalLeft, VerticalLeft,
/// Positions the scrollbar on the bottom, scrolling horizontally
HorizontalBottom, HorizontalBottom,
/// Positions the scrollbar on the top, scrolling horizontally
HorizontalTop, HorizontalTop,
} }
/// A widget to display a scrollbar /// A widget to display a scrollbar
/// ///
/// The following components of the scrollbar are customizable in symbol and style. /// The following components of the scrollbar are customizable in symbol and style. Note the
/// scrollbar is represented horizontally but it can also be set vertically (which is actually the
/// default).
/// ///
/// ```text /// ```text
/// <--▮-------> /// <--▮------->
@ -146,7 +180,6 @@ pub enum ScrollbarOrientation {
/// use ratatui::{prelude::*, widgets::*}; /// use ratatui::{prelude::*, widgets::*};
/// ///
/// # fn render_paragraph_with_scrollbar(frame: &mut Frame, area: Rect) { /// # fn render_paragraph_with_scrollbar(frame: &mut Frame, area: Rect) {
///
/// let vertical_scroll = 0; // from app state /// let vertical_scroll = 0; // from app state
/// ///
/// let items = vec![ /// let items = vec![
@ -158,20 +191,23 @@ pub enum ScrollbarOrientation {
/// .scroll((vertical_scroll as u16, 0)) /// .scroll((vertical_scroll as u16, 0))
/// .block(Block::new().borders(Borders::RIGHT)); // to show a background for the scrollbar /// .block(Block::new().borders(Borders::RIGHT)); // to show a background for the scrollbar
/// ///
/// let scrollbar = Scrollbar::default() /// let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
/// .orientation(ScrollbarOrientation::VerticalRight)
/// .begin_symbol(Some("↑")) /// .begin_symbol(Some("↑"))
/// .end_symbol(Some("↓")); /// .end_symbol(Some("↓"));
/// let mut scrollbar_state = ScrollbarState::new(items.iter().len()).position(vertical_scroll); ///
/// let mut scrollbar_state = ScrollbarState::new(items.len()).position(vertical_scroll);
/// ///
/// let area = frame.size(); /// let area = frame.size();
/// // Note we render the paragraph
/// frame.render_widget(paragraph, area); /// frame.render_widget(paragraph, area);
/// // and the scrollbar, those are separate widgets
/// frame.render_stateful_widget( /// frame.render_stateful_widget(
/// scrollbar, /// scrollbar,
/// area.inner(&Margin { /// area.inner(&Margin {
/// // using an inner vertical margin of 1 unit makes the scrollbar inside the block
/// vertical: 1, /// vertical: 1,
/// horizontal: 0, /// horizontal: 0,
/// }), // using a inner vertical margin of 1 unit makes the scrollbar inside the block /// }),
/// &mut scrollbar_state, /// &mut scrollbar_state,
/// ); /// );
/// # } /// # }
@ -206,12 +242,22 @@ impl<'a> Default for Scrollbar<'a> {
} }
impl<'a> Scrollbar<'a> { impl<'a> Scrollbar<'a> {
/// Creates a new scrollbar with the given position.
///
/// Most of the time you'll want [`ScrollbarOrientation::VerticalLeft`] or
/// [`ScrollbarOrientation::HorizontalBottom`]. See [`ScrollbarOrientation`] for more options.
pub fn new(orientation: ScrollbarOrientation) -> Self { pub fn new(orientation: ScrollbarOrientation) -> Self {
Self::default().orientation(orientation) Self::default().orientation(orientation)
} }
/// Sets the orientation of the scrollbar. /// Sets the position of the scrollbar.
/// Resets the symbols to [`DOUBLE_VERTICAL`] or [`DOUBLE_HORIZONTAL`] based on orientation ///
/// The orientation of the scrollbar is the position it will take around a [`Rect`]. See
/// [`ScrollbarOrientation`] for more details.
///
/// Resets the symbols to [`DOUBLE_VERTICAL`] or [`DOUBLE_HORIZONTAL`] based on orientation.
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn orientation(mut self, orientation: ScrollbarOrientation) -> Self { pub fn orientation(mut self, orientation: ScrollbarOrientation) -> Self {
self.orientation = orientation; self.orientation = orientation;
@ -224,6 +270,11 @@ impl<'a> Scrollbar<'a> {
} }
/// Sets the orientation and symbols for the scrollbar from a [`Set`]. /// Sets the orientation and symbols for the scrollbar from a [`Set`].
///
/// This has the same effect as calling [`Scrollbar::orientation`] and then
/// [`Scrollbar::symbols`]. See those for more details.
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn orientation_and_symbol(mut self, orientation: ScrollbarOrientation, set: Set) -> Self { pub fn orientation_and_symbol(mut self, orientation: ScrollbarOrientation, set: Set) -> Self {
self.orientation = orientation; self.orientation = orientation;
@ -231,16 +282,26 @@ impl<'a> Scrollbar<'a> {
} }
/// Sets the symbol that represents the thumb of the scrollbar. /// Sets the symbol that represents the thumb of the scrollbar.
///
/// The thumb is the handle representing the progression on the scrollbar. See [`Scrollbar`]
/// for a visual example of what this represents.
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn thumb_symbol(mut self, thumb_symbol: &'a str) -> Self { pub fn thumb_symbol(mut self, thumb_symbol: &'a str) -> Self {
self.thumb_symbol = thumb_symbol; self.thumb_symbol = thumb_symbol;
self self
} }
/// Sets the style that represents the thumb of the scrollbar. /// Sets the style on the scrollbar thumb.
///
/// The thumb is the handle representing the progression on the scrollbar. See [`Scrollbar`]
/// for a visual example of what this represents.
/// ///
/// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or /// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
/// your own type that implements [`Into<Style>`]). /// your own type that implements [`Into<Style>`]).
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn thumb_style<S: Into<Style>>(mut self, thumb_style: S) -> Self { pub fn thumb_style<S: Into<Style>>(mut self, thumb_style: S) -> Self {
self.thumb_style = thumb_style.into(); self.thumb_style = thumb_style.into();
@ -248,6 +309,10 @@ impl<'a> Scrollbar<'a> {
} }
/// Sets the symbol that represents the track of the scrollbar. /// Sets the symbol that represents the track of the scrollbar.
///
/// See [`Scrollbar`] for a visual example of what this represents.
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn track_symbol(mut self, track_symbol: Option<&'a str>) -> Self { pub fn track_symbol(mut self, track_symbol: Option<&'a str>) -> Self {
self.track_symbol = track_symbol; self.track_symbol = track_symbol;
@ -256,8 +321,12 @@ impl<'a> Scrollbar<'a> {
/// Sets the style that is used for the track of the scrollbar. /// Sets the style that is used for the track of the scrollbar.
/// ///
/// See [`Scrollbar`] for a visual example of what this represents.
///
/// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or /// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
/// your own type that implements [`Into<Style>`]). /// your own type that implements [`Into<Style>`]).
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn track_style<S: Into<Style>>(mut self, track_style: S) -> Self { pub fn track_style<S: Into<Style>>(mut self, track_style: S) -> Self {
self.track_style = track_style.into(); self.track_style = track_style.into();
@ -265,6 +334,10 @@ impl<'a> Scrollbar<'a> {
} }
/// Sets the symbol that represents the beginning of the scrollbar. /// Sets the symbol that represents the beginning of the scrollbar.
///
/// See [`Scrollbar`] for a visual example of what this represents.
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn begin_symbol(mut self, begin_symbol: Option<&'a str>) -> Self { pub fn begin_symbol(mut self, begin_symbol: Option<&'a str>) -> Self {
self.begin_symbol = begin_symbol; self.begin_symbol = begin_symbol;
@ -273,8 +346,12 @@ impl<'a> Scrollbar<'a> {
/// Sets the style that is used for the beginning of the scrollbar. /// Sets the style that is used for the beginning of the scrollbar.
/// ///
/// See [`Scrollbar`] for a visual example of what this represents.
///
/// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or /// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
/// your own type that implements [`Into<Style>`]). /// your own type that implements [`Into<Style>`]).
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn begin_style<S: Into<Style>>(mut self, begin_style: S) -> Self { pub fn begin_style<S: Into<Style>>(mut self, begin_style: S) -> Self {
self.begin_style = begin_style.into(); self.begin_style = begin_style.into();
@ -282,6 +359,10 @@ impl<'a> Scrollbar<'a> {
} }
/// Sets the symbol that represents the end of the scrollbar. /// Sets the symbol that represents the end of the scrollbar.
///
/// See [`Scrollbar`] for a visual example of what this represents.
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn end_symbol(mut self, end_symbol: Option<&'a str>) -> Self { pub fn end_symbol(mut self, end_symbol: Option<&'a str>) -> Self {
self.end_symbol = end_symbol; self.end_symbol = end_symbol;
@ -290,8 +371,12 @@ impl<'a> Scrollbar<'a> {
/// Sets the style that is used for the end of the scrollbar. /// Sets the style that is used for the end of the scrollbar.
/// ///
/// See [`Scrollbar`] for a visual example of what this represents.
///
/// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or /// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
/// your own type that implements [`Into<Style>`]). /// your own type that implements [`Into<Style>`]).
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn end_style<S: Into<Style>>(mut self, end_style: S) -> Self { pub fn end_style<S: Into<Style>>(mut self, end_style: S) -> Self {
self.end_style = end_style.into(); self.end_style = end_style.into();
@ -310,7 +395,10 @@ impl<'a> Scrollbar<'a> {
/// ``` /// ```
/// ///
/// Only sets begin_symbol, end_symbol and track_symbol if they already contain a value. /// Only sets begin_symbol, end_symbol and track_symbol if they already contain a value.
/// If they were set to `None` explicitly, this function will respect that choice. /// If they were set to `None` explicitly, this function will respect that choice. Use their
/// respective setters to change their value.
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn symbols(mut self, symbol: Set) -> Self { pub fn symbols(mut self, symbol: Set) -> Self {
self.thumb_symbol = symbol.thumb; self.thumb_symbol = symbol.thumb;
@ -339,6 +427,8 @@ impl<'a> Scrollbar<'a> {
/// │ └──────── thumb /// │ └──────── thumb
/// └─────────── begin /// └─────────── begin
/// ``` /// ```
///
/// This is a fluent setter method which must be chained or used as it consumes self
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn style<S: Into<Style>>(mut self, style: S) -> Self { pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
let style = style.into(); let style = style.into();