Skip to main content

tuiserial_ui/
lib.rs

1//! Terminal user interface components for tuiserial
2//!
3//! This crate provides the UI rendering logic using ratatui for displaying
4//! serial port configuration, logs, and user interactions with full mouse support.
5//!
6//! ## Architecture
7//!
8//! The UI is organized into modular components:
9//! - `areas`: UI area definitions and mouse interaction handling
10//! - `menu`: Menu bar and dropdown rendering
11//! - `config`: Configuration panel with dropdowns for serial settings
12//! - `status`: Status panel and statistics display
13//! - `log`: Log area showing serial communication data
14//! - `tx`: Transmission input area
15//! - `notification`: Notification bar for user messages
16//! - `utils`: Utility functions for UI rendering
17
18use ratatui::{
19    layout::{Constraint, Direction, Layout, Rect},
20    Frame,
21};
22use tuiserial_core::{AppState, MenuState};
23
24// Module declarations
25mod areas;
26mod config;
27mod help;
28mod log;
29mod menu;
30mod mouse;
31mod notification;
32mod shortcuts;
33mod status;
34mod tx;
35mod utils;
36
37// Re-exports for external use
38pub use areas::{
39    get_clicked_field, get_clicked_menu, get_clicked_tab, get_ui_areas, is_inside,
40    is_shortcuts_hint_clicked, update_cursor_state, UiAreas,
41};
42pub use crossterm;
43pub use mouse::{
44    calculate_dropdown_area, get_cursor_type, get_hover_style, handle_mouse_click,
45    handle_mouse_hover, handle_mouse_scroll, is_clickable_area, CursorType, MouseAction,
46    ScrollAction, ScrollDirection,
47};
48pub use ratatui;
49pub use shortcuts::{draw_context_shortcuts, draw_shortcuts_help, draw_shortcuts_hint};
50
51// Re-export menu functions
52pub use menu::find_clicked_menu;
53
54/// Main draw function - renders the entire application UI
55///
56/// This is the entry point for rendering the UI. It orchestrates the layout
57/// and delegates rendering to specialized modules.
58pub fn draw(f: &mut Frame, app: &AppState) {
59    // Main layout: menu bar, content area, notification bar, shortcuts hint
60    let chunks = Layout::default()
61        .direction(Direction::Vertical)
62        .constraints([
63            Constraint::Length(1), // Menu bar
64            Constraint::Min(15),   // Main content
65            Constraint::Length(3), // Notification area
66            Constraint::Length(1), // Shortcuts hint bar
67        ])
68        .split(f.area());
69
70    // Render main content first
71    draw_main_content(f, app, chunks[1]);
72
73    // Render notification bar
74    notification::draw_notification_bar(f, app, chunks[2]);
75
76    // Render shortcuts hint bar
77    shortcuts::draw_shortcuts_hint(f, chunks[3], app.language);
78
79    // Render menu bar (without dropdown)
80    menu::draw_menu_bar(f, app, chunks[0]);
81
82    // Render dropdown last to ensure it's on top
83    if let MenuState::Dropdown(menu_idx, item_idx) = app.menu_state {
84        menu::draw_menu_dropdown(f, app, chunks[0], menu_idx, item_idx);
85    }
86
87    // Render shortcuts help overlay if active (on top of everything)
88    if app.show_shortcuts_help {
89        shortcuts::draw_shortcuts_help(f, app.language);
90    }
91
92    // Store menu bar, notification area, and shortcuts hint for mouse interaction
93    areas::update_area(areas::UiAreaField::MenuBar, chunks[0]);
94    areas::update_area(areas::UiAreaField::NotificationArea, chunks[2]);
95    areas::update_area(areas::UiAreaField::ShortcutsHint, chunks[3]);
96}
97
98/// Draw the main content area (config panel + log/tx areas)
99fn draw_main_content(f: &mut Frame, app: &AppState, area: Rect) {
100    let chunks = Layout::default()
101        .direction(Direction::Horizontal)
102        .constraints([Constraint::Length(42), Constraint::Min(50)])
103        .split(area);
104
105    draw_config_panel(f, app, chunks[0]);
106    draw_main_area(f, app, chunks[1]);
107}
108
109/// Draw the configuration panel on the left
110fn draw_config_panel(f: &mut Frame, app: &AppState, area: Rect) {
111    let chunks = Layout::default()
112        .direction(Direction::Vertical)
113        .constraints([
114            Constraint::Length(5), // Port
115            Constraint::Length(5), // Baud rate
116            Constraint::Length(3), // Data bits
117            Constraint::Length(3), // Parity
118            Constraint::Length(3), // Stop bits
119            Constraint::Length(3), // Flow control
120            Constraint::Min(10),   // Status panel
121        ])
122        .split(area);
123
124    config::draw_port_dropdown(f, app, chunks[0]);
125    config::draw_baud_rate_dropdown(f, app, chunks[1]);
126    config::draw_data_bits_dropdown(f, app, chunks[2]);
127    config::draw_parity_dropdown(f, app, chunks[3]);
128    config::draw_stop_bits_dropdown(f, app, chunks[4]);
129    config::draw_flow_control_dropdown(f, app, chunks[5]);
130    status::draw_status_panel(f, app, chunks[6]);
131}
132
133/// Draw the main area on the right (log + tx + control)
134fn draw_main_area(f: &mut Frame, app: &AppState, area: Rect) {
135    let chunks = Layout::default()
136        .direction(Direction::Vertical)
137        .constraints([
138            Constraint::Min(10),   // Log area
139            Constraint::Length(7), // TX area
140            Constraint::Length(3), // Control/stats area
141        ])
142        .split(area);
143
144    log::draw_log_area(f, app, chunks[0]);
145    tx::draw_tx_area(f, app, chunks[1]);
146    status::draw_control_area(f, app, chunks[2]);
147}