mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-22 04:33:13 +00:00
Better ergonomics for ScrollbarState and improved documentation (#456)
* feat(scrollbar): Better ergonomics for ScrollbarState and improved documentation ✨ * feat(scrollbar)!: Use usize instead of u16 for scrollbar ✨ 💥
This commit is contained in:
parent
927a5d8251
commit
0696f484e8
3 changed files with 90 additions and 26 deletions
|
@ -66,27 +66,23 @@ fn run_app<B: Backend>(
|
|||
KeyCode::Char('q') => return Ok(()),
|
||||
KeyCode::Char('j') => {
|
||||
app.vertical_scroll = app.vertical_scroll.saturating_add(1);
|
||||
app.vertical_scroll_state = app
|
||||
.vertical_scroll_state
|
||||
.position(app.vertical_scroll as u16);
|
||||
app.vertical_scroll_state =
|
||||
app.vertical_scroll_state.position(app.vertical_scroll);
|
||||
}
|
||||
KeyCode::Char('k') => {
|
||||
app.vertical_scroll = app.vertical_scroll.saturating_sub(1);
|
||||
app.vertical_scroll_state = app
|
||||
.vertical_scroll_state
|
||||
.position(app.vertical_scroll as u16);
|
||||
app.vertical_scroll_state =
|
||||
app.vertical_scroll_state.position(app.vertical_scroll);
|
||||
}
|
||||
KeyCode::Char('h') => {
|
||||
app.horizontal_scroll = app.horizontal_scroll.saturating_sub(1);
|
||||
app.horizontal_scroll_state = app
|
||||
.horizontal_scroll_state
|
||||
.position(app.horizontal_scroll as u16);
|
||||
app.horizontal_scroll_state =
|
||||
app.horizontal_scroll_state.position(app.horizontal_scroll);
|
||||
}
|
||||
KeyCode::Char('l') => {
|
||||
app.horizontal_scroll = app.horizontal_scroll.saturating_add(1);
|
||||
app.horizontal_scroll_state = app
|
||||
.horizontal_scroll_state
|
||||
.position(app.horizontal_scroll as u16);
|
||||
app.horizontal_scroll_state =
|
||||
app.horizontal_scroll_state.position(app.horizontal_scroll);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -151,10 +147,8 @@ fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
|||
),
|
||||
]),
|
||||
];
|
||||
app.vertical_scroll_state = app.vertical_scroll_state.content_length(text.len() as u16);
|
||||
app.horizontal_scroll_state = app
|
||||
.horizontal_scroll_state
|
||||
.content_length(long_line.len() as u16);
|
||||
app.vertical_scroll_state = app.vertical_scroll_state.content_length(text.len());
|
||||
app.horizontal_scroll_state = app.horizontal_scroll_state.content_length(long_line.len());
|
||||
|
||||
let create_block = |title| {
|
||||
Block::default()
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
//! - [`BarChart`]
|
||||
//! - [`Gauge`]
|
||||
//! - [`Sparkline`]
|
||||
//! - [`Scrollbar`]
|
||||
//! - [`calendar::Monthly`]
|
||||
//! - [`Clear`]
|
||||
|
||||
|
|
|
@ -20,10 +20,16 @@ pub enum ScrollDirection {
|
|||
|
||||
/// A struct representing the state of a Scrollbar widget.
|
||||
///
|
||||
/// # Important
|
||||
///
|
||||
/// It's essential to set the `content_length` field when using this struct. This field
|
||||
/// represents the total length of the scrollable content. The default value is zero
|
||||
/// which will result in the Scrollbar not rendering.
|
||||
///
|
||||
/// For example, in the following list, assume there are 4 bullet points:
|
||||
///
|
||||
/// - the `position` is 0
|
||||
/// - the `content_length` is 4
|
||||
/// - the `position` is 0
|
||||
/// - the `viewport_content_length` is 2
|
||||
///
|
||||
/// ```text
|
||||
|
@ -39,29 +45,36 @@ pub enum ScrollDirection {
|
|||
/// default of 0 and it'll use the track size as a `viewport_content_length`.
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub struct ScrollbarState {
|
||||
// The current position within the scrollable content.
|
||||
position: u16,
|
||||
// The total length of the scrollable content.
|
||||
content_length: u16,
|
||||
content_length: usize,
|
||||
// The current position within the scrollable content.
|
||||
position: usize,
|
||||
// The length of content in current viewport.
|
||||
viewport_content_length: u16,
|
||||
viewport_content_length: usize,
|
||||
}
|
||||
|
||||
impl ScrollbarState {
|
||||
/// Constructs a new ScrollbarState with the specified content length.
|
||||
pub fn new(content_length: usize) -> Self {
|
||||
Self {
|
||||
content_length,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
/// Sets the scroll position of the scrollbar and returns the modified ScrollbarState.
|
||||
pub fn position(mut self, position: u16) -> Self {
|
||||
pub fn position(mut self, position: usize) -> Self {
|
||||
self.position = position;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the length of the scrollable content and returns the modified ScrollbarState.
|
||||
pub fn content_length(mut self, content_length: u16) -> Self {
|
||||
pub fn content_length(mut self, content_length: usize) -> Self {
|
||||
self.content_length = content_length;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the length of the viewport content and returns the modified ScrollbarState.
|
||||
pub fn viewport_content_length(mut self, viewport_content_length: u16) -> Self {
|
||||
pub fn viewport_content_length(mut self, viewport_content_length: usize) -> Self {
|
||||
self.viewport_content_length = viewport_content_length;
|
||||
self
|
||||
}
|
||||
|
@ -124,6 +137,37 @@ pub enum ScrollbarOrientation {
|
|||
/// │ └──────── thumb
|
||||
/// └─────────── begin
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::prelude::*;
|
||||
/// # use ratatui::widgets::*;
|
||||
/// # fn render_paragraph_with_scrollbar<B: Backend>(frame: &mut Frame<B>, area: Rect) {
|
||||
///
|
||||
/// let vertical_scroll = 0; // from app state
|
||||
///
|
||||
/// let items = vec![Line::from("Item 1"), Line::from("Item 2"), Line::from("Item 3")];
|
||||
/// let paragraph = Paragraph::new(items.clone())
|
||||
/// .scroll((vertical_scroll as u16, 0))
|
||||
/// .block(Block::new().borders(Borders::RIGHT)); // to show a background for the scrollbar
|
||||
///
|
||||
/// let scrollbar = Scrollbar::default()
|
||||
/// .orientation(ScrollbarOrientation::VerticalRight)
|
||||
/// .begin_symbol(Some("↑"))
|
||||
/// .end_symbol(Some("↓"));
|
||||
/// let mut scrollbar_state = ScrollbarState::new(items.iter().len()).position(vertical_scroll);
|
||||
///
|
||||
/// let area = frame.size();
|
||||
/// frame.render_widget(paragraph, area);
|
||||
/// frame.render_stateful_widget(scrollbar,
|
||||
/// area.inner(&Margin {
|
||||
/// vertical: 1,
|
||||
/// horizontal: 0,
|
||||
/// }), // using a inner vertical margin of 1 unit makes the scrollbar inside the block
|
||||
/// &mut scrollbar_state);
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Scrollbar<'a> {
|
||||
orientation: ScrollbarOrientation,
|
||||
|
@ -312,7 +356,7 @@ impl<'a> Scrollbar<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn should_not_render(&self, track_start: u16, track_end: u16, content_length: u16) -> bool {
|
||||
fn should_not_render(&self, track_start: u16, track_end: u16, content_length: usize) -> bool {
|
||||
if track_end - track_start == 0 || content_length == 0 {
|
||||
return true;
|
||||
}
|
||||
|
@ -363,7 +407,7 @@ impl<'a> Scrollbar<'a> {
|
|||
let (track_start, track_end) = track_start_end;
|
||||
|
||||
let viewport_content_length = if state.viewport_content_length == 0 {
|
||||
track_end - track_start
|
||||
(track_end - track_start) as usize
|
||||
} else {
|
||||
state.viewport_content_length
|
||||
};
|
||||
|
@ -534,6 +578,31 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_renders_empty_with_content_length_is_zero() {
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 2, 8));
|
||||
let mut state = ScrollbarState::default().position(0);
|
||||
Scrollbar::default()
|
||||
.begin_symbol(None)
|
||||
.end_symbol(None)
|
||||
.render(buffer.area, &mut buffer, &mut state);
|
||||
assert_buffer_eq!(
|
||||
buffer,
|
||||
Buffer::with_lines(vec![" ", " ", " ", " ", " ", " ", " ", " "])
|
||||
);
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 2, 8));
|
||||
let mut state = ScrollbarState::new(8).position(0);
|
||||
Scrollbar::default()
|
||||
.begin_symbol(None)
|
||||
.end_symbol(None)
|
||||
.render(buffer.area, &mut buffer, &mut state);
|
||||
assert_buffer_eq!(
|
||||
buffer,
|
||||
Buffer::with_lines(vec![" █", " █", " █", " █", " █", " █", " █", " █"])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_render_when_area_zero() {
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 0, 0));
|
||||
|
|
Loading…
Reference in a new issue