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}