Skip to content

Commit

Permalink
fix: review comments, refactor recipe, scrollbar
Browse files Browse the repository at this point in the history
  • Loading branch information
joshka committed Sep 17, 2023
1 parent 8512c89 commit 47ad830
Show file tree
Hide file tree
Showing 19 changed files with 819 additions and 585 deletions.
3 changes: 2 additions & 1 deletion README.md
@@ -1,4 +1,5 @@
![Demo of Ratatui](https://repository-images.githubusercontent.com/600886023/96016d23-3bdd-4611-8c03-2e8b5836f900)
![Demo of
Ratatui](https://github.com/ratatui-org/ratatui/blob/ed851fb5bb3673b3213d1511e77be82fa6719ea0/examples/demo2-noborders.gif?raw=true)
<!-- See RELEASE.md for instructions on creating the demo gif --->

<div align="center">
Expand Down
2 changes: 1 addition & 1 deletion examples/README.md
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
58 changes: 51 additions & 7 deletions examples/demo2.tape
Expand Up @@ -2,27 +2,71 @@
# To run this script, install vhs and run `vhs ./examples/demo.tape`
Output "target/demo2.gif"
Set Theme "OceanicMaterial"
# Github social preview size (1280x640 with 80px padding)
# 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
# Without the padding for README.md, etc.
# Set Width 1120
# Set Height 480
# Set Padding 0
Hide
Type "cargo run --example demo2"
Enter
Sleep 2s
Show
# About screen
Sleep 5s
Sleep 3.5s
Down # Red eye
Sleep 0.5s
Down # black eye
Sleep 1s
Tab
# Email
Sleep 5s
Down
Sleep 1s
Down
Sleep 1s
Down
Sleep 1s
Down
Sleep 2s
Tab
# Trace route
Sleep 5s
Tab
# Misc
Sleep 5s
Sleep 1s
Down
Sleep 0.5s
Down
Sleep 0.5s
Down
Sleep 0.5s
Down
Sleep 0.5s
Down
Sleep 0.5s
Down
Sleep 0.5s
Down
Sleep 1s
Tab
# Recipe
Sleep 1s
Down
Sleep 0.5s
Down
Sleep 0.5s
Down
Sleep 0.5s
Down
Sleep 0.5s
Down
Sleep 0.5s
Down
Sleep 0.5s
Down
Sleep 1s
Tab
# Misc
Sleep 5s
Tab
100 changes: 35 additions & 65 deletions examples/demo2/app.rs
@@ -1,79 +1,52 @@
use std::{io::Stdout, time::Duration};
use std::time::Duration;

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

use crate::{
app_widget::AppWidget,
tabs,
tabs::{AboutTab, EmailTab, MiscWidgetsTab, RecipeTab, Tab, TracerouteTab},
tui,
};
use crate::{Root, Term};

#[derive(Debug)]
pub struct App {
terminal: Terminal<CrosstermBackend<Stdout>>,
term: Term,
should_quit: bool,
tab_index: usize,
selected_index: usize,
tabs: Vec<Box<dyn Tab>>,
context: AppContext,
}

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

impl App {
pub fn new() -> Result<Self> {
let terminal = tui::create_terminal()?;
fn new() -> Result<Self> {
Ok(Self {
terminal,
term: Term::start()?,
should_quit: false,
tab_index: 0,
selected_index: 0,
tabs: vec![
Box::new(tabs::AboutTab::new()),
Box::new(tabs::EmailTab::new(0)),
Box::new(tabs::TracerouteTab::new(0)),
Box::new(tabs::MiscWidgetsTab::new()),
Box::new(tabs::RecipeTab::new(0)),
],
context: AppContext::default(),
})
}

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

fn draw(&mut self) -> Result<()> {
self.terminal
.draw(|frame| {
let titles = self.tabs.iter().map(|tab| tab.title()).collect_vec();
let tab: Box<dyn Tab> = {
// This is a bit of a hack to get around the borrow checker.
// which works because we know that the tabs are all static.
match self.tab_index {
0 => Box::new(AboutTab::new()),
1 => Box::new(EmailTab::new(self.selected_index)),
2 => Box::new(TracerouteTab::new(self.selected_index)),
3 => Box::new(MiscWidgetsTab::new()),
4 => Box::new(RecipeTab::new(self.selected_index)),
_ => unreachable!(),
}
};
let view = AppWidget::new(tab, self.tab_index, titles);
let area = frame.size();
frame.render_widget(view, area)
})
self.term
.draw(|frame| frame.render_widget(Root::new(&self.context), frame.size()))
.context("terminal.draw")?;
Ok(())
}

fn handle_events(&mut self) -> Result<()> {
match tui::next_event(Duration::from_millis(16))? {
match Term::next_event(Duration::from_millis(16))? {
Some(Event::Key(key)) => self.handle_key_event(key),
_ => Ok(()),
}
Expand All @@ -83,44 +56,41 @@ impl App {
if key.kind != KeyEventKind::Press {
return Ok(());
}

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

impl Drop for App {
fn drop(&mut self) {
let _ = tui::restore();
}
}

pub fn install_panic_hook() {
better_panic::install();
let hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let _ = tui::restore();
let _ = Term::stop();
hook(info);
std::process::exit(1);
}));
Expand Down
69 changes: 0 additions & 69 deletions examples/demo2/app_widget.rs

This file was deleted.

9 changes: 5 additions & 4 deletions examples/demo2/colors.rs
@@ -1,11 +1,11 @@
#![allow(dead_code)]
use palette::{
convert::{FromColorUnclamped, IntoColorUnclamped},
Okhsv, Srgb,
};
use ratatui::{prelude::*, widgets::*};

fn render_16_colors(area: Rect, buf: &mut Buffer) {
#[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()]),
Expand All @@ -26,7 +26,8 @@ fn render_16_colors(area: Rect, buf: &mut Buffer) {
.render(area, buf);
}

fn render_256_colors(area: Rect, buf: &mut Buffer) {
#[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);
Expand All @@ -38,7 +39,7 @@ fn render_256_colors(area: Rect, buf: &mut Buffer) {
}
}

pub fn render_rgb_colors(area: Rect, buf: &mut Buffer) {
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;
Expand Down
13 changes: 8 additions & 5 deletions examples/demo2/main.rs
@@ -1,13 +1,16 @@
use anyhow::Result;
pub use app::*;
pub use root::*;
pub use term::*;
pub use theme::*;

mod app;
mod app_widget;
mod colors;
mod styles;
mod root;
mod tabs;
mod tui;
mod term;
mod theme;

fn main() -> Result<()> {
app::install_panic_hook();
app::App::new()?.run()
App::run()
}

0 comments on commit 47ad830

Please sign in to comment.