Skip to main content

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}