ratatui_interact/lib.rs
1//! # TUI Extension
2//!
3//! Reusable UI components extending ratatui with focus management and mouse support.
4//!
5//! This crate provides interactive UI components that integrate with ratatui's
6//! widget system while adding:
7//!
8//! - **Focus Management**: Tab navigation between components
9//! - **Click Regions**: Mouse click support with hit-testing
10//! - **Composition**: Container-based component hierarchies for dialogs
11//!
12//! ## Quick Start
13//!
14//! ```rust,ignore
15//! use ratatui_interact::prelude::*;
16//!
17//! // Create component state
18//! let mut checkbox_state = CheckBoxState::new(false);
19//! let mut input_state = InputState::new("Hello");
20//! let button_state = ButtonState::enabled();
21//!
22//! // Use in your render function
23//! fn render(frame: &mut Frame, area: Rect) {
24//! let checkbox = CheckBox::new("Enable", &checkbox_state);
25//! let input = Input::new(&input_state).label("Name");
26//! let button = Button::new("Submit", &button_state);
27//!
28//! // Render and get click regions
29//! let cb_region = checkbox.render_stateful(area, frame.buffer_mut());
30//! let input_region = input.render_stateful(frame, input_area);
31//! let btn_region = button.render_stateful(button_area, frame.buffer_mut());
32//! }
33//! ```
34//!
35//! ## Components
36//!
37//! ### CheckBox
38//!
39//! A toggleable checkbox with customizable symbols:
40//!
41//! ```rust
42//! use ratatui_interact::components::{CheckBox, CheckBoxState, CheckBoxStyle};
43//!
44//! let mut state = CheckBoxState::new(false);
45//! let checkbox = CheckBox::new("Dark mode", &state)
46//! .style(CheckBoxStyle::unicode()); // ☑ / ☐
47//!
48//! // Toggle on user action
49//! state.toggle();
50//! ```
51//!
52//! ### Input
53//!
54//! A text input field with cursor and editing support:
55//!
56//! ```rust
57//! use ratatui_interact::components::{Input, InputState};
58//!
59//! let mut state = InputState::new("Initial text");
60//!
61//! // Edit text
62//! state.insert_char('!');
63//! state.move_left();
64//! state.delete_char_backward();
65//! ```
66//!
67//! ### Button
68//!
69//! Buttons with multiple display variants:
70//!
71//! ```rust
72//! use ratatui_interact::components::{Button, ButtonState, ButtonVariant, ButtonStyle};
73//!
74//! let state = ButtonState::enabled();
75//!
76//! // Different styles
77//! let simple = Button::new("OK", &state);
78//! let with_icon = Button::new("Save", &state).icon("💾");
79//! let block_style = Button::new("Submit", &state).variant(ButtonVariant::Block);
80//! let toggle = Button::new("Active", &ButtonState::toggled(true))
81//! .variant(ButtonVariant::Toggle);
82//! ```
83//!
84//! ### PopupDialog
85//!
86//! A container for popup dialogs with focus management:
87//!
88//! ```rust,ignore
89//! use ratatui_interact::components::{DialogConfig, DialogState, PopupDialog};
90//! use ratatui_interact::traits::ContainerAction;
91//!
92//! let config = DialogConfig::new("Settings")
93//! .width_percent(50)
94//! .ok_cancel();
95//!
96//! let mut state = DialogState::new(MyContent::default());
97//! state.show();
98//!
99//! let mut dialog = PopupDialog::new(&config, &mut state, |frame, area, content| {
100//! // Render dialog content
101//! });
102//! dialog.render(frame);
103//!
104//! // Handle events
105//! match dialog.handle_key(key_event) {
106//! EventResult::Action(ContainerAction::Submit) => { /* save */ }
107//! EventResult::Action(ContainerAction::Close) => { /* cancel */ }
108//! _ => {}
109//! }
110//! ```
111//!
112//! ## Focus Management
113//!
114//! The `FocusManager` handles Tab navigation:
115//!
116//! ```rust
117//! use ratatui_interact::state::FocusManager;
118//!
119//! #[derive(Clone, PartialEq, Eq, Hash)]
120//! enum Element { Name, Email, Submit }
121//!
122//! let mut focus = FocusManager::new();
123//! focus.register(Element::Name);
124//! focus.register(Element::Email);
125//! focus.register(Element::Submit);
126//!
127//! // Navigate
128//! focus.next(); // Name -> Email
129//! focus.prev(); // Email -> Name
130//! focus.set(Element::Submit); // Jump to Submit
131//! ```
132//!
133//! ## Click Regions
134//!
135//! Track clickable areas with `ClickRegionRegistry`:
136//!
137//! ```rust
138//! use ratatui_interact::traits::ClickRegionRegistry;
139//! use ratatui::layout::Rect;
140//!
141//! let mut registry: ClickRegionRegistry<&str> = ClickRegionRegistry::new();
142//!
143//! // Register during render
144//! registry.clear();
145//! registry.register(Rect::new(0, 0, 10, 1), "button1");
146//! registry.register(Rect::new(15, 0, 10, 1), "button2");
147//!
148//! // Check clicks during event handling
149//! if let Some(clicked) = registry.handle_click(5, 0) {
150//! println!("Clicked: {}", clicked);
151//! }
152//! ```
153
154pub mod components;
155pub mod events;
156pub mod state;
157pub mod theme;
158pub mod traits;
159pub mod utils;
160
161/// Prelude for convenient imports.
162///
163/// Import everything commonly needed:
164///
165/// ```rust
166/// use ratatui_interact::prelude::*;
167/// ```
168pub mod prelude {
169 // Interactive Components
170 pub use crate::components::{
171 Button, ButtonAction, ButtonState, ButtonStyle, ButtonVariant, CheckBox, CheckBoxAction,
172 CheckBoxState, CheckBoxStyle, ContextMenu, ContextMenuAction, ContextMenuItem,
173 ContextMenuState, ContextMenuStyle, DialogConfig, DialogFocusTarget, DialogState, Input,
174 InputAction, InputState, InputStyle, Menu, MenuBar, MenuBarAction, MenuBarClickTarget,
175 MenuBarItem, MenuBarState, MenuBarStyle, PopupDialog, calculate_menu_bar_height,
176 calculate_menu_height, handle_context_menu_key, handle_context_menu_mouse,
177 handle_menu_bar_key, handle_menu_bar_mouse, is_context_menu_trigger,
178 menu_bar_dropdown_height,
179 };
180
181 // Display Components
182 pub use crate::components::{
183 AnimatedText, AnimatedTextEffect, AnimatedTextState, AnimatedTextStyle, ParagraphExt,
184 Progress, ProgressStyle, ScrollableContent, ScrollableContentAction,
185 ScrollableContentState, ScrollableContentStyle, Toast, ToastDismissPolicy, ToastId,
186 ToastItem, ToastOrder, ToastPlacement, ToastStack, ToastStackLayout, ToastStackState,
187 ToastState, ToastStyle, WaveDirection, handle_scrollable_content_key,
188 handle_scrollable_content_mouse,
189 };
190
191 // Utility Components
192 pub use crate::components::{MousePointer, MousePointerState, MousePointerStyle};
193
194 // Navigation Components
195 pub use crate::components::{
196 EntryType, FileEntry, FileExplorer, FileExplorerState, FileExplorerStyle, ListPicker,
197 ListPickerState, ListPickerStyle, key_hints_footer,
198 };
199
200 // Tree Components
201 pub use crate::components::{
202 FlatNode, TreeNode, TreeStyle, TreeView, TreeViewState, get_selected_id,
203 };
204
205 // Layout Components
206 pub use crate::components::{
207 Orientation, SplitPane, SplitPaneAction, SplitPaneState, SplitPaneStyle,
208 handle_split_pane_key, handle_split_pane_mouse,
209 };
210
211 // Viewer Components
212 pub use crate::components::{
213 DiffData, DiffHunk, DiffLine, DiffLineType, DiffViewMode, DiffViewer, DiffViewerAction,
214 DiffViewerState, DiffViewerStyle, LogViewer, LogViewerState, LogViewerStyle, SearchState,
215 Step, StepDisplay, StepDisplayState, StepDisplayStyle, StepStatus, SubStep,
216 handle_diff_viewer_key, handle_diff_viewer_mouse, step_display_height,
217 };
218
219 // Dialog Components
220 pub use crate::components::{
221 CategoryClickRegion, HotkeyCategory, HotkeyClickRegion, HotkeyDialog, HotkeyDialogAction,
222 HotkeyDialogState, HotkeyDialogStyle, HotkeyEntryData, HotkeyFocus, HotkeyProvider,
223 handle_hotkey_dialog_key, handle_hotkey_dialog_mouse, render_hotkey_dialog,
224 };
225
226 // Theme
227 pub use crate::theme::{ColorPalette, Theme};
228
229 // Utilities
230 pub use crate::utils::{
231 clean_for_display, format_size, pad_to_width, parse_ansi_to_spans, truncate_to_width,
232 };
233
234 // Clipboard utilities
235 pub use crate::utils::{
236 ClipboardResult, copy_lines_to_clipboard, copy_to_clipboard, get_from_clipboard,
237 is_clipboard_available,
238 };
239
240 // Mouse capture utilities
241 pub use crate::utils::{
242 MouseCaptureState, disable_mouse_capture, enable_mouse_capture, set_mouse_capture,
243 toggle_mouse_capture,
244 };
245
246 // Traits
247 pub use crate::traits::{
248 ClickRegion, ClickRegionRegistry, Clickable, Container, ContainerAction, EventResult,
249 FocusId, Focusable, PopupContainer,
250 };
251
252 // State management
253 pub use crate::state::FocusManager;
254
255 // Event helpers
256 pub use crate::events::{
257 get_char, get_mouse_pos, get_scroll, has_alt, has_ctrl, has_shift, is_activate_key,
258 is_backspace, is_backtab, is_close_key, is_ctrl_a, is_ctrl_e, is_ctrl_k, is_ctrl_u,
259 is_ctrl_w, is_delete, is_end, is_enter, is_home, is_left_click, is_mouse_drag,
260 is_mouse_move, is_navigation_key, is_right_click, is_space, is_tab,
261 };
262}
263
264#[cfg(test)]
265mod tests {
266 use super::prelude::*;
267
268 #[test]
269 fn test_prelude_imports() {
270 // Verify all prelude items are accessible
271 let _: CheckBoxState = CheckBoxState::new(false);
272 let _: InputState = InputState::new("");
273 let _: ButtonState = ButtonState::enabled();
274 let _: FocusManager<usize> = FocusManager::new();
275 let _: ClickRegionRegistry<()> = ClickRegionRegistry::new();
276 }
277}