appcui_proc_macro 0.2.6

Procedural macros for the AppCUI TUI framework.
Documentation

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, accordions, 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-color support (24 bits per pixel) for terminals that support it.

πŸ“Έ Screenshots

πŸ‘‰ Check out the Gallery for full demos of all controls!

πŸ–₯️ Backends

AppCUI supports various backends depending on the operating system:

  • Windows Console - based on the Win32 low-level API, designed for the classical Windows console
  • Windows VT - based on ANSI sequences, designed for modern Windows virtual terminals
  • NCurses - based on the NCurses API for Linux environments
  • Termios - based on ANSI sequences and low-level APIs for macOS
  • Web Terminal - designed for web implementations (based on WebGL)
  • CrossTerm - based on the crossterm crate, 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

An 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 contains the new counter
        let text = format!("{}",self.counter);
        // acquire a mutable reference to the button using its handle
        if let Some(button) = self.control_mut(handle) {
            // set the caption of the button to the 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 will start the window loop and message 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.