Skip to content

Commit

Permalink
fix: more tweaks based on PR comments
Browse files Browse the repository at this point in the history
#500 (review)

- add comments
- remove dead code (in colors and weather)
- make RGB swatch into a widget
- simplify the RGB logic in the swatch / weather gauge
- move layout() helper function to root module
- remove unused keyboard bindings
- remove unused imports in theme
- remove unused lipsum crate
- turn on doc scraping
  • Loading branch information
joshka committed Sep 20, 2023
1 parent 9bfb6d6 commit 8096177
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 119 deletions.
4 changes: 1 addition & 3 deletions Cargo.toml
Expand Up @@ -55,7 +55,6 @@ cargo-husky = { version = "1.5.0", default-features = false, features = [
] }
criterion = { version = "0.5.1", features = ["html_reports"] }
fakeit = "1.1"
lipsum = "0.9.0"
rand = "0.8.5"
palette = "0.7.3"
pretty_assertions = "1.4.0"
Expand Down Expand Up @@ -154,8 +153,7 @@ doc-scrape-examples = false
[[example]]
name = "demo2"
required-features = ["crossterm", "widget-calendar"]
# this runs for all of the terminal backends, so it can't be built using --all-features or scraped
doc-scrape-examples = false
doc-scrape-examples = true

[[example]]
name = "gauge"
Expand Down
2 changes: 0 additions & 2 deletions examples/demo2/app.rs
Expand Up @@ -76,8 +76,6 @@ impl App {
context.tab_index = context.tab_index.saturating_add(1) % TAB_COUNT;
context.row_index = 0;
}
KeyCode::Left | KeyCode::Char('h') => {}
KeyCode::Right | KeyCode::Char('l') => {}
KeyCode::Up | KeyCode::Char('k') => {
context.row_index = context.row_index.saturating_sub(1);
}
Expand Down
78 changes: 26 additions & 52 deletions examples/demo2/colors.rs
@@ -1,60 +1,34 @@
use palette::{
convert::{FromColorUnclamped, IntoColorUnclamped},
Okhsv, Srgb,
};
use palette::{IntoColor, Okhsv, Srgb};
use ratatui::{prelude::*, widgets::*};

#[allow(dead_code)]
fn render_16_color_swatch(area: Rect, buf: &mut Buffer) {
let sym = "██";
Paragraph::new(vec![
Line::from(vec![sym.black(), sym.red(), sym.green(), sym.yellow()]),
Line::from(vec![sym.blue(), sym.magenta(), sym.cyan(), sym.gray()]),
Line::from(vec![
sym.dark_gray(),
sym.light_red(),
sym.light_green(),
sym.light_yellow(),
]),
Line::from(vec![
sym.light_blue(),
sym.light_magenta(),
sym.light_cyan(),
sym.white(),
]),
])
.render(area, buf);
}
/// A widget that renders a color swatch of RGB colors.
///
/// The widget is rendered as a rectangle with the hue changing along the x-axis from 0.0 to 360.0
/// and the value changing along the y-axis (from 1.0 to 0.0). Each pixel is rendered as a block
/// character with the top half slightly lighter than the bottom half.
pub struct RgbSwatch;

#[allow(dead_code)]
fn render_256_color_swatch(area: Rect, buf: &mut Buffer) {
for (xi, x) in (16..52).zip(area.left()..area.right()) {
for (yi, y) in (0..3).zip(area.top()..area.bottom()) {
let fg = Color::Indexed(yi * 72 + xi);
let bg = Color::Indexed(yi * 72 + xi + 36);
buf.get_mut(x, y).set_char('▀').set_fg(fg).set_bg(bg);
impl Widget for RgbSwatch {
fn render(self, area: Rect, buf: &mut Buffer) {
for (yi, y) in (area.top()..area.bottom()).enumerate() {
let value = area.height as f32 - yi as f32;
let value_fg = value / (area.height as f32);
let value_bg = (value - 0.5) / (area.height as f32);
for (xi, x) in (area.left()..area.right()).enumerate() {
let hue = xi as f32 * 360.0 / area.width as f32;
let fg = color_from_oklab(hue, Okhsv::max_saturation(), value_fg);
let bg = color_from_oklab(hue, Okhsv::max_saturation(), value_bg);
buf.get_mut(x, y).set_char('▀').set_fg(fg).set_bg(bg);
}
}
let fg = Color::Indexed(xi.saturating_add(216));
buf.get_mut(x, area.bottom() - 1).set_char('█').set_fg(fg);
}
}

pub fn render_rgb_swatch(area: Rect, buf: &mut Buffer) {
for (xi, x) in (area.left()..area.right()).enumerate() {
for (yi, y) in (area.top()..area.bottom()).enumerate() {
let yi = area.height as usize - yi - 1;
let hue = xi as f32 * 360.0 / area.width as f32;
let value_bg = (yi as f32 - 0.0) / (area.height as f32);
let value_fg = (yi as f32 + 0.5) / (area.height as f32);
let fg = Okhsv::<f32>::new(hue, Okhsv::max_saturation(), value_fg);
let fg: Srgb<f32> = fg.into_color_unclamped();
let fg: Srgb<u8> = fg.into_format();
let fg = Color::Rgb(fg.red, fg.green, fg.blue);
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);
}
}
/// Convert a hue and value into an RGB color via the OkLab color space.
///
/// See https://bottosson.github.io/posts/oklab/ for more details.
pub fn color_from_oklab(hue: f32, saturation: f32, value: f32) -> Color {
let color: Srgb = Okhsv::new(hue, saturation, value).into_color();
let color = color.into_format();
Color::Rgb(color.red, color.green, color.blue)
}
1 change: 1 addition & 0 deletions examples/demo2/main.rs
@@ -1,5 +1,6 @@
use anyhow::Result;
pub use app::*;
pub use colors::*;
pub use root::*;
pub use term::*;
pub use theme::*;
Expand Down
24 changes: 21 additions & 3 deletions examples/demo2/root.rs
@@ -1,7 +1,9 @@
use std::rc::Rc;

use itertools::Itertools;
use ratatui::{prelude::*, widgets::*};

use crate::{layout, tabs::*, AppContext, THEME};
use crate::{tabs::*, AppContext, THEME};

pub struct Root<'a> {
context: &'a AppContext,
Expand Down Expand Up @@ -53,8 +55,6 @@ impl Root<'_> {
let keys = [
("Q/Esc", "Quit"),
("Tab", "Next Tab"),
("←/h", "Left"),
("→/l", "Right"),
("↑/k", "Up"),
("↓/j", "Down"),
];
Expand All @@ -73,3 +73,21 @@ impl Root<'_> {
.render(area, buf);
}
}

/// simple helper method to split an area into multiple sub-areas
pub fn layout(area: Rect, direction: Direction, heights: Vec<u16>) -> Rc<[Rect]> {
let constraints = heights
.iter()
.map(|&h| {
if h > 0 {
Constraint::Length(h)
} else {
Constraint::Min(0)
}
})
.collect_vec();
Layout::default()
.direction(direction)
.constraints(constraints)
.split(area)
}
8 changes: 6 additions & 2 deletions examples/demo2/tabs/about.rs
@@ -1,7 +1,7 @@
use itertools::Itertools;
use ratatui::{prelude::*, widgets::*};

use crate::{colors, layout, THEME};
use crate::{layout, RgbSwatch, THEME};

const RATATUI_LOGO: [&str; 32] = [
" ███ ",
Expand Down Expand Up @@ -50,7 +50,7 @@ impl AboutTab {

impl Widget for AboutTab {
fn render(self, area: Rect, buf: &mut Buffer) {
colors::render_rgb_swatch(area, buf);
RgbSwatch.render(area, buf);
let area = layout(area, Direction::Horizontal, vec![34, 0]);
render_crate_description(area[1], buf);
render_logo(self.selected_row, area[0], buf);
Expand Down Expand Up @@ -91,6 +91,10 @@ fn render_crate_description(area: Rect, buf: &mut Buffer) {
.render(area, buf);
}

/// Use half block characters to render a logo based on the RATATUI_LOGO const.
///
/// The logo is rendered in three colors, one for the rat, one for the terminal, and one for the
/// rat's eye. The eye color alternates between two colors based on the selected row.
pub fn render_logo(selected_row: usize, area: Rect, buf: &mut Buffer) {
let eye_color = if selected_row % 2 == 0 {
THEME.logo.rat_eye
Expand Down
4 changes: 2 additions & 2 deletions examples/demo2/tabs/email.rs
Expand Up @@ -2,7 +2,7 @@ use itertools::Itertools;
use ratatui::{prelude::*, widgets::*};
use unicode_width::UnicodeWidthStr;

use crate::{colors, layout, THEME};
use crate::{layout, RgbSwatch, THEME};

#[derive(Debug, Default)]
pub struct Email {
Expand Down Expand Up @@ -54,7 +54,7 @@ impl EmailTab {

impl Widget for EmailTab {
fn render(self, area: Rect, buf: &mut Buffer) {
colors::render_rgb_swatch(area, buf);
RgbSwatch.render(area, buf);
let area = area.inner(&Margin {
vertical: 1,
horizontal: 2,
Expand Down
4 changes: 2 additions & 2 deletions examples/demo2/tabs/recipe.rs
@@ -1,7 +1,7 @@
use itertools::Itertools;
use ratatui::{prelude::*, widgets::*};

use crate::{colors, layout, THEME};
use crate::{layout, RgbSwatch, THEME};

#[derive(Debug, Default, Clone, Copy)]
struct Ingredient {
Expand Down Expand Up @@ -99,7 +99,7 @@ impl RecipeTab {

impl Widget for RecipeTab {
fn render(self, area: Rect, buf: &mut Buffer) {
colors::render_rgb_swatch(area, buf);
RgbSwatch.render(area, buf);
let area = area.inner(&Margin {
vertical: 1,
horizontal: 2,
Expand Down
10 changes: 6 additions & 4 deletions examples/demo2/tabs/traceroute.rs
Expand Up @@ -4,7 +4,7 @@ use ratatui::{
widgets::{canvas::*, *},
};

use crate::{colors, layout, THEME};
use crate::{layout, RgbSwatch, THEME};

#[derive(Debug)]
pub struct TracerouteTab {
Expand All @@ -21,7 +21,7 @@ impl TracerouteTab {

impl Widget for TracerouteTab {
fn render(self, area: Rect, buf: &mut Buffer) {
colors::render_rgb_swatch(area, buf);
RgbSwatch.render(area, buf);
let area = area.inner(&Margin {
vertical: 1,
horizontal: 2,
Expand Down Expand Up @@ -112,8 +112,10 @@ fn render_map(selected_row: usize, area: Rect, buf: &mut Buffer) {
.style(theme.style),
)
.marker(Marker::Dot)
.x_bounds([113.0, 154.0]) // australia
.y_bounds([-42.0, -11.0]) // australia
// picked to show Australia for the demo as it's the most interesting part of the map
// (and the only part with hops ;))
.x_bounds([113.0, 154.0])
.y_bounds([-42.0, -11.0])
.paint(|context| {
context.draw(&map);
if let Some(path) = path {
Expand Down
37 changes: 10 additions & 27 deletions examples/demo2/tabs/weather.rs
@@ -1,12 +1,12 @@
use itertools::Itertools;
use palette::{convert::IntoColorUnclamped, Okhsv, Srgb};
use palette::Okhsv;
use ratatui::{
prelude::*,
widgets::{calendar::CalendarEventStore, *},
};
use time::OffsetDateTime;

use crate::{colors, layout, THEME};
use crate::{color_from_oklab, layout, RgbSwatch, THEME};

pub struct WeatherTab {
pub selected_row: usize,
Expand All @@ -20,7 +20,7 @@ impl WeatherTab {

impl Widget for WeatherTab {
fn render(self, area: Rect, buf: &mut Buffer) {
colors::render_rgb_swatch(area, buf);
RgbSwatch.render(area, buf);
let area = area.inner(&Margin {
vertical: 1,
horizontal: 2,
Expand Down Expand Up @@ -118,37 +118,20 @@ pub fn render_gauges(progress: usize, area: Rect, buf: &mut Buffer) {
render_line_gauge(percent, area, buf);
}

// fn render_gauge(percent: f64, label: &str, area: Rect, buf: &mut Buffer) {
// // let bg = Color::Rgb(32, 96, 48);
// // let fg = Color::Rgb(64, 192, 96);
// let bg = Color::Red;
// let fg = Color::Yellow;
// Gauge::default()
// .ratio(percent / 100.0)
// .label(format!("Processing: {}", label))
// .gauge_style(Style::new().fg(fg).bg(bg))
// .use_unicode(false)
// .render(area, buf);
// }

fn render_line_gauge(percent: f64, area: Rect, buf: &mut Buffer) {
let hue = 90.0 - (percent * 0.6);
let fg = Okhsv::<f64>::new(hue, Okhsv::max_saturation(), 1.0);
let bg = Okhsv::<f64>::new(hue, Okhsv::max_saturation(), 0.5);
let fg: Srgb<f64> = fg.into_color_unclamped();
let fg: Srgb<u8> = fg.into_format();
let fg = Color::Rgb(fg.red, fg.green, fg.blue);
let bg: Srgb<f64> = bg.into_color_unclamped();
let bg: Srgb<u8> = bg.into_format();
let bg = Color::Rgb(bg.red, bg.green, bg.blue);
let progress_label = if percent < 100.0 {
// cycle color hue based on the percent for a neat effect yellow -> red
let hue = 90.0 - (percent as f32 * 0.6);
let value = Okhsv::max_value();
let fg = color_from_oklab(hue, Okhsv::max_saturation(), value);
let bg = color_from_oklab(hue, Okhsv::max_saturation(), value * 0.5);
let label = if percent < 100.0 {
format!("Downloading: {}%", percent)
} else {
"Download Complete!".into()
};
LineGauge::default()
.ratio(percent / 100.0)
.label(progress_label)
.label(label)
.style(Style::new().light_blue())
.gauge_style(Style::new().fg(fg).bg(bg))
.line_set(symbols::line::THICK)
Expand Down
20 changes: 0 additions & 20 deletions examples/demo2/term.rs
@@ -1,7 +1,6 @@
use std::{
io::{self, stdout, Stdout},
ops::{Deref, DerefMut},
rc::Rc,
time::Duration,
};

Expand All @@ -11,7 +10,6 @@ use crossterm::{
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
ExecutableCommand,
};
use itertools::Itertools;
use ratatui::prelude::*;

/// A wrapper around the terminal that handles setting up and tearing down the terminal
Expand Down Expand Up @@ -71,21 +69,3 @@ impl Drop for Term {
let _ = Term::stop();
}
}

/// simple helper method to split an area into multiple sub-areas
pub fn layout(area: Rect, direction: Direction, heights: Vec<u16>) -> Rc<[Rect]> {
let constraints = heights
.iter()
.map(|&h| {
if h > 0 {
Constraint::Length(h)
} else {
Constraint::Min(0)
}
})
.collect_vec();
Layout::default()
.direction(direction)
.constraints(constraints)
.split(area)
}
2 changes: 0 additions & 2 deletions examples/demo2/theme.rs
@@ -1,5 +1,3 @@
#![allow(unused)]
use palette::{named::LIGHTGRAY, white_point::B};
use ratatui::prelude::*;

pub struct Theme {
Expand Down

0 comments on commit 8096177

Please sign in to comment.