Skip to content

Commit

Permalink
Create demo and refactor basic.rs (#3)
Browse files Browse the repository at this point in the history
* docs: add vhs tape

* docs(example): make the example more robust

- Add color_eyre for handling errors
- Move init/restore terminal to functions
- Move run function to App impl
- impl Widget for App to render the app instead of ui function
- Use updated Ratatui API functions for layout and rendering

* docs(example): use enum instead of strings for actions

* docs(readme): use enum in README and add demo gif
  • Loading branch information
joshka authored Apr 5, 2024
1 parent b66dfa1 commit f493732
Show file tree
Hide file tree
Showing 4 changed files with 254 additions and 106 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ repository = "https://github.com/shuoli84/tui-menu"
ratatui = "0.26.1"

[dev-dependencies]
color-eyre = "0.6.3"
crossterm = "0.27.0"
65 changes: 46 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

A menu widget for [Ratatui](https://crates.io/crates/ratatui).

![Demo](https://vhs.charm.sh/vhs-3pN84WzBTmPTbU2SCtvUiV.gif)

## Features

- Sub menu groups.
Expand All @@ -23,60 +25,85 @@ take a look at examples/basic.rs
```rust
// menu should be draw at last, so it can stay on top of other content
let menu = Menu::new();
f.render_stateful_widget(menu, chunks[0], &mut app.menu);
frame.render_stateful_widget(menu, chunks[0], &mut app.menu);
```

### Create nested menu tree

Note: MenuItems can be created from any type that implements `Clone`. Using an enum is just one
option which can work. You could use strings or your own state types.

```rust
#[derive(Debug, Clone)]
enum Action {
FileNew,
FileOpen,
FileOpenRecent(String),
FileSaveAs,
Exit,
EditCopy,
EditCut,
EditPaste,
AboutAuthor,
AboutHelp,
}

let menu = MenuState::new(vec![
MenuItem::group(
"File",
vec![
MenuItem::item("New", "file.new".into()),
MenuItem::item("Open", "file.open".into()),
MenuItem::item("New", Action::FileNew),
MenuItem::item("Open", Action::FileOpen),
MenuItem::group(
"Open recent",
vec!["file_1.txt", "file_2.txt"]
.into_iter()
.map(|f| MenuItem::item(f, format!("file.recent:{f}").into()))
["file_1.txt", "file_2.txt"]
.iter()
.map(|&f| MenuItem::item(f, Action::FileOpenRecent(f.into())))
.collect(),
),
MenuItem::item("Save as", "file.save_as".into()),
MenuItem::item("Exit", "exit".into()),
MenuItem::item("Save as", Action::FileSaveAs),
MenuItem::item("Exit", Action::Exit),
],
),
MenuItem::group(
"Edit",
vec![
MenuItem::item("Copy", "edit.new".into()),
MenuItem::item("Cut", "edit.cut".into()),
MenuItem::item("Paste", "edit.paste".into()),
MenuItem::item("Copy", Action::EditCopy),
MenuItem::item("Cut", Action::EditCut),
MenuItem::item("Paste", Action::EditPaste),
],
),
MenuItem::group(
"About",
vec![
MenuItem::item("Author", "about.author".into()),
MenuItem::item("Help", "about.help".into()),
MenuItem::item("Author", Action::AboutAuthor),
MenuItem::item("Help", Action::AboutHelp),
],
),
]);
]),
```

### Consume events

``` rust
for e in app.menu.drain_events() {
for e in menu.drain_events() {
match e {
tui_menu::MenuEvent::Selected(item) => match item.as_ref() {
"exit" => {
MenuEvent::Selected(item) => match item {
Action::Exit => {
return Ok(());
}
_ => {
// println!("{} selected", item);
Action::FileNew => {
self.content.clear();
}
Action::FileOpenRecent(file) => {
self.content = format!("content of {file}");
}
action => {
self.content = format!("{action:?} not implemented");
}
},
}
// close the menu once the event has been handled.
menu.reset();
}
```
88 changes: 88 additions & 0 deletions demo.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# VHS documentation
#
# Output:
# Output <path>.gif Create a GIF output at the given <path>
# Output <path>.mp4 Create an MP4 output at the given <path>
# Output <path>.webm Create a WebM output at the given <path>
#
# Require:
# Require <string> Ensure a program is on the $PATH to proceed
#
# Settings:
# Set FontSize <number> Set the font size of the terminal
# Set FontFamily <string> Set the font family of the terminal
# Set Height <number> Set the height of the terminal
# Set Width <number> Set the width of the terminal
# Set LetterSpacing <float> Set the font letter spacing (tracking)
# Set LineHeight <float> Set the font line height
# Set LoopOffset <float>% Set the starting frame offset for the GIF loop
# Set Theme <json|string> Set the theme of the terminal
# Set Padding <number> Set the padding of the terminal
# Set Framerate <number> Set the framerate of the recording
# Set PlaybackSpeed <float> Set the playback speed of the recording
# Set MarginFill <file|#000000> Set the file or color the margin will be filled with.
# Set Margin <number> Set the size of the margin. Has no effect if MarginFill isn't set.
# Set BorderRadius <number> Set terminal border radius, in pixels.
# Set WindowBar <string> Set window bar type. (one of: Rings, RingsRight, Colorful, ColorfulRight)
# Set WindowBarSize <number> Set window bar size, in pixels. Default is 40.
# Set TypingSpeed <time> Set the typing speed of the terminal. Default is 50ms.
#
# Sleep:
# Sleep <time> Sleep for a set amount of <time> in seconds
#
# Type:
# Type[@<time>] "<characters>" Type <characters> into the terminal with a
# <time> delay between each character
#
# Keys:
# Escape[@<time>] [number] Press the Escape key
# Backspace[@<time>] [number] Press the Backspace key
# Delete[@<time>] [number] Press the Delete key
# Insert[@<time>] [number] Press the Insert key
# Down[@<time>] [number] Press the Down key
# Enter[@<time>] [number] Press the Enter key
# Space[@<time>] [number] Press the Space key
# Tab[@<time>] [number] Press the Tab key
# Left[@<time>] [number] Press the Left Arrow key
# Right[@<time>] [number] Press the Right Arrow key
# Up[@<time>] [number] Press the Up Arrow key
# Down[@<time>] [number] Press the Down Arrow key
# PageUp[@<time>] [number] Press the Page Up key
# PageDown[@<time>] [number] Press the Page Down key
# Ctrl+<key> Press the Control key + <key> (e.g. Ctrl+C)
#
# Display:
# Hide Hide the subsequent commands from the output
# Show Show the subsequent commands in the output

Output target/demo.gif

Set Theme "Aardvark Blue"
Set Width 1200
Set Height 450

Hide
Type "cargo run --example basic" Enter
Sleep 2s # allow some time for the build to complete
Show
Set TypingSpeed 500ms

# File | Open recent | file_1.txt
Right Down 3 Right
# screenshot for ratatui.rs showcase
Sleep 100ms Screenshot target/demo.png
Enter Sleep 1s

# File | Open recent | file_2.txt
Right Down 3 Right Down Enter Sleep 1s

# Show off other menus
Right 3 Left 2

# File | Exit
Down 5 Enter

# Show that the app exited before repeating the animation
Sleep 1s
Hide

Loading

0 comments on commit f493732

Please sign in to comment.