8 releases

new 0.3.0 Dec 9, 2024
0.2.4 Aug 28, 2024
0.2.2 Jun 25, 2024
0.2.1 Apr 5, 2024
0.1.1 Nov 29, 2022

#390 in Command-line interface

Download history 153/week @ 2024-08-26 1/week @ 2024-09-09 35/week @ 2024-09-16 26/week @ 2024-09-23 51/week @ 2024-09-30 18/week @ 2024-10-07 36/week @ 2024-10-14 218/week @ 2024-10-21 47/week @ 2024-10-28 18/week @ 2024-11-04 39/week @ 2024-11-11 15/week @ 2024-11-18 31/week @ 2024-12-02

87 downloads per month
Used in plastic_tui

MIT/Apache

29KB
500 lines

tui-menu

A menu widget for Ratatui.

Demo

Features

  • Sub menu groups.
  • Intuitive movement.
  • Item's data is generic as long as it Cloneable.

Try

cargo run --example basic

Example

take a look at examples/basic.rs

Render

// menu should be draw at last, so it can stay on top of other content
let menu = Menu::new();
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.

#[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", Action::FileNew),
            MenuItem::item("Open", Action::FileOpen),
            MenuItem::group(
                "Open recent",
                ["file_1.txt", "file_2.txt"]
                    .iter()
                    .map(|&f| MenuItem::item(f, Action::FileOpenRecent(f.into())))
                    .collect(),
            ),
            MenuItem::item("Save as", Action::FileSaveAs),
            MenuItem::item("Exit", Action::Exit),
        ],
    ),
    MenuItem::group(
        "Edit",
        vec![
            MenuItem::item("Copy", Action::EditCopy),
            MenuItem::item("Cut", Action::EditCut),
            MenuItem::item("Paste", Action::EditPaste),
        ],
    ),
    MenuItem::group(
        "About",
        vec![
            MenuItem::item("Author", Action::AboutAuthor),
            MenuItem::item("Help", Action::AboutHelp),
        ],
    ),
]),

Consume events

for e in menu.drain_events() {
    match e {
        MenuEvent::Selected(item) => match item {
            Action::Exit => {
                return Ok(());
            }
            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();
}

Dependencies

~6–15MB
~208K SLoC