Skip to content

Commit

Permalink
feat(examples): add demo2 example (#500)
Browse files Browse the repository at this point in the history
  • Loading branch information
joshka authored Sep 21, 2023
1 parent 21303f2 commit be55a5f
Show file tree
Hide file tree
Showing 21 changed files with 1,431 additions and 55 deletions.
3 changes: 3 additions & 0 deletions .markdownlint.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# configuration for https://github.com/DavidAnson/markdownlint

first-line-heading: false
no-inline-html:
allowed_elements:
- img
- details
- summary
- div
- br
line-length:
line_length: 100

Expand Down
12 changes: 9 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,15 @@ lru = "0.11.1"

[dev-dependencies]
anyhow = "1.0.71"
argh = "0.1"
argh = "0.1.12"
better-panic = "0.3.0"
cargo-husky = { version = "1.5.0", default-features = false, features = [
"user-hooks",
] }
criterion = { version = "0.5", features = ["html_reports"] }
criterion = { version = "0.5.1", features = ["html_reports"] }
fakeit = "1.1"
rand = "0.8"
rand = "0.8.5"
palette = "0.7.3"
pretty_assertions = "1.4.0"

[features]
Expand Down Expand Up @@ -149,6 +150,11 @@ name = "demo"
# this runs for all of the terminal backends, so it can't be built using --all-features or scraped
doc-scrape-examples = false

[[example]]
name = "demo2"
required-features = ["crossterm", "widget-calendar"]
doc-scrape-examples = true

[[example]]
name = "gauge"
required-features = ["crossterm"]
Expand Down
33 changes: 23 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
# Ratatui
![Demo of
Ratatui](https://raw.githubusercontent.com/ratatui-org/ratatui/aa09e59dc0058347f68d7c1e0c91f863c6f2b8c9/examples/demo2.gif)
<!--
Permalink to https://github.com/ratatui-org/ratatui/blob/images/examples/demo2.gif
See RELEASE.md for instructions on creating the demo gif
--->

<img align="left" src="https://avatars.githubusercontent.com/u/125200832?s=128&v=4">

`ratatui` is a [Rust](https://www.rust-lang.org) library that is all about cooking up terminal user interfaces.
It is a community fork of the original [tui-rs](https://github.com/fdehau/tui-rs)
project.
<div align="center">

[![Crates.io](https://img.shields.io/crates/v/ratatui?logo=rust&style=flat-square)](https://crates.io/crates/ratatui)
[![License](https://img.shields.io/crates/l/ratatui?style=flat-square)](./LICENSE) [![GitHub CI
Status](https://img.shields.io/github/actions/workflow/status/ratatui-org/ratatui/ci.yml?style=flat-square&logo=github)](https://github.com/ratatui-org/ratatui/actions?query=workflow%3ACI+)
[![Docs.rs](https://img.shields.io/docsrs/ratatui?logo=rust&style=flat-square)](https://docs.rs/crate/ratatui/)
[![Docs.rs](https://img.shields.io/docsrs/ratatui?logo=rust&style=flat-square)](https://docs.rs/crate/ratatui/)<br>
[![Dependency
Status](https://deps.rs/repo/github/ratatui-org/ratatui/status.svg?style=flat-square)](https://deps.rs/repo/github/ratatui-org/ratatui)
[![Codecov](https://img.shields.io/codecov/c/github/ratatui-org/ratatui?logo=codecov&style=flat-square&token=BAQ8SOKEST)](https://app.codecov.io/gh/ratatui-org/ratatui)
[![Discord](https://img.shields.io/discord/1070692720437383208?label=discord&logo=discord&style=flat-square)](https://discord.gg/pMCEU9hNEj)
[![Matrix](https://img.shields.io/matrix/ratatui-general%3Amatrix.org?style=flat-square&logo=matrix&label=Matrix)](https://matrix.to/#/#ratatui:matrix.org)
[![Matrix](https://img.shields.io/matrix/ratatui-general%3Amatrix.org?style=flat-square&logo=matrix&label=Matrix)](https://matrix.to/#/#ratatui:matrix.org)<br>
[Documentation](https://docs.rs/ratatui)
· [Examples](https://github.com/ratatui-org/ratatui/tree/main/examples)
· [Report a bug](https://github.com/ratatui-org/ratatui/issues/new?labels=bug&projects=&template=bug_report.md)
· [Request a Feature](https://github.com/ratatui-org/ratatui/issues/new?labels=enhancement&projects=&template=feature_request.md)
· [Send a Pull Request](https://github.com/ratatui-org/ratatui/compare)

</div>

<!-- See RELEASE.md for instructions on creating the demo gif --->
![Demo of Ratatui](https://vhs.charm.sh/vhs-tF0QbuPbtHgUeG0sTVgFr.gif)
<img align="left" src="https://avatars.githubusercontent.com/u/125200832?s=128&v=4">

# Ratatui

`ratatui` is a [Rust](https://www.rust-lang.org) library that is all about cooking up terminal user
interfaces. It is a community fork of the original [tui-rs](https://github.com/fdehau/tui-rs)
project.

<details>
<summary>Table of Contents</summary>
Expand Down
10 changes: 6 additions & 4 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ actions](.github/workflows/cd.yml) and triggered by pushing a tag.
[vhs](https://github.com/charmbracelet/vhs) (installation instructions in README).

```shell
cargo build --example demo
vhs examples/demo.tape --publish --quiet
cargo build --example demo2
vhs examples/demo2.tape
```

Then update the link in the [examples README](./examples/README) and the main README. Avoid
adding the gif to the git repo as binary files tend to bloat repositories.
1. Switch branches to the images branch and copy demo2.gif to examples/, commit, and push.
1. Grab the permalink from <https://github.com/ratatui-org/ratatui/blob/images/examples/demo2.gif> and
append `?raw=true` to redirect to the actual image url. Then update the link in the main README.
Avoid adding the gif to the git repo as binary files tend to bloat repositories.

1. Bump the version in [Cargo.toml](Cargo.toml).
1. Bump versions in the doc comments of [lib.rs](src/lib.rs).
Expand Down
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ occur in a terminal.

## Demo

This is the demo example from the main README. It is available for each of the backends. Source:
This is the previous demo example from the main README. It is available for each of the backends. Source:
[demo.rs](./demo/).

```shell
Expand Down
59 changes: 23 additions & 36 deletions examples/colors_rgb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ use crossterm::{
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
ExecutableCommand,
};
use itertools::Itertools;
use palette::{
convert::{FromColorUnclamped, IntoColorUnclamped},
Okhsv, Srgb,
};
use ratatui::{prelude::*, widgets::*};

type Result<T> = std::result::Result<T, Box<dyn Error>>;
Expand Down Expand Up @@ -77,9 +80,8 @@ 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);
Self::render_colors(layout[1], buf);
}
}

Expand All @@ -99,41 +101,26 @@ impl RgbColors {
}

/// 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))
fn render_colors(area: Rect, buf: &mut Buffer) {
for (xi, x) in (area.left()..area.right()).enumerate() {
for (yi, y) in (area.top()..area.bottom()).enumerate() {
let hue = xi as f32 * 360.0 / area.width as f32;

let value_fg = (yi as f32) / (area.height as f32 - 0.5);
let fg = Okhsv::<f32>::new(hue, Okhsv::max_saturation(), value_fg);
let fg: Srgb = fg.into_color_unclamped();
let fg: Srgb<u8> = fg.into_format();
let fg = Color::Rgb(fg.red, fg.green, fg.blue);

let value_bg = (yi as f32 + 0.5) / (area.height as f32 - 0.5);
let bg = Okhsv::new(hue, Okhsv::max_saturation(), value_bg);
let bg = Srgb::<f32>::from_color_unclamped(bg);
let bg: Srgb<u8> = bg.into_format();
let bg = Color::Rgb(bg.red, bg.green, bg.blue);

buf.get_mut(x, y).set_char('▀').set_fg(fg).set_bg(bg);
}
result.push(column);
}
result
}
}

Expand Down
43 changes: 43 additions & 0 deletions examples/demo2-social.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# This is a vhs script. See https://github.com/charmbracelet/vhs for more info.
# To run this script, install vhs and run `vhs ./examples/demo.tape`

Output "target/demo2-social.gif"
Set Theme "OceanicMaterial"
# Github social preview size (1280x640 with 80px padding) and must be < 1MB
# This puts some constraints on the amount of interactivity we can do.
Set Width 1280
Set Height 640
Set Padding 80
Hide
Type "cargo run --example demo2 --features crossterm,widget-calendar"
Enter
Sleep 2s
Show
# About screen
Sleep 3.5s
Down # Red eye
Sleep 0.4s
Down # black eye
Sleep 1s
Tab
# Recipe
Sleep 1s
Set TypingSpeed 500ms
Down 7
Sleep 1s
Tab
# Email
Sleep 2s
Down 4
Sleep 2s
Tab
# Trace route
Sleep 1s
Set TypingSpeed 200ms
Down 10
Sleep 2s
Tab
# Weather
Set TypingSpeed 100ms
Down 40
Sleep 2s
49 changes: 49 additions & 0 deletions examples/demo2.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# This is a vhs script. See https://github.com/charmbracelet/vhs for more info.
# To run this script, install vhs and run `vhs ./examples/demo.tape`
# NOTE: Requires VHS 0.6.1 or later for Screenshot support
Output "target/demo2.gif"
Set Theme "OceanicMaterial"
# The reason for this strange size is that the social preview image for this
# demo is 1280x64 with 80 pixels of padding on each side. We want a version
# without the padding for README.md, etc.
Set Width 1120
Set Height 480
Set Padding 0
Hide
Type "cargo run --example demo2 --features crossterm,widget-calendar"
Enter
Sleep 2s
Show
# About screen
Screenshot "target/demo2-about.png"
Sleep 3.5s
Down # Red eye
Sleep 0.4s
Down # black eye
Sleep 1s
Tab
# Recipe
Screenshot "target/demo2-recipe.png"
Sleep 1s
Set TypingSpeed 500ms
Down 7
Sleep 1s
Tab
# Email
Screenshot "target/demo2-email.png"
Sleep 2s
Down 4
Sleep 2s
Tab
# Trace route
Screenshot "target/demo2-trace.png"
Sleep 1s
Set TypingSpeed 200ms
Down 10
Sleep 2s
Tab
# Weather
Screenshot "target/demo2-weather.png"
Set TypingSpeed 100ms
Down 40
Sleep 2s
99 changes: 99 additions & 0 deletions examples/demo2/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use std::time::Duration;

use anyhow::{Context, Result};
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use ratatui::prelude::Rect;

use crate::{Root, Term};

#[derive(Debug)]
pub struct App {
term: Term,
should_quit: bool,
context: AppContext,
}

#[derive(Debug, Default, Clone, Copy)]
pub struct AppContext {
pub tab_index: usize,
pub row_index: usize,
}

impl App {
fn new() -> Result<Self> {
Ok(Self {
term: Term::start()?,
should_quit: false,
context: AppContext::default(),
})
}

pub fn run() -> Result<()> {
install_panic_hook();
let mut app = Self::new()?;
while !app.should_quit {
app.draw()?;
app.handle_events()?;
}
Term::stop()?;
Ok(())
}

fn draw(&mut self) -> Result<()> {
self.term
.draw(|frame| frame.render_widget(Root::new(&self.context), frame.size()))
.context("terminal.draw")?;
Ok(())
}

fn handle_events(&mut self) -> Result<()> {
match Term::next_event(Duration::from_millis(16))? {
Some(Event::Key(key)) => self.handle_key_event(key),
Some(Event::Resize(width, height)) => {
Ok(self.term.resize(Rect::new(0, 0, width, height))?)
}
_ => Ok(()),
}
}

fn handle_key_event(&mut self, key: KeyEvent) -> Result<()> {
if key.kind != KeyEventKind::Press {
return Ok(());
}

let context = &mut self.context;
const TAB_COUNT: usize = 5;
match key.code {
KeyCode::Char('q') | KeyCode::Esc => {
self.should_quit = true;
}
KeyCode::Tab | KeyCode::BackTab if key.modifiers.contains(KeyModifiers::SHIFT) => {
let tab_index = context.tab_index + TAB_COUNT; // to wrap around properly
context.tab_index = tab_index.saturating_sub(1) % TAB_COUNT;
context.row_index = 0;
}
KeyCode::Tab | KeyCode::BackTab => {
context.tab_index = context.tab_index.saturating_add(1) % TAB_COUNT;
context.row_index = 0;
}
KeyCode::Up | KeyCode::Char('k') => {
context.row_index = context.row_index.saturating_sub(1);
}
KeyCode::Down | KeyCode::Char('j') => {
context.row_index = context.row_index.saturating_add(1);
}
_ => {}
};
Ok(())
}
}

pub fn install_panic_hook() {
better_panic::install();
let hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let _ = Term::stop();
hook(info);
std::process::exit(1);
}));
}
Loading

0 comments on commit be55a5f

Please sign in to comment.