# π¦ Feather-Tui



> 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)
```rust
// 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
- [Original C Version (part of my school management system)](https://github.com/nongtajkrub/school-management)