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 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}