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        ParagraphExt, Progress, ProgressStyle, Toast, ToastState, ToastStyle,
183    };
184
185    // Utility Components
186    pub use crate::components::{MousePointer, MousePointerState, MousePointerStyle};
187
188    // Navigation Components
189    pub use crate::components::{
190        EntryType, FileEntry, FileExplorer, FileExplorerState, FileExplorerStyle, ListPicker,
191        ListPickerState, ListPickerStyle, key_hints_footer,
192    };
193
194    // Tree Components
195    pub use crate::components::{
196        FlatNode, TreeNode, TreeStyle, TreeView, TreeViewState, get_selected_id,
197    };
198
199    // Layout Components
200    pub use crate::components::{
201        Orientation, SplitPane, SplitPaneAction, SplitPaneState, SplitPaneStyle,
202        handle_split_pane_key, handle_split_pane_mouse,
203    };
204
205    // Viewer Components
206    pub use crate::components::{
207        DiffData, DiffHunk, DiffLine, DiffLineType, DiffViewMode, DiffViewer, DiffViewerAction,
208        DiffViewerState, DiffViewerStyle, LogViewer, LogViewerState, LogViewerStyle, SearchState,
209        Step, StepDisplay, StepDisplayState, StepDisplayStyle, StepStatus, SubStep,
210        handle_diff_viewer_key, handle_diff_viewer_mouse, step_display_height,
211    };
212
213    // Dialog Components
214    pub use crate::components::{
215        CategoryClickRegion, HotkeyCategory, HotkeyClickRegion, HotkeyDialog, HotkeyDialogAction,
216        HotkeyDialogState, HotkeyDialogStyle, HotkeyEntryData, HotkeyFocus, HotkeyProvider,
217        handle_hotkey_dialog_key, handle_hotkey_dialog_mouse, render_hotkey_dialog,
218    };
219
220    // Utilities
221    pub use crate::utils::{
222        clean_for_display, format_size, pad_to_width, parse_ansi_to_spans, truncate_to_width,
223    };
224
225    // Traits
226    pub use crate::traits::{
227        ClickRegion, ClickRegionRegistry, Clickable, Container, ContainerAction, EventResult,
228        FocusId, Focusable, PopupContainer,
229    };
230
231    // State management
232    pub use crate::state::FocusManager;
233
234    // Event helpers
235    pub use crate::events::{
236        get_char, get_mouse_pos, get_scroll, has_alt, has_ctrl, has_shift, is_activate_key,
237        is_backspace, is_backtab, is_close_key, is_ctrl_a, is_ctrl_e, is_ctrl_k, is_ctrl_u,
238        is_ctrl_w, is_delete, is_end, is_enter, is_home, is_left_click, is_mouse_drag,
239        is_mouse_move, is_navigation_key, is_right_click, is_space, is_tab,
240    };
241}
242
243#[cfg(test)]
244mod tests {
245    use super::prelude::*;
246
247    #[test]
248    fn test_prelude_imports() {
249        // Verify all prelude items are accessible
250        let _: CheckBoxState = CheckBoxState::new(false);
251        let _: InputState = InputState::new("");
252        let _: ButtonState = ButtonState::enabled();
253        let _: FocusManager<usize> = FocusManager::new();
254        let _: ClickRegionRegistry<()> = ClickRegionRegistry::new();
255    }
256}