Files
docs/versioned_docs/version-0.1.0/00-intro/getting_started.md
T
2025-08-03 13:05:42 -05:00

5.8 KiB

slug
slug
/

Getting Started with OSUI

This guide will walk you through setting up your first OSUI project and running a basic application.

Prerequisites

Before you begin, ensure you have:

  • Rust and Cargo: If you don't have Rust installed, you can get it from rustup.rs. OSUI requires a recent stable version of Rust.

1. Create a New Cargo Project

First, create a new Rust binary project:

cargo new my_osui_app --bin
cd my_osui_app

2. Add OSUI to Your Dependencies

Open your Cargo.toml file and add osui to your [dependencies] section. We also recommend adding crossterm if you plan to handle raw terminal events directly, though OSUI uses it internally.

# Cargo.toml
[package]
name = "my_osui_app"
version = "0.1.0"
edition = "2021"

[dependencies]
osui = "0.1" # Use the latest version from crates.io
crossterm = "0.28" # Required for input handling, OSUI uses it internally
figlet-rs = "0.1" # Used by the Heading element, can be excluded if not needed

Note

: Always check crates.io/crates/osui for the latest available version.

3. Write Your First OSUI Application

Now, open src/main.rs and replace its contents with the following code. This example sets up a basic screen, adds an input handler (using an Extension), and displays a simple "Hello, OSUI!" message. It also includes a paginator with multiple "pages" to demonstrate basic navigation.

// src/main.rs
use osui::prelude::*;

fn main() -> std::io::Result<()> {
    // Create the main Screen instance.
    // The Screen manages the rendering loop, widgets, and extensions.
    let screen = Screen::new();

    // Register the InputExtension.
    // This extension enables raw mode and dispatches keyboard events to widgets.
    // Without it, keyboard input (like 'q' to quit or Tab for paginator) won't work.
    screen.extension(InputExtension);

    // Create a dynamic state variable for a counter.
    // This demonstrates OSUI's reactivity.
    let count = use_state(0);

    // Spawn a background thread to increment the counter every second.
    // This will cause the associated widget to re-render automatically.
    std::thread::spawn({
        let count = count.clone(); // Clone the Arc for the thread
        move || loop {
            // Dereference the MutexGuard and modify the inner value.
            // This also marks the state as 'changed'.
            **count.get() += 1;
            std::thread::sleep(std::time::Duration::from_secs(1));
        }
    });

    // Define the UI using the `rsx!` macro.
    // This is the declarative way to build your UI tree.
    rsx! {
        // Attach a Handler component to the root widget for key events.
        // This handler closes the screen when 'q' is pressed.
        @Handler::new({
            let screen = screen.clone(); // Clone the Arc for the closure
            move |_, e: &crossterm::event::Event| {
                if let crossterm::event::Event::Key(crossterm::event::KeyEvent { code, .. }) = e {
                    if *code == crossterm::event::KeyCode::Char('q') {
                        screen.close(); // Close the screen, exiting the main loop
                    }
                }
            }});
        // Paginator is an element that displays one child at a time.
        // Press Tab/Shift+Tab to cycle through its children.
        Paginator {
            // First page: A FlexRow container with a heading and text.
            FlexRow {
                Heading, smooth: false, { "OSUI" } // A large ASCII art heading
                "Welcome to the OSUI demo!"
                "Press tab to switch to the next page or shift+tab to the previous page"
            }

            // Second page: A FlexCol container with two Divs, demonstrating styling.
            FlexCol, gap: 3, {
                // Attach Transform and Style components directly to the Div.
                @Transform::new().padding(2, 2);
                @Style { foreground: None, background: Background::RoundedOutline(0x00ff00) };
                Div {
                    "This is text inside a div"
                }

                @Transform::new().padding(2, 2);
                @Style { foreground: None, background: Background::Outline(0x00ff00) };
                Div {
                    "This is text inside a div with square outlines"
                }
            }

            // Third page: Another FlexCol with a reactive counter and an Input element.
            FlexCol, gap: 2, {
                @transform!{ y: Center }; // Custom macro for convenient Transform creation
                static Div { // `static` keyword means this Div itself is static, but its children can be dynamic.
                    %count // The '%' symbol indicates a dependency on the 'count' state.
                    "This will increment every second: {count}" // 'count' will be automatically updated.
                }

                // An interactive Input field.
                @Transform::new().padding(1, 1).dimensions(40, 1);
                @Style { foreground: Some(0xffffff), background: Background::RoundedOutline(0xff0000) };
                Input { }
            }
        }
    }
    // Draw the entire RSX tree onto the screen.
    .draw(&screen);

    // Start the main event loop and rendering.
    screen.run()
}

4. Run Your Application

From your project's root directory, run:

cargo run

You should see a terminal application launch, displaying the "OSUI" heading and welcome message.

  • Press Tab to navigate through the pages.
  • Press Shift+Tab to go back.
  • Observe the counter incrementing on the third page.
  • Interact with the input field on the third page.
  • Press q to quit the application.

Congratulations! You've successfully set up and run your first OSUI application.