feather-tui 0.1.7

A crate for building simple terminal-based user interfaces.
Documentation

πŸ¦€ Feather-Tui

Language License Status

A Rust rewrite of the terminal UI library I originally wrote in C for my school management project.

⚠️ This is my first-ever Rust project ⚠️

Feather-Tui is a simple terminal UI library designed to provide building blocks for text-based user interfaces. It started life as a small C library in my school management system project, aiming to offer an easy-to-use UI framework for terminal applications. Now, I’m rewriting it in Rust to learn the language and (hopefully) improve both performance and maintainability.


πŸ“Š Progress

  • 🚧 Feather-Tui is still under development.
  • βœ… Some parts are complete, while others are only partially done.
  • ✨ These parts may get refined or improved in the future.
  • πŸ‘ For now, they’re good enough to work with.

πŸ“¦ Crates

https://crates.io/crates/feather-tui


πŸš€ Usage

I am really not unexpected people to actually use this crate. So here is a quick example I made in 5 minutes. (Comments are generated by Claude AI)

// Import the feather_tui library with a shorter alias for convenience
use feather_tui as tui;

// Define a trigger function for detecting the 'w' key press (move up)
// This macro creates a function that checks if the input character is 'w'
tui::tui_trg_new_trigger_func!(up_trig_func, key_char, {
    match key_char.downcast_ref::<std::option::Option<char>>().unwrap() {
        Some(c) => *c == 'w',
        None => false,
    }
});

// Define a trigger function for detecting the 's' key press (move down)
tui::tui_trg_new_trigger_func!(down_trig_func, key_char, {
    match key_char.downcast_ref::<std::option::Option<char>>().unwrap() {
        Some(c) => *c == 's',
        None => false,
    }
});

// Define a trigger function for detecting the 'e' key press (selection)
tui::tui_trg_new_trigger_func!(selc_trig_func, key_char, {
    match key_char.downcast_ref::<std::option::Option<char>>().unwrap() {
        Some(c) => *c == 'e',
        None => false,
    }
});

// Define a trigger function for detecting the 'q' key press (quit application)
tui::tui_trg_new_trigger_func!(quit_trig_func, key_char, {
    match key_char.downcast_ref::<std::option::Option<char>>().unwrap() {
        Some(c) => *c == 'q',
        None => false,
    }
});

// Define a callback function that will be executed when menu options are selected
// This function expects a u32 argument and prints it to the console
tui::tui_cbk_new_callback_func!(callback_func, argument, {
    println!("Callback Argument: {}", argument.downcast_ref::<u32>().expect("Expect callback argument to be a u32"));
});

// Helper function to read a key from input
// Marked as inline for performance optimization
#[inline]
fn read_key() -> std::option::Option<char> {
    tui::inp::key_char().expect(tui::inp::READ_KEY_FAIL_ERRMSG)
}

fn main() {
    // Initialize key_char with the first key press
    let mut key_char: std::option::Option<char> = read_key();
    
    // Create a UI container with various components:
    let mut container = tui::con::Container::new()
        // Add a header component with the title "Main Menu"
        .with_header(tui::cpn::hed::Header::new("Main Menu"))
        // Add a first selectable option that will call callback_func with argument 1u32
        .with_option(
            tui::cpn::opt::Option::new(
                "Option1",
                tui::cbk::Callback::new(callback_func, 1u32)))
        // Add a second selectable option that will call callback_func with argument 2u32
        .with_option(
            tui::cpn::opt::Option::new(
                "Option2",
                tui::cbk::Callback::new(callback_func, 2u32)))
        // Add a text component with yellow background and right alignment
        .with_text(
            tui::cpn::txt::Text::new(
                "Text", 
                tui::cpn::txt::TextFlags::COLOR_YELLOW_BACK |
                tui::cpn::txt::TextFlags::ALIGN_RIGHT))
        // Add a selector component that uses the trigger functions for navigation
        .with_selector(
            tui::sel::Selector::new(
                tui::trg::Trigger::new(up_trig_func, key_char),
                tui::trg::Trigger::new(down_trig_func, key_char),
                tui::trg::Trigger::new(selc_trig_func, key_char)));
    
    // Create a renderer with dimensions 40x20 characters
    let mut renderer = tui::ren::Renderer::new(40, 20);
    
    // Create a quit trigger using the quit_trig_func defined earlier
    let mut quit_trig = tui::trg::Trigger::new(quit_trig_func, key_char);
    
    // Flag to determine if the UI needs to be redrawn
    let mut should_update = true;
    
    // Initialize the renderer
    tui::ren::ready();
    
    // Main application loop
    loop {
        // Read user input
        key_char = read_key();
        
        // Update all triggers with the new key_char value
        container.selector_mut().update_trig_arg(key_char, key_char, key_char);
        quit_trig.update_arg(key_char);
        
        // Check if the quit trigger has been activated
        if quit_trig.check() {
            break;  // Exit the main loop if 'q' was pressed
        }
        
        // Render the UI if an update is needed
        if should_update {
            renderer.clear();           // Clear the screen
            renderer.render(&mut container);  // Render the container
            renderer.draw();            // Draw the rendered content to the screen
        }
        
        // Process UI logic and determine if an update is needed next iteration
        should_update = container.looper();
    }
    
    // Clean up the renderer before exiting
    tui::ren::unready();
}

πŸ—οΈ Dependencies

bitflags crossterm


🌱 Related Projects