4 releases
Uses new Rust 2024
new 0.2.3 | Mar 24, 2025 |
---|---|
0.2.2 | Mar 24, 2025 |
0.1.9 |
|
#238 in Command-line interface
387 downloads per month
41KB
583 lines
π¦ 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.
π Changelog
tui_trg_new_trigger_func!
is changed totrg_new_trigger_func!
tui_cbk_new_callback_func!
is changed tocbk_new_callback_func!
- New method call
simple_draw
forRenderer
- Added some more documentation.
π¦ Crates
https://crates.io/crates/feather-tui
π Usage
I was not unexpecting people to actually use this crate. So here is a quick example I made in 5 minutes. (Comments are generated by Claude AI)
use feather_tui as tui;
// Define trigger functions for handling keyboard input
// This macro creates a function that checks if 'w' key was pressed (for moving up)
tui::trg_new_trigger_func!(up_trig_func, key_char, {
match key_char.downcast_ref::<std::option::Option<char>>().unwrap() {
Some(c) => *c == 'w', // Return true if 'w' is pressed
None => false, // Return false if no key was pressed
}
});
// Create trigger function for moving down with 's' key
tui::trg_new_trigger_func!(down_trig_func, key_char, {
match key_char.downcast_ref::<std::option::Option<char>>().unwrap() {
Some(c) => *c == 's', // Return true if 's' is pressed
None => false,
}
});
// Create trigger function for selection with 'e' key
tui::trg_new_trigger_func!(selc_trig_func, key_char, {
match key_char.downcast_ref::<std::option::Option<char>>().unwrap() {
Some(c) => *c == 'e', // Return true if 'e' is pressed
None => false,
}
});
// Create trigger function for quitting with 'q' key
tui::trg_new_trigger_func!(quit_trig_func, key_char, {
match key_char.downcast_ref::<std::option::Option<char>>().unwrap() {
Some(c) => *c == 'q', // Return true if 'q' is pressed
None => false,
}
});
// Define callback function that executes when a menu option is selected
tui::cbk_new_callback_func!(callback_func, argument, {
// Convert the u32 argument to a string to display which option was selected
let number_as_str =
argument.downcast_ref::<u32>().expect("Expect callback argument to be a u32").to_string();
// Create a new renderer with dimensions 40x20
tui::ren::Renderer::new(40, 20)
.simple_draw(
&mut tui::con::Container::new()
.with_header(
tui::cpn::hed::Header::new("Callback Trigger")) // Add header to container
.with_text(
tui::cpn::txt::Text::new(
&format!("Option {} selected", number_as_str), // Display which option was selected
tui::cpn::txt::TextFlags::COLOR_RED_BACK))); // Red background for the text
// Pause for 1 second to show the selection message
std::thread::sleep(std::time::Duration::from_secs(1));
});
// Helper function to read keyboard input
#[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 keypress
let mut key_char: std::option::Option<char> = read_key();
// Create main container with various UI components
let mut container = tui::con::Container::new()
.with_header(tui::cpn::hed::Header::new("Main Menu")) // Add header "Main Menu"
.with_option(
tui::cpn::opt::Option::new(
"Option1", // First menu option text
tui::cbk::Callback::new(callback_func, 1u32))) // Link to callback with argument 1
.with_option(
tui::cpn::opt::Option::new(
"Option2", // Second menu option text
tui::cbk::Callback::new(callback_func, 2u32))) // Link to callback with argument 2
.with_text(
tui::cpn::txt::Text::new(
"Text", // Additional text element
tui::cpn::txt::TextFlags::COLOR_YELLOW_BACK | // Yellow background
tui::cpn::txt::TextFlags::ALIGN_RIGHT)) // Right-aligned
.with_selector(
tui::sel::Selector::new(
tui::trg::Trigger::new(up_trig_func, key_char), // Up trigger ('w' key)
tui::trg::Trigger::new(down_trig_func, key_char), // Down trigger ('s' key)
tui::trg::Trigger::new(selc_trig_func, key_char))); // Select trigger ('e' key)
// Create renderer with 40x20 dimensions
let mut renderer = tui::ren::Renderer::new(40, 20);
// Create quit trigger with 'q' key
let mut quit_trig = tui::trg::Trigger::new(quit_trig_func, key_char);
// Flag to determine if UI needs to be redrawn
let mut should_update = true;
// Initialize the renderer
tui::ren::ready();
// Main application loop
loop {
// Read the latest keypress
key_char = read_key();
// Update all triggers with the latest keypress
container.selector_mut().update_trig_arg(key_char, key_char, key_char);
quit_trig.update_arg(key_char);
// Check if quit was triggered
if quit_trig.check() {
break; // Exit the loop if 'q' was pressed
}
// Redraw the UI if necessary
if should_update {
renderer.clear(); // Clear the screen
renderer.render(&mut container); // Render the container
renderer.draw(); // Draw to screen
}
// Process any container events and determine if update is needed next iteration
should_update = container.looper();
}
// Clean up renderer when application ends
tui::ren::unready();
}
ποΈ Dependencies
bitflags
crossterm
π± Related Projects
Dependencies
~3β11MB
~148K SLoC