mirror of
https://github.com/ratatui/ratatui.git
synced 2025-10-02 15:25:54 +00:00
docs(examples): add colors_rgb example (#476)
This commit is contained in:
parent
17797d83da
commit
6b8725f091
@ -48,6 +48,7 @@ document-features = { version = "0.2.7", optional = true }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = "1.0.71"
|
anyhow = "1.0.71"
|
||||||
argh = "0.1"
|
argh = "0.1"
|
||||||
|
better-panic = "0.3.0"
|
||||||
cargo-husky = { version = "1.5.0", default-features = false, features = [
|
cargo-husky = { version = "1.5.0", default-features = false, features = [
|
||||||
"user-hooks",
|
"user-hooks",
|
||||||
] }
|
] }
|
||||||
@ -132,6 +133,11 @@ required-features = ["crossterm"]
|
|||||||
# this example is a bit verbose, so we don't want to include it in the docs
|
# this example is a bit verbose, so we don't want to include it in the docs
|
||||||
doc-scrape-examples = false
|
doc-scrape-examples = false
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "colors_rgb"
|
||||||
|
required-features = ["crossterm"]
|
||||||
|
doc-scrape-examples = true
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "custom_widget"
|
name = "custom_widget"
|
||||||
required-features = ["crossterm"]
|
required-features = ["crossterm"]
|
||||||
|
@ -98,6 +98,18 @@ cargo run --example=colors --features=crossterm
|
|||||||
|
|
||||||
![Colors][colors.gif]
|
![Colors][colors.gif]
|
||||||
|
|
||||||
|
## Colors (RGB)
|
||||||
|
|
||||||
|
Demonstrates the available RGB
|
||||||
|
[`Color`](https://docs.rs/ratatui/latest/ratatui/style/enum.Color.html) options. These can be used
|
||||||
|
in any style field. Source: [colors_rgb.rs](./colors_rgb.rs).
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo run --example=colors_rgb --features=crossterm
|
||||||
|
```
|
||||||
|
|
||||||
|
![Colors RGB][colors_rgb.gif]
|
||||||
|
|
||||||
## Custom Widget
|
## Custom Widget
|
||||||
|
|
||||||
Demonstrates how to implement the
|
Demonstrates how to implement the
|
||||||
@ -286,6 +298,7 @@ examples/generate.bash
|
|||||||
[canvas.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/canvas.gif?raw=true
|
[canvas.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/canvas.gif?raw=true
|
||||||
[chart.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/chart.gif?raw=true
|
[chart.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/chart.gif?raw=true
|
||||||
[colors.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/colors.gif?raw=true
|
[colors.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/colors.gif?raw=true
|
||||||
|
[colors_rgb.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/colors_rgb.gif?raw=true
|
||||||
[custom_widget.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/custom_widget.gif?raw=true
|
[custom_widget.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/custom_widget.gif?raw=true
|
||||||
[demo.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/demo.gif?raw=true
|
[demo.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/demo.gif?raw=true
|
||||||
[gauge.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/gauge.gif?raw=true
|
[gauge.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/gauge.gif?raw=true
|
||||||
|
160
examples/colors_rgb.rs
Normal file
160
examples/colors_rgb.rs
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/// This example shows the full range of RGB colors that can be displayed in the terminal.
|
||||||
|
///
|
||||||
|
/// Requires a terminal that supports 24-bit color (true color) and unicode.
|
||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
io::{stdout, Stdout},
|
||||||
|
rc::Rc,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crossterm::{
|
||||||
|
event::{self, Event, KeyCode, KeyEventKind},
|
||||||
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
|
ExecutableCommand,
|
||||||
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
|
use ratatui::{prelude::*, widgets::*};
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, Box<dyn Error>>;
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
install_panic_hook();
|
||||||
|
App::new()?.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct App {
|
||||||
|
terminal: Terminal<CrosstermBackend<Stdout>>,
|
||||||
|
should_quit: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
terminal: Terminal::new(CrosstermBackend::new(stdout()))?,
|
||||||
|
should_quit: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(mut self) -> Result<()> {
|
||||||
|
init_terminal()?;
|
||||||
|
self.terminal.clear()?;
|
||||||
|
while !self.should_quit {
|
||||||
|
self.draw()?;
|
||||||
|
self.handle_events()?;
|
||||||
|
}
|
||||||
|
restore_terminal()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&mut self) -> Result<()> {
|
||||||
|
self.terminal.draw(|frame| {
|
||||||
|
frame.render_widget(RgbColors, frame.size());
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_events(&mut self) -> Result<()> {
|
||||||
|
if event::poll(Duration::from_millis(100))? {
|
||||||
|
if let Event::Key(key) = event::read()? {
|
||||||
|
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
|
||||||
|
self.should_quit = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for App {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = restore_terminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RgbColors;
|
||||||
|
|
||||||
|
impl Widget for RgbColors {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
let layout = Self::layout(area);
|
||||||
|
let rgb_colors = Self::create_rgb_color_grid(area.width, area.height * 2);
|
||||||
|
Self::render_title(layout[0], buf);
|
||||||
|
Self::render_colors(layout[1], buf, rgb_colors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RgbColors {
|
||||||
|
fn layout(area: Rect) -> Rc<[Rect]> {
|
||||||
|
Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints(vec![Constraint::Length(1), Constraint::Min(0)])
|
||||||
|
.split(area)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_title(area: Rect, buf: &mut Buffer) {
|
||||||
|
Paragraph::new("colors_rgb example. Press q to quit")
|
||||||
|
.dark_gray()
|
||||||
|
.alignment(Alignment::Center)
|
||||||
|
.render(area, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a colored grid of half block characters (`"▀"`) each with a different RGB color.
|
||||||
|
fn render_colors(area: Rect, buf: &mut Buffer, rgb_colors: Vec<Vec<Color>>) {
|
||||||
|
for (x, column) in (area.left()..area.right()).zip(rgb_colors.iter()) {
|
||||||
|
for (y, (fg, bg)) in (area.top()..area.bottom()).zip(column.iter().tuples()) {
|
||||||
|
let cell = buf.get_mut(x, y);
|
||||||
|
cell.fg = *fg;
|
||||||
|
cell.bg = *bg;
|
||||||
|
cell.symbol = "▀".into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a smooth grid of colors
|
||||||
|
///
|
||||||
|
/// Red ranges from 0 to 255 across the x axis. Green ranges from 0 to 255 across the y axis.
|
||||||
|
/// Blue repeats every 32 pixels in both directions, but flipped every 16 pixels so that it
|
||||||
|
/// doesn't transition sharply from light to dark.
|
||||||
|
///
|
||||||
|
/// The result stored in a 2d vector of colors with the x axis as the first dimension, and the
|
||||||
|
/// y axis the second dimension.
|
||||||
|
fn create_rgb_color_grid(width: u16, height: u16) -> Vec<Vec<Color>> {
|
||||||
|
let mut result = vec![];
|
||||||
|
for x in 0..width {
|
||||||
|
let mut column = vec![];
|
||||||
|
for y in 0..height {
|
||||||
|
// flip both axes every 16 pixels. E.g. [0, 1, ... 15, 15, ... 1, 0]
|
||||||
|
let yy = if (y % 32) < 16 { y % 32 } else { 31 - y % 32 };
|
||||||
|
let xx = if (x % 32) < 16 { x % 32 } else { 31 - x % 32 };
|
||||||
|
let r = (256 * x / width) as u8;
|
||||||
|
let g = (256 * y / height) as u8;
|
||||||
|
let b = (yy * 16 + xx) as u8;
|
||||||
|
column.push(Color::Rgb(r, g, b))
|
||||||
|
}
|
||||||
|
result.push(column);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install a panic hook that restores the terminal before panicking.
|
||||||
|
fn install_panic_hook() {
|
||||||
|
better_panic::install();
|
||||||
|
let prev_hook = std::panic::take_hook();
|
||||||
|
std::panic::set_hook(Box::new(move |info| {
|
||||||
|
let _ = restore_terminal();
|
||||||
|
prev_hook(info);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_terminal() -> Result<()> {
|
||||||
|
enable_raw_mode()?;
|
||||||
|
stdout().execute(EnterAlternateScreen)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore_terminal() -> Result<()> {
|
||||||
|
disable_raw_mode()?;
|
||||||
|
stdout().execute(LeaveAlternateScreen)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
18
examples/colors_rgb.tape
Normal file
18
examples/colors_rgb.tape
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# This is a vhs script. See https://github.com/charmbracelet/vhs for more info.
|
||||||
|
# To run this script, install vhs and run `vhs ./examples/colors_rgb.tape`
|
||||||
|
Output "target/colors_rgb.gif"
|
||||||
|
# The OceanicMaterial theme is a good choice for this example (Obsidian is almost as good) because:
|
||||||
|
# - Black is dark and distinct from the default background
|
||||||
|
# - White is light and distinct from the default foreground
|
||||||
|
# - Normal and bright colors are distinct
|
||||||
|
# - Black and DarkGray are distinct
|
||||||
|
# - White and Gray are distinct
|
||||||
|
Set Theme "OceanicMaterial"
|
||||||
|
Set Width 1200
|
||||||
|
Set Height 1410
|
||||||
|
Hide
|
||||||
|
Type "cargo run --example=colors_rgb --features=crossterm"
|
||||||
|
Enter
|
||||||
|
Sleep 2s
|
||||||
|
Show
|
||||||
|
Sleep 1s
|
Loading…
x
Reference in New Issue
Block a user