Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions tui-scrollbar/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ crossterm_0_29 = ["dep:crossterm_0_29"]
color-eyre.workspace = true
ratatui = { workspace = true, default-features = true }

[[example]]
name = "scrollbar"
doc-scrape-examples = true # applies to all examples, not just this one

[[example]]
name = "scrollbar_mouse"
required-features = ["crossterm"]

[[example]]
name = "scrollbar_styled"
69 changes: 45 additions & 24 deletions tui-scrollbar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,19 @@ exposes a small interaction API plus pure metrics so apps can control behavior e
cargo add tui-scrollbar
```

## Important

- Zero lengths are treated as 1.
- Arrow endcaps are disabled by default; configure them with [`ScrollBarArrows`].
- The default [`GlyphSet`] hides the track using spaces; use [`GlyphSet::box_drawing`] or
[`GlyphSet::unicode`] for a visible track.
- The default glyphs use [Symbols for Legacy Computing] for missing upper/right eighth blocks.
Use [`GlyphSet::unicode`] if you need only standard Unicode block elements.

## Quick start

This example renders a vertical [`ScrollBar`] into a [`Buffer`] using a fixed track size and
offset. Use it as a minimal template when you just need a thumb and track on screen.
If you prefer named arguments, use [`ScrollLengths`].

```rust
use ratatui_core::buffer::Buffer;
Expand Down Expand Up @@ -80,7 +88,35 @@ Most apps update `offset` in response to input events and re-render each frame.
axis. For many apps, those units are items or lines. The ratio between `viewport_len` and
`content_len` is what matters, so any consistent unit works.

Zero lengths are treated as 1.
## Styling

Style the track, thumb, and arrow endcaps directly on [`ScrollBar`]. See [`ScrollBar`] for a
full method map and more focused examples.

Scrollbar glyphs are terminal characters. For visible track glyphs, thumb blocks, and arrow
symbols, `Style::fg` colors the glyph itself and `Style::bg` colors the cell behind it. The
default [`GlyphSet::minimal`] track renders spaces, so only the track background is visible in
empty track cells. Visible track glyph sets, such as [`GlyphSet::box_drawing`] and
[`GlyphSet::unicode`], can use foreground color for the track line. Thumb glyphs are block
characters, so `Style::fg` is usually the useful knob for thumb color; `Style::bg` still colors
the rest of the cell. With partial thumb glyphs, especially on a visible line track such as
[`GlyphSet::box_drawing`], that background can show at the ends of the thumb. Match the thumb
background to the track background unless that contrast is intentional.

```rust
use ratatui_core::style::{Color, Style};
use tui_scrollbar::{ScrollBar, ScrollBarArrows, ScrollLengths};

let lengths = ScrollLengths {
content_len: 120,
viewport_len: 30,
};
let scrollbar = ScrollBar::vertical(lengths)
.arrows(ScrollBarArrows::Both)
.track_style(Style::new().bg(Color::Black))
.thumb_style(Style::new().fg(Color::Rgb(255, 158, 100)))
.arrow_style(Style::new().fg(Color::Yellow).bg(Color::Black));
```

## Layout integration

Expand Down Expand Up @@ -159,9 +195,11 @@ assert!(metrics.thumb_len() >= SUBCELL);

## Glyph selection

The default glyphs include [Symbols for Legacy Computing] so the thumb can render upper/right
partial fills that are missing from the standard block set. Use [`GlyphSet`] when you want to
switch to a glyph set that avoids legacy symbols.
[`GlyphSet`] controls the track and thumb characters. The default glyphs include
[Symbols for Legacy Computing] so the thumb can render upper/right partial fills that are
missing from the standard block set. Use [`GlyphSet::box_drawing`] for a visible line track, or
[`GlyphSet::unicode`] when the terminal font should avoid [Symbols for Legacy Computing]
glyphs.

```rust
use tui_scrollbar::{GlyphSet, ScrollBar, ScrollLengths};
Expand Down Expand Up @@ -193,28 +231,10 @@ let scrollbar = ScrollBar::vertical(lengths).glyph_set(GlyphSet::unicode());
- [`PointerEvent`], [`PointerEventKind`], [`PointerButton`]
- [`ScrollWheel`], [`ScrollAxis`]

## Features

- `crossterm`: enables crossterm mouse events (latest supported version, currently `crossterm`
0.29).
- `crossterm_0_28`: enables crossterm mouse events using `crossterm` 0.28.
- `crossterm_0_29`: enables crossterm mouse events using `crossterm` 0.29.

When multiple crossterm versions are enabled, the latest one is used.
The selected version is re-exported as `tui_scrollbar::crossterm`.

## Important

- Zero lengths are treated as 1.
- Arrow endcaps are disabled by default; configure them with [`ScrollBarArrows`].
- The default [`GlyphSet`] hides the track using spaces; use [`GlyphSet::box_drawing`] or
[`GlyphSet::unicode`] for a visible track.
- The default glyphs use [Symbols for Legacy Computing] for missing upper/right eighth blocks.
Use [`GlyphSet::unicode`] if you need only standard Unicode block elements.

## See also

- [tui-scrollbar examples]
- [`scrollbar_styled` example]
- [`scrollbar_mouse` example]
- [`scrollbar` example]
- [`Widget`]
Expand Down Expand Up @@ -245,6 +265,7 @@ For the full suite of widgets, see [tui-widgets].
[Contributing]: https://github.com/ratatui/tui-widgets/blob/main/CONTRIBUTING.md
[Crate source]: https://github.com/ratatui/tui-widgets/blob/main/tui-scrollbar/src/lib.rs
[`scrollbar_mouse` example]: https://github.com/ratatui/tui-widgets/tree/main/tui-scrollbar/examples/scrollbar_mouse.rs
[`scrollbar_styled` example]: https://github.com/ratatui/tui-widgets/tree/main/tui-scrollbar/examples/scrollbar_styled.rs
[`scrollbar` example]: https://github.com/ratatui/tui-widgets/tree/main/tui-scrollbar/examples/scrollbar.rs
[tui-scrollbar examples]: https://github.com/ratatui/tui-widgets/tree/main/tui-scrollbar/examples
[`Buffer`]: ratatui_core::buffer::Buffer
Expand Down
73 changes: 73 additions & 0 deletions tui-scrollbar/examples/scrollbar_styled.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! Styled scrollbar showcase.
//!
//! This example renders a static layout with independently styled track, thumb, and arrow glyphs.

use std::time::Duration;

use color_eyre::Result;
use ratatui::layout::{Margin, Rect};
use ratatui::style::{Color, Modifier};
use ratatui::widgets::{Block, Borders, Paragraph};
use tui_scrollbar::{GlyphSet, ScrollBar, ScrollBarArrows, ScrollLengths};

fn main() -> Result<()> {
color_eyre::install()?;
let mut terminal = ratatui::init();
terminal.draw(|frame| render(frame.area(), frame))?;
std::thread::sleep(Duration::from_secs(3));
ratatui::restore();
Ok(())
}

fn render(area: Rect, frame: &mut ratatui::Frame) {
if area.width < 2 || area.height < 2 {
return;
}

let horizontal_bar = area
.rows()
.next_back()
.unwrap_or(area)
.inner(Margin::new(1, 0));
let vertical_bar = area
.columns()
.next_back()
.unwrap_or(area)
.inner(Margin::new(0, 1));

let block = Block::new()
.borders(Borders::ALL)
.title("styled scrollbars")
.border_style((Color::LightBlue, Color::Black))
.style((Color::Gray, Color::Black));
let content = block.inner(area).inner(Margin::new(2, 1));
frame.render_widget(block, area);

frame.render_widget(
Paragraph::new("track_style, thumb_style, and arrow_style can each use distinct colors")
.style((Color::Gray, Color::Black)),
content,
);

let lengths = ScrollLengths {
content_len: 160,
viewport_len: 40,
};
let horizontal = ScrollBar::horizontal(lengths)
.offset(48)
.arrows(ScrollBarArrows::Both)
.glyph_set(GlyphSet::box_drawing())
.track_style((Color::Blue, Color::Black).into())
.thumb_style((Color::Yellow, Modifier::BOLD).into())
.arrow_style((Color::LightGreen, Color::Black, Modifier::BOLD).into());
let vertical = ScrollBar::vertical(lengths)
.offset(80)
.arrows(ScrollBarArrows::Both)
.glyph_set(GlyphSet::box_drawing())
.track_style((Color::Magenta, Color::Black).into())
.thumb_style((Color::Cyan, Modifier::BOLD).into())
.arrow_style((Color::LightRed, Color::Black, Modifier::BOLD).into());

frame.render_widget(&horizontal, horizontal_bar);
frame.render_widget(&vertical, vertical_bar);
}
12 changes: 11 additions & 1 deletion tui-scrollbar/src/glyphs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub struct GlyphSet {
impl GlyphSet {
/// Minimal glyphs: no visible track by default.
///
/// Choose this when the thumb should stand out by color against a filled background instead of
/// a visible line.
///
/// This uses a space character for the track so the scrollbar is "all thumb", with the
/// background color coming from `track_style`.
///
Expand All @@ -53,6 +56,8 @@ impl GlyphSet {

/// Glyphs that include box-drawing line symbols for the track.
///
/// Choose this when callers should see a visible track line behind the thumb.
///
/// ```plain
/// [██──────]
/// [🮋█▏─────]
Expand All @@ -70,6 +75,9 @@ impl GlyphSet {

/// Glyphs that mix standard block elements with legacy supplement glyphs.
///
/// Choose this when the terminal font supports [Symbols for Legacy Computing] and you want
/// precise 1/8th-cell rendering on every thumb edge.
///
/// Use this to get full 1/8th coverage for upper and right edges that the standard block set
/// lacks; these glyphs come from [Symbols for Legacy Computing].
///
Expand Down Expand Up @@ -107,7 +115,7 @@ impl GlyphSet {

/// Glyphs using only standard Unicode block elements.
///
/// Use this if your font lacks the legacy glyphs.
/// Choose this if your font lacks the legacy glyphs.
///
/// The standard block set does not include 1/8th upper or right fills (those come from
/// [Symbols for Legacy Computing]), so this set approximates upper and right partials by
Expand All @@ -128,6 +136,8 @@ impl GlyphSet {
/// [▕█▉─────]
/// [─██─────]
/// ```
///
/// [Symbols for Legacy Computing]: https://en.wikipedia.org/wiki/Symbols_for_Legacy_Computing
pub const fn unicode() -> Self {
let vertical_lower = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
let vertical_upper = ['▔', '▔', '▀', '▀', '▀', '▀', '█', '█'];
Expand Down
69 changes: 45 additions & 24 deletions tui-scrollbar/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,19 @@
//! cargo add tui-scrollbar
//! ```
//!
//! # Important
//!
//! - Zero lengths are treated as 1.
//! - Arrow endcaps are disabled by default; configure them with [`ScrollBarArrows`].
//! - The default [`GlyphSet`] hides the track using spaces; use [`GlyphSet::box_drawing`] or
//! [`GlyphSet::unicode`] for a visible track.
//! - The default glyphs use [Symbols for Legacy Computing] for missing upper/right eighth blocks.
//! Use [`GlyphSet::unicode`] if you need only standard Unicode block elements.
//!
//! # Quick start
//!
//! This example renders a vertical [`ScrollBar`] into a [`Buffer`] using a fixed track size and
//! offset. Use it as a minimal template when you just need a thumb and track on screen.
//! If you prefer named arguments, use [`ScrollLengths`].
//!
//! ```rust
//! use ratatui_core::buffer::Buffer;
Expand Down Expand Up @@ -76,7 +84,35 @@
//! axis. For many apps, those units are items or lines. The ratio between `viewport_len` and
//! `content_len` is what matters, so any consistent unit works.
//!
//! Zero lengths are treated as 1.
//! # Styling
//!
//! Style the track, thumb, and arrow endcaps directly on [`ScrollBar`]. See [`ScrollBar`] for a
//! full method map and more focused examples.
//!
//! Scrollbar glyphs are terminal characters. For visible track glyphs, thumb blocks, and arrow
//! symbols, `Style::fg` colors the glyph itself and `Style::bg` colors the cell behind it. The
//! default [`GlyphSet::minimal`] track renders spaces, so only the track background is visible in
//! empty track cells. Visible track glyph sets, such as [`GlyphSet::box_drawing`] and
//! [`GlyphSet::unicode`], can use foreground color for the track line. Thumb glyphs are block
//! characters, so `Style::fg` is usually the useful knob for thumb color; `Style::bg` still colors
//! the rest of the cell. With partial thumb glyphs, especially on a visible line track such as
//! [`GlyphSet::box_drawing`], that background can show at the ends of the thumb. Match the thumb
//! background to the track background unless that contrast is intentional.
//!
//! ```rust
//! use ratatui_core::style::{Color, Style};
//! use tui_scrollbar::{ScrollBar, ScrollBarArrows, ScrollLengths};
//!
//! let lengths = ScrollLengths {
//! content_len: 120,
//! viewport_len: 30,
//! };
//! let scrollbar = ScrollBar::vertical(lengths)
//! .arrows(ScrollBarArrows::Both)
//! .track_style(Style::new().bg(Color::Black))
//! .thumb_style(Style::new().fg(Color::Rgb(255, 158, 100)))
//! .arrow_style(Style::new().fg(Color::Yellow).bg(Color::Black));
//! ```
//!
//! # Layout integration
//!
Expand Down Expand Up @@ -161,9 +197,11 @@
//!
//! # Glyph selection
//!
//! The default glyphs include [Symbols for Legacy Computing] so the thumb can render upper/right
//! partial fills that are missing from the standard block set. Use [`GlyphSet`] when you want to
//! switch to a glyph set that avoids legacy symbols.
//! [`GlyphSet`] controls the track and thumb characters. The default glyphs include
//! [Symbols for Legacy Computing] so the thumb can render upper/right partial fills that are
//! missing from the standard block set. Use [`GlyphSet::box_drawing`] for a visible line track, or
//! [`GlyphSet::unicode`] when the terminal font should avoid [Symbols for Legacy Computing]
//! glyphs.
//!
//! ```rust
//! use tui_scrollbar::{GlyphSet, ScrollBar, ScrollLengths};
Expand Down Expand Up @@ -195,28 +233,10 @@
//! - [`PointerEvent`], [`PointerEventKind`], [`PointerButton`]
//! - [`ScrollWheel`], [`ScrollAxis`]
//!
//! # Features
//!
//! - `crossterm`: enables crossterm mouse events (latest supported version, currently `crossterm`
//! 0.29).
//! - `crossterm_0_28`: enables crossterm mouse events using `crossterm` 0.28.
//! - `crossterm_0_29`: enables crossterm mouse events using `crossterm` 0.29.
//!
//! When multiple crossterm versions are enabled, the latest one is used.
//! The selected version is re-exported as `tui_scrollbar::crossterm`.
//!
//! # Important
//!
//! - Zero lengths are treated as 1.
//! - Arrow endcaps are disabled by default; configure them with [`ScrollBarArrows`].
//! - The default [`GlyphSet`] hides the track using spaces; use [`GlyphSet::box_drawing`] or
//! [`GlyphSet::unicode`] for a visible track.
//! - The default glyphs use [Symbols for Legacy Computing] for missing upper/right eighth blocks.
//! Use [`GlyphSet::unicode`] if you need only standard Unicode block elements.
//!
//! # See also
//!
//! - [tui-scrollbar examples]
//! - [`scrollbar_styled` example]
//! - [`scrollbar_mouse` example]
//! - [`scrollbar` example]
//! - [`Widget`]
Expand Down Expand Up @@ -247,6 +267,7 @@
//! [Contributing]: https://github.com/ratatui/tui-widgets/blob/main/CONTRIBUTING.md
//! [Crate source]: https://github.com/ratatui/tui-widgets/blob/main/tui-scrollbar/src/lib.rs
//! [`scrollbar_mouse` example]: https://github.com/ratatui/tui-widgets/tree/main/tui-scrollbar/examples/scrollbar_mouse.rs
//! [`scrollbar_styled` example]: https://github.com/ratatui/tui-widgets/tree/main/tui-scrollbar/examples/scrollbar_styled.rs
//! [`scrollbar` example]: https://github.com/ratatui/tui-widgets/tree/main/tui-scrollbar/examples/scrollbar.rs
//! [tui-scrollbar examples]: https://github.com/ratatui/tui-widgets/tree/main/tui-scrollbar/examples
//! [`Buffer`]: ratatui_core::buffer::Buffer
Expand Down
6 changes: 4 additions & 2 deletions tui-scrollbar/src/scrollbar/interaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ use crate::ScrollLengths;
impl ScrollBar {
/// Handles a backend-agnostic scrollbar event.
///
/// Returns a [`ScrollCommand`] when the event should update the offset.
/// Returns a [`ScrollCommand`] when the event should update the caller-owned offset. This
/// method does not mutate your application state directly.
///
/// Pointer events outside the track are ignored. Scroll wheel events are ignored unless the
/// axis matches the scrollbar orientation.
Expand Down Expand Up @@ -88,7 +89,8 @@ impl ScrollBar {
/// Handles crossterm mouse events for this scrollbar.
///
/// This helper converts crossterm events into [`ScrollEvent`] values before delegating to
/// [`Self::handle_event`].
/// [`Self::handle_event`]. See the `scrollbar_mouse` example for a complete terminal event
/// loop with mouse capture.
pub fn handle_mouse_event(
&self,
area: Rect,
Expand Down
Loading