#tui #proc-macro #cui

macro appcui_proc_macro

Procedural macros for the AppCUI TUI framework

15 releases

new 0.2.4 Oct 17, 2025
0.2.3 Oct 11, 2025
0.2.2 Sep 27, 2025
0.1.9 Aug 21, 2025
0.1.1 Jun 28, 2025

#1263 in Procedural macros

Download history 212/week @ 2025-06-25 271/week @ 2025-07-02 193/week @ 2025-07-09 264/week @ 2025-07-16 145/week @ 2025-07-23 133/week @ 2025-07-30 48/week @ 2025-08-06 138/week @ 2025-08-13 177/week @ 2025-08-20 7/week @ 2025-08-27 305/week @ 2025-09-03 311/week @ 2025-09-10 190/week @ 2025-09-17 215/week @ 2025-09-24 62/week @ 2025-10-01 225/week @ 2025-10-08

710 downloads per month
Used in 2 crates (via appcui)

MIT license

580KB
13K SLoC

AppCUI-rs

⯈ 𝗔𝗽𝗽𝗖𝗨𝗜-𝗿𝘀 🖳

Windows Build Status Linux Build Status MacOS Build Status Code Coverage License Crates.io Docs.rs Gallery

AppCUI-rs is a fast, cross-platform Rust library for building modern, text-based user interfaces (TUIs) with rich widgets, themes, and full Unicode support—an alternative to ncurses and other terminal UI frameworks.

✨ Features

  • multiple out-of-the-box controls (buttons, labels, text boxes, check boxes, radio buttons, list views, tree views, combo boxes, date/time pickers, color pickers, tabs, accordeons, etc.). A full list of controls can be found here
  • powerful layout system that allows you to position controls using absolute coordinates, relative coordinates, docking, alignment, anchors or pivot positioning (see more here)
  • menus and toolbars
  • multi-platform support (Windows via API and virtual terminal, Linux via ncurses, MacOS via termios)
  • multi-threading support to allow background tasks
  • timers
  • mouse support
  • clipboard support
  • color themes
  • support for Unicode characters
  • predefined dialogs (message box, input box, color picker, save & open dialogs, folder navigator, etc)
  • true colors support (24 bits per pixel) for terminals that supports it.

📸 Screenshots

👉 Check out the Gallery for full demos of all controls!

🖥️ Backends

AppCUI supports various backends based on the operating system it is being used for:

  • Windows Console - based on Win32 low level API, design for clasical windows console
  • Windows VT - based on ANSI sequances, designed for modern windows virtual terminals
  • NCurses - based on NCurses API for linux envinronments
  • Termios - based on ANSI sequances and low level APIs for MAC OSX
  • Web Terminal - designed for Web implementation (based on webgl)
  • CrossTerm - based on the crossterm crate, but enabled via a feature flag

More on the supported backends can be found here

🚀 Quick Start

Add the following to your Cargo.toml:

[dependencies]
appcui = "*"

Then create a new Rust project and add the following code:

use appcui::prelude::*;

fn main() -> Result<(), appcui::system::Error> {
    let mut app = App::new().build()?;
    let mut win = Window::new(
        "Test",
        LayoutBuilder::new().alignment(Alignment::Center).width(30).height(9).build(),
        window::Flags::Sizeable,
    );
    win.add(Label::new(
        "Hello World !",
        LayoutBuilder::new().alignment(Alignment::Center).width(13).height(1).build(),
    ));
    app.add_window(win);
    app.run();
    Ok(())
}

or a more compact version using proc-macros:

use appcui::prelude::*;

fn main() -> Result<(), appcui::system::Error> {
    let mut app = App::new().build()?;
    let mut win = window!("Test,a:c,w:30,h:9");
    win.add(label!("'Hello World !',a:c,w:13,h:1"));
    app.add_window(win);
    app.run();
    Ok(())
}

Then run the project with cargo run. You should see a window with the title Test and the text Hello World ! in the center.

🧪 Examples

AppCUI-rs comes with a set of examples to help you get started. You can find them in the examples folder, including:

🛠️ A more complex example

Am example that creates a window with a button that when pressed increases a counter.

use appcui::prelude::*;

// Create a window that handles button events and has a counter
#[Window(events = ButtonEvents)]
struct CounterWindow {
    counter: i32
}

impl CounterWindow {
    fn new() -> Self {
        let mut w = Self {
            // set up the window title and position
            base: window!("'Counter window',a:c,w:30,h:5"),
            // initial counter is 1
            counter: 1            
        };
        // add a single button with the caption "1" (like the counter)
        w.add(button!("'1',d:b,w:20"));
        w
    }
}
impl ButtonEvents for CounterWindow {
    // When the button is pressed, this function will be called
    // with the handle of the button that was pressed
    // Since we only have one button, we don't need to store its handle 
    // in the struct, as we will receive the handle via the on_pressed method
    fn on_pressed(&mut self, handle: Handle<Button>) -> EventProcessStatus {
        // increase the counter
        self.counter += 1;
        // create a text that containe the new counter
        let text = format!("{}",self.counter);
        // aquire a mutable reference to the button using its handle
        if let Some(button) = self.control_mut(handle) {
            // set the caption of the button to th new text
            button.set_caption(&text);
        }
        // Tell the AppCUI framework that we have processed this event
        // This allows AppCUI to repaint the button
        EventProcessStatus::Processed
    }
}

fn main() -> Result<(), appcui::system::Error> {
    // create a new application
    let mut a = App::new().build()?;
    // add a new window (of type CounterWindow) to the application
    a.add_window(CounterWindow::new());
    // Run AppCUI framework (this wil start the window loop and messaage passing)
    a.run();
    Ok(())
}

🛣️ Roadmap

  • Basic set of widgets and support for Windows, Linux and MacOS
  • WebGL support
  • OpenGL / SDL / Vulkan support
  • TextArea support for code highlighting

🤝 Contributing

Contributions, issues, and feature requests are welcome!
Check out CONTRIBUTING.md to get started.

Join the discussion in GitHub Discussions.

No runtime deps