Skip to main content

basic_usage/
basic_usage.rs

1//! Basic MinUI example showing window creation and input handling.
2//!
3//! This example demonstrates the basic concepts of MinUI's app runner pattern:
4//! - Creating a terminal window and initializing the application
5//! - Handling keyboard events in the event loop
6//! - Rendering UI elements to the window
7//! - Separating update logic from drawing logic
8//!
9//! # The App Runner Pattern
10//!
11//! MinUI uses an **App runner** to manage the main event loop for you. Instead of manually
12//! handling terminal setup, event polling, and frame rendering, you provide two closures:
13//!
14//! 1. **Update closure**: Called for each input event. Your job is to update application state
15//!    based on the event and return `true` to continue running or `false` to exit.
16//!
17//! 2. **Draw closure**: Called after the update closure. Your job is to render the current
18//!    state to the window.
19//!
20//! This separation of concerns makes it easy to build responsive, event-driven TUI applications.
21//!
22//! # Example Structure
23//!
24//! ```ignore
25//! let mut app = App::new(initial_state)?;  // Create app with state
26//!
27//! app.run(
28//!     |state, event| {                      // Update: handle events
29//!         // Modify state based on event
30//!         // Return false to exit
31//!         true
32//!     },
33//!     |state, window| {                     // Draw: render UI
34//!         // Use state to draw UI elements
35//!         Ok(())
36//!     }
37//! )?;
38//! ```
39//!
40//! # State Management
41//!
42//! The state type is passed to `App::new()`. In this example, we use `()` (unit type)
43//! since we don't need to track any state. For more complex applications, you would define
44//! a struct and pass an instance of it.
45//!
46//! The state is available to both the update and draw closures, allowing you to:
47//! - Modify state in response to events (update closure)
48//! - Use the current state to render the UI (draw closure)
49//!
50//! # Event Handling
51//!
52//! The update closure receives an `Event` which can be:
53//! - **Keyboard events**: `Event::Character`, `Event::KeyUp`, `Event::KeyDown`, etc.
54//! - **Mouse events**: `Event::MouseClick`, `Event::MouseMove`, `Event::MouseScroll`, etc.
55//! - **System events**: `Event::Resize`, `Event::Frame` (when using a fixed frame rate)
56//!
57//! Return `false` from the update closure to exit the application.
58//!
59//! # Drawing
60//!
61//! The draw closure receives a mutable reference to the window. You can:
62//! - Create widgets like `Label`, `Panel`, `Container`
63//! - Call `.draw(window)?` to render them
64//! - Use low-level window methods like `window.write_str()` for custom rendering
65//!
66//! # Timed Updates (Optional)
67//!
68//! By default, the app runs in **event-driven mode** - the update closure is only called
69//! when there's input. For animations or realtime-style apps, you can enable a fixed frame rate:
70//!
71//! ```ignore
72//! let mut app = App::new(state)?.with_frame_rate(Duration::from_millis(16)); // ~60 FPS
73//! ```
74//!
75//! When using a fixed frame rate, you'll also receive `Event::Frame` events at regular intervals.
76
77use minui::prelude::*;
78
79fn main() -> minui::Result<()> {
80    // Create a new application instance with unit state `()`.
81    // This is the simplest case - we don't need to track any application state.
82    // For more complex apps, you'd pass a struct like `MyAppState { ... }`.
83    let mut app = App::new(())?;
84
85    // Run the application with update and draw closures.
86    // The app handles the event loop, window management, and rendering for you.
87    app.run(
88        // ========== UPDATE CLOSURE ==========
89        // Called whenever there's an input event (keyboard, mouse, etc.)
90        // Purpose: Update application state based on user input
91        // Returns: true to continue running, false to exit
92        |_state, event| {
93            // Handle the 'q' key to quit the application.
94            //
95            // The keyboard handler may emit:
96            // - `Event::KeyWithModifiers(KeyKind::Char('q'))` (modifier-aware), or
97            // - `Event::Character('q')` (legacy fallback).
98            match event {
99                Event::KeyWithModifiers(k) if matches!(k.key, KeyKind::Char('q')) => false,
100                Event::Character('q') => false,
101                _ => true,
102            }
103        },
104        // ========== DRAW CLOSURE ==========
105        // Called after each update to render the current application state
106        // Purpose: Draw the UI based on the current state
107        // The window parameter is where you draw everything
108        |_state, window| {
109            // Create a simple label widget centered on the screen
110            let label = Label::new("Press 'q' to quit").with_alignment(Alignment::Center);
111
112            // Draw the label to the window
113            // MinUI handles positioning, sizing, and rendering automatically
114            label.draw(window)?;
115
116            // Flush buffered rendering (App no longer auto-flushes after draw)
117            window.flush()?;
118
119            // Return Ok(()) to indicate drawing succeeded
120            Ok(())
121        },
122    )?;
123
124    // If we reach here, the app exited successfully
125    Ok(())
126}