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 traits;
158pub mod utils;
159
160/// Prelude for convenient imports.
161///
162/// Import everything commonly needed:
163///
164/// ```rust
165/// use ratatui_interact::prelude::*;
166/// ```
167pub mod prelude {
168 // Interactive Components
169 pub use crate::components::{
170 Button, ButtonAction, ButtonState, ButtonStyle, ButtonVariant, CheckBox, CheckBoxAction,
171 CheckBoxState, CheckBoxStyle, ContextMenu, ContextMenuAction, ContextMenuItem,
172 ContextMenuState, ContextMenuStyle, DialogConfig, DialogFocusTarget, DialogState, Input,
173 InputAction, InputState, InputStyle, Menu, MenuBar, MenuBarAction, MenuBarClickTarget,
174 MenuBarItem, MenuBarState, MenuBarStyle, PopupDialog, calculate_menu_bar_height,
175 calculate_menu_height, handle_context_menu_key, handle_context_menu_mouse,
176 handle_menu_bar_key, handle_menu_bar_mouse, is_context_menu_trigger,
177 menu_bar_dropdown_height,
178 };
179
180 // Display Components
181 pub use crate::components::{
182 AnimatedText, AnimatedTextEffect, AnimatedTextState, AnimatedTextStyle, ParagraphExt,
183 Progress, ProgressStyle, ScrollableContent, ScrollableContentAction,
184 ScrollableContentState, ScrollableContentStyle, Toast, ToastState, ToastStyle,
185 WaveDirection, handle_scrollable_content_key, handle_scrollable_content_mouse,
186 };
187
188 // Utility Components
189 pub use crate::components::{MousePointer, MousePointerState, MousePointerStyle};
190
191 // Navigation Components
192 pub use crate::components::{
193 EntryType, FileEntry, FileExplorer, FileExplorerState, FileExplorerStyle, ListPicker,
194 ListPickerState, ListPickerStyle, key_hints_footer,
195 };
196
197 // Tree Components
198 pub use crate::components::{
199 FlatNode, TreeNode, TreeStyle, TreeView, TreeViewState, get_selected_id,
200 };
201
202 // Layout Components
203 pub use crate::components::{
204 Orientation, SplitPane, SplitPaneAction, SplitPaneState, SplitPaneStyle,
205 handle_split_pane_key, handle_split_pane_mouse,
206 };
207
208 // Viewer Components
209 pub use crate::components::{
210 DiffData, DiffHunk, DiffLine, DiffLineType, DiffViewMode, DiffViewer, DiffViewerAction,
211 DiffViewerState, DiffViewerStyle, LogViewer, LogViewerState, LogViewerStyle, SearchState,
212 Step, StepDisplay, StepDisplayState, StepDisplayStyle, StepStatus, SubStep,
213 handle_diff_viewer_key, handle_diff_viewer_mouse, step_display_height,
214 };
215
216 // Dialog Components
217 pub use crate::components::{
218 CategoryClickRegion, HotkeyCategory, HotkeyClickRegion, HotkeyDialog, HotkeyDialogAction,
219 HotkeyDialogState, HotkeyDialogStyle, HotkeyEntryData, HotkeyFocus, HotkeyProvider,
220 handle_hotkey_dialog_key, handle_hotkey_dialog_mouse, render_hotkey_dialog,
221 };
222
223 // Utilities
224 pub use crate::utils::{
225 clean_for_display, format_size, pad_to_width, parse_ansi_to_spans, truncate_to_width,
226 };
227
228 // Clipboard utilities
229 pub use crate::utils::{
230 ClipboardResult, copy_lines_to_clipboard, copy_to_clipboard, get_from_clipboard,
231 is_clipboard_available,
232 };
233
234 // Mouse capture utilities
235 pub use crate::utils::{
236 MouseCaptureState, disable_mouse_capture, enable_mouse_capture, set_mouse_capture,
237 toggle_mouse_capture,
238 };
239
240 // Traits
241 pub use crate::traits::{
242 ClickRegion, ClickRegionRegistry, Clickable, Container, ContainerAction, EventResult,
243 FocusId, Focusable, PopupContainer,
244 };
245
246 // State management
247 pub use crate::state::FocusManager;
248
249 // Event helpers
250 pub use crate::events::{
251 get_char, get_mouse_pos, get_scroll, has_alt, has_ctrl, has_shift, is_activate_key,
252 is_backspace, is_backtab, is_close_key, is_ctrl_a, is_ctrl_e, is_ctrl_k, is_ctrl_u,
253 is_ctrl_w, is_delete, is_end, is_enter, is_home, is_left_click, is_mouse_drag,
254 is_mouse_move, is_navigation_key, is_right_click, is_space, is_tab,
255 };
256}
257
258#[cfg(test)]
259mod tests {
260 use super::prelude::*;
261
262 #[test]
263 fn test_prelude_imports() {
264 // Verify all prelude items are accessible
265 let _: CheckBoxState = CheckBoxState::new(false);
266 let _: InputState = InputState::new("");
267 let _: ButtonState = ButtonState::enabled();
268 let _: FocusManager<usize> = FocusManager::new();
269 let _: ClickRegionRegistry<()> = ClickRegionRegistry::new();
270 }
271}