ratatui_toolkit/master_layout/
layout_manager.rs

1//! Master Layout - Top-level orchestrator for tabs, navigation, and modes
2
3use crate::master_layout::MasterLayoutKeyBindings;
4use super::{InteractionMode, PaneId, Tab};
5use crate::{MenuBar, MenuItem};
6use crossterm::event::{Event, KeyCode, KeyEvent, MouseButton, MouseEvent, MouseEventKind};
7use ratatui::layout::{Constraint, Direction, Layout, Rect};
8
9/// Result of event handling
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum EventResult {
12    /// Event was consumed by the master layout or a pane
13    Consumed,
14    /// Event was not handled
15    NotHandled,
16    /// Application should quit
17    Quit,
18}
19
20/// Master Layout - orchestrates the entire TUI application
21///
22/// # Architecture
23///
24/// ```text
25/// ┌─────────────────────────────────┐
26/// │ Navigation Bar (1 row + border) │ ← 3 rows total
27/// ├─────────────────────────────────┤
28/// │                                 │
29/// │         Active Tab              │ ← Remaining space
30/// │  (panes + footer)               │
31/// │                                 │
32/// └─────────────────────────────────┘
33/// ```
34///
35/// # Modes
36///
37/// - **Layout Mode**: Navigate panes with hjkl, Tab/Shift+Tab, Enter to focus
38/// - **Focus Mode**: All input goes to focused pane, Ctrl-A to exit
39///
40/// # Auto-Focus Mode
41///
42/// When `auto_focus` is enabled, the selected pane immediately receives input:
43/// - No Enter required to focus
44/// - No Ctrl-A to exit
45/// - Tab/Shift+Tab switch panes and route input to the new pane
46/// - Global keys (q, 1-9) still work for app control
47///
48/// # Key Bindings
49///
50/// **Global (always work)**:
51/// - Ctrl+Q: Quit
52/// - 1-9: Switch tabs
53///
54/// **Layout Mode** (auto_focus: false):
55/// - h/j/k/l: Directional navigation
56/// - Tab: Next pane
57/// - Shift+Tab: Previous pane
58/// - Enter: Focus selected pane
59///
60/// **Layout Mode** (auto_focus: true):
61/// - h/j/k/l: Route to selected pane
62/// - Tab: Next pane + route input
63/// - Shift+Tab: Previous pane + route input
64/// - Enter: Route to selected pane
65///
66/// **Focus Mode**:
67/// - Ctrl-A: Exit to Layout Mode
68/// - All other keys: Route to focused pane
69/// - Mouse on same pane: Route to pane
70/// - Mouse on different pane: Change focus
71pub struct MasterLayout {
72    nav_bar: MenuBar,
73    tabs: Vec<Tab>,
74    active_tab_index: usize,
75    mode: InteractionMode,
76    global_area: Rect,
77    nav_bar_offset: u16,
78    keybindings: MasterLayoutKeyBindings,
79    auto_focus: bool,
80}
81
82impl MasterLayout {
83    /// Create a new empty master layout
84    pub fn new() -> Self {
85        Self {
86            nav_bar: MenuBar::new(Vec::new()),
87            tabs: Vec::new(),
88            active_tab_index: 0,
89            mode: InteractionMode::default(),
90            global_area: Rect::default(),
91            nav_bar_offset: 0,
92            keybindings: MasterLayoutKeyBindings::default(),
93            auto_focus: false,
94        }
95    }
96
97    /// Set custom keybindings for the layout
98    ///
99    /// # Example
100    ///
101    /// ```
102    /// use ratatui_toolkit::master_layout::{MasterLayout, MasterLayoutKeyBindings};
103    /// use crossterm::event::{KeyCode, KeyModifiers};
104    ///
105    /// let mut bindings = MasterLayoutKeyBindings::default();
106    /// bindings.quit = vec![(KeyCode::Char('x'), KeyModifiers::empty())];
107    ///
108    /// let layout = MasterLayout::new().with_keybindings(bindings);
109    /// ```
110    pub fn with_keybindings(mut self, keybindings: MasterLayoutKeyBindings) -> Self {
111        self.keybindings = keybindings;
112        self
113    }
114
115    /// Get the current keybindings
116    pub fn keybindings(&self) -> &MasterLayoutKeyBindings {
117        &self.keybindings
118    }
119
120    /// Set the keybindings
121    pub fn set_keybindings(&mut self, keybindings: MasterLayoutKeyBindings) {
122        self.keybindings = keybindings;
123    }
124
125    /// Enable auto-focus mode (selected pane receives input immediately)
126    ///
127    /// # Example
128    ///
129    /// ```
130    /// use ratatui_toolkit::master_layout::MasterLayout;
131    ///
132    /// // Default: modal behavior (Enter to focus, Ctrl-A to exit)
133    /// let layout = MasterLayout::new();
134    ///
135    /// // Auto-focus: selected pane receives input immediately
136    /// let layout = MasterLayout::new().with_auto_focus(true);
137    /// ```
138    pub fn with_auto_focus(mut self, enabled: bool) -> Self {
139        self.auto_focus = enabled;
140        self
141    }
142
143    /// Get the auto-focus setting
144    pub fn auto_focus(&self) -> bool {
145        self.auto_focus
146    }
147
148    /// Set the auto-focus setting
149    pub fn set_auto_focus(&mut self, enabled: bool) {
150        self.auto_focus = enabled;
151    }
152
153    /// Set the navigation bar left offset (to make room for other components like IconNavBar)
154    pub fn set_nav_bar_offset(&mut self, offset: u16) {
155        self.nav_bar_offset = offset;
156    }
157
158    /// Add a tab to the master layout
159    pub fn add_tab(&mut self, tab: Tab) {
160        self.tabs.push(tab);
161
162        // Rebuild nav bar with updated tab names as menu items
163        let menu_items: Vec<MenuItem> = self
164            .tabs
165            .iter()
166            .enumerate()
167            .map(|(i, t)| MenuItem::new(t.name(), i))
168            .collect();
169        self.nav_bar = MenuBar::new(menu_items).with_selected(self.active_tab_index);
170
171        // If this is the first tab and we have panes, select the first pane
172        if self.tabs.len() == 1 {
173            self.select_first_pane_in_active_tab();
174        }
175    }
176
177    /// Get number of tabs
178    pub fn tab_count(&self) -> usize {
179        self.tabs.len()
180    }
181
182    /// Get active tab index
183    pub fn active_tab_index(&self) -> usize {
184        self.active_tab_index
185    }
186
187    /// Set active tab by index
188    pub fn set_active_tab(&mut self, index: usize) {
189        if index < self.tabs.len() {
190            self.active_tab_index = index;
191            // Update menu bar selection
192            for (i, item) in self.nav_bar.items.iter_mut().enumerate() {
193                item.selected = i == index;
194            }
195
196            // When switching tabs, select first pane in new tab
197            self.select_first_pane_in_active_tab();
198        }
199    }
200
201    /// Get reference to active tab
202    pub fn active_tab(&self) -> Option<&Tab> {
203        self.tabs.get(self.active_tab_index)
204    }
205
206    /// Get mutable reference to active tab
207    pub fn active_tab_mut(&mut self) -> Option<&mut Tab> {
208        self.tabs.get_mut(self.active_tab_index)
209    }
210
211    /// Get current interaction mode
212    pub fn mode(&self) -> &InteractionMode {
213        &self.mode
214    }
215
216    /// Enter Layout Mode
217    pub fn enter_layout_mode(&mut self) {
218        if let Some(focused_id) = self.mode.focused_pane() {
219            // If we're in focus mode, transition to layout mode with selection
220            self.mode = InteractionMode::layout_with_selection(focused_id);
221        } else {
222            // Just ensure we're in layout mode
223            if !self.mode.is_layout() {
224                self.mode = InteractionMode::layout();
225            }
226        }
227    }
228
229    /// Enter Focus Mode with a specific pane
230    pub fn enter_focus_mode(&mut self, pane_id: PaneId) {
231        self.mode = InteractionMode::focus(pane_id);
232    }
233
234    /// Exit Focus Mode (Ctrl-A) - returns to Layout Mode
235    pub fn exit_focus_mode(&mut self) {
236        self.mode.exit_focus();
237    }
238
239    /// Select the first focusable pane in the active tab
240    fn select_first_pane_in_active_tab(&mut self) {
241        if let Some(tab) = self.active_tab() {
242            let container = tab.pane_container();
243            if let Some(first_pane) = container.select_next(None) {
244                self.mode = InteractionMode::layout_with_selection(first_pane);
245            }
246        }
247    }
248
249    /// Select next pane (Tab key in Layout Mode)
250    pub fn select_next_pane(&mut self) {
251        if !self.mode.is_layout() {
252            return;
253        }
254
255        if let Some(tab) = self.active_tab() {
256            let current = self.mode.selected_pane();
257            if let Some(next) = tab.pane_container().select_next(current) {
258                self.mode.select_pane(next);
259            }
260        }
261    }
262
263    /// Select previous pane (Shift+Tab key in Layout Mode)
264    pub fn select_prev_pane(&mut self) {
265        if !self.mode.is_layout() {
266            return;
267        }
268
269        if let Some(tab) = self.active_tab() {
270            let current = self.mode.selected_pane();
271            if let Some(prev) = tab.pane_container().select_prev(current) {
272                self.mode.select_pane(prev);
273            }
274        }
275    }
276
277    /// Select pane to the left (h key in Layout Mode)
278    pub fn select_left(&mut self) {
279        if !self.mode.is_layout() {
280            return;
281        }
282
283        if let Some(tab) = self.active_tab() {
284            if let Some(current) = self.mode.selected_pane() {
285                if let Some(left) = tab.pane_container().select_left(current) {
286                    self.mode.select_pane(left);
287                }
288            }
289        }
290    }
291
292    /// Select pane to the right (l key in Layout Mode)
293    pub fn select_right(&mut self) {
294        if !self.mode.is_layout() {
295            return;
296        }
297
298        if let Some(tab) = self.active_tab() {
299            if let Some(current) = self.mode.selected_pane() {
300                if let Some(right) = tab.pane_container().select_right(current) {
301                    self.mode.select_pane(right);
302                }
303            }
304        }
305    }
306
307    /// Select pane above (k key in Layout Mode)
308    pub fn select_up(&mut self) {
309        if !self.mode.is_layout() {
310            return;
311        }
312
313        if let Some(tab) = self.active_tab() {
314            if let Some(current) = self.mode.selected_pane() {
315                if let Some(up) = tab.pane_container().select_up(current) {
316                    self.mode.select_pane(up);
317                }
318            }
319        }
320    }
321
322    /// Select pane below (j key in Layout Mode)
323    pub fn select_down(&mut self) {
324        if !self.mode.is_layout() {
325            return;
326        }
327
328        if let Some(tab) = self.active_tab() {
329            if let Some(current) = self.mode.selected_pane() {
330                if let Some(down) = tab.pane_container().select_down(current) {
331                    self.mode.select_pane(down);
332                }
333            }
334        }
335    }
336
337    /// Focus the currently selected pane (Enter key in Layout Mode)
338    pub fn focus_selected(&mut self) {
339        if !self.mode.is_layout() {
340            return;
341        }
342
343        if let Some(selected) = self.mode.selected_pane() {
344            self.enter_focus_mode(selected);
345        }
346    }
347
348    /// Handle a crossterm event
349    pub fn handle_event(&mut self, event: Event) -> EventResult {
350        match event {
351            Event::Key(key) => self.handle_key_event(key),
352            Event::Mouse(mouse) => self.handle_mouse_event(mouse),
353            Event::Resize(_, _) => EventResult::Consumed,
354            _ => EventResult::NotHandled,
355        }
356    }
357
358    /// Handle keyboard events
359    fn handle_key_event(&mut self, key: KeyEvent) -> EventResult {
360        // Mode-specific handling
361        match self.mode.clone() {
362            InteractionMode::Layout { selected_pane } => {
363                // Auto-focus mode: route keys directly to selected pane
364                if self.auto_focus {
365                    if let Some(pane_id) = selected_pane {
366                        return self.handle_auto_focus_layout_key(key, pane_id);
367                    }
368                }
369
370                // In Layout Mode: Check quit keybinding
371                if self.keybindings.is_quit(&key) {
372                    return EventResult::Quit;
373                }
374
375                // In Layout Mode: Handle copy_selection and clear_selection for text selection if pane has selection
376                if let Some(pane_id) = selected_pane {
377                    // Handle copy selection - only if pane has selection
378                    if self.keybindings.is_copy_selection(&key) {
379                        if let Some(tab) = self.active_tab_mut() {
380                            if let Some(pane) = tab.pane_container_mut().get_pane_mut(pane_id) {
381                                // Only route to pane if it has a selection
382                                if pane.has_selection() && pane.handle_key(key) {
383                                    return EventResult::Consumed;
384                                }
385                            }
386                        }
387                    }
388
389                    // Handle clear_selection (Esc) - only if pane has selection
390                    if self.keybindings.is_clear_selection(&key) {
391                        if let Some(tab) = self.active_tab_mut() {
392                            if let Some(pane) = tab.pane_container_mut().get_pane_mut(pane_id) {
393                                // Only route to pane if it has a selection to clear
394                                if pane.has_selection() && pane.handle_key(key) {
395                                    return EventResult::Consumed;
396                                }
397                            }
398                        }
399                        // If no selection to clear, fall through to let Layout Mode handle it
400                    }
401                }
402
403                // Handle other Layout Mode keys (hjkl navigation, Enter, tab switching, etc.)
404                self.handle_layout_mode_key(key)
405            }
406            InteractionMode::Focus { focused_pane } => {
407                // Check exit_focus_mode keybinding - EVERYTHING else goes to the pane
408                // This includes 'q', 'h', 'j', 'k', 'l', numbers, etc.
409                if self.keybindings.is_exit_focus_mode(&key) {
410                    self.exit_focus_mode();
411                    return EventResult::Consumed;
412                }
413
414                // ALL other keys (including 'q', Ctrl+C, Ctrl+W, Esc, etc.) go to focused pane
415                if let Some(tab) = self.active_tab_mut() {
416                    if let Some(pane) = tab.pane_container_mut().get_pane_mut(focused_pane) {
417                        if pane.handle_key(key) {
418                            return EventResult::Consumed;
419                        }
420                    }
421                }
422
423                EventResult::NotHandled
424            }
425        }
426    }
427
428    /// Handle keyboard events in Layout Mode
429    fn handle_layout_mode_key(&mut self, key: KeyEvent) -> EventResult {
430        // Clear selection (Esc)
431        if self.keybindings.is_clear_selection(&key) {
432            self.mode = InteractionMode::Layout {
433                selected_pane: None,
434            };
435            return EventResult::Consumed;
436        }
437
438        // Deselect pane (Ctrl-A in Layout Mode)
439        if self.keybindings.is_deselect_pane(&key) {
440            self.mode = InteractionMode::Layout {
441                selected_pane: None,
442            };
443            return EventResult::Consumed;
444        }
445
446        // Switch tabs (1-9)
447        if let Some(tab_index) = self.keybindings.get_tab_switch_index(&key) {
448            if tab_index < self.tab_count() {
449                self.set_active_tab(tab_index);
450                return EventResult::Consumed;
451            }
452            return EventResult::NotHandled;
453        }
454
455        // Directional navigation (hjkl)
456        if self.keybindings.is_navigate_left(&key) {
457            self.select_left();
458            return EventResult::Consumed;
459        }
460        if self.keybindings.is_navigate_down(&key) {
461            self.select_down();
462            return EventResult::Consumed;
463        }
464        if self.keybindings.is_navigate_up(&key) {
465            self.select_up();
466            return EventResult::Consumed;
467        }
468        if self.keybindings.is_navigate_right(&key) {
469            self.select_right();
470            return EventResult::Consumed;
471        }
472
473        // Focus selected pane (Enter)
474        if self.keybindings.is_focus_pane(&key) {
475            self.focus_selected();
476            return EventResult::Consumed;
477        }
478
479        EventResult::NotHandled
480    }
481
482    /// Handle keyboard events in Layout Mode with auto-focus enabled
483    fn handle_auto_focus_layout_key(&mut self, key: KeyEvent, pane_id: PaneId) -> EventResult {
484        // Check global keys first (quit and tab switching)
485        if self.keybindings.is_quit(&key) {
486            return EventResult::Quit;
487        }
488
489        if let Some(tab_index) = self.keybindings.get_tab_switch_index(&key) {
490            if tab_index < self.tab_count() {
491                self.set_active_tab(tab_index);
492                return EventResult::Consumed;
493            }
494            return EventResult::NotHandled;
495        }
496
497        // Handle tab navigation: switch pane AND route to new pane
498        if key.code == KeyCode::Tab && key.modifiers.is_empty() {
499            self.select_next_pane();
500            if let Some(new_pane) = self.mode.selected_pane() {
501                return self.route_key_to_pane(key, new_pane);
502            }
503            return EventResult::NotHandled;
504        }
505
506        if key.code == KeyCode::BackTab {
507            self.select_prev_pane();
508            if let Some(new_pane) = self.mode.selected_pane() {
509                return self.route_key_to_pane(key, new_pane);
510            }
511            return EventResult::NotHandled;
512        }
513
514        // Handle navigation keys that both navigate AND route to pane
515        if self.keybindings.is_navigate_left(&key) {
516            self.select_left();
517            if let Some(new_pane) = self.mode.selected_pane() {
518                return self.route_key_to_pane(key, new_pane);
519            }
520            return EventResult::NotHandled;
521        }
522
523        if self.keybindings.is_navigate_right(&key) {
524            self.select_right();
525            if let Some(new_pane) = self.mode.selected_pane() {
526                return self.route_key_to_pane(key, new_pane);
527            }
528            return EventResult::NotHandled;
529        }
530
531        if self.keybindings.is_navigate_up(&key) {
532            self.select_up();
533            if let Some(new_pane) = self.mode.selected_pane() {
534                return self.route_key_to_pane(key, new_pane);
535            }
536            return EventResult::NotHandled;
537        }
538
539        if self.keybindings.is_navigate_down(&key) {
540            self.select_down();
541            if let Some(new_pane) = self.mode.selected_pane() {
542                return self.route_key_to_pane(key, new_pane);
543            }
544            return EventResult::NotHandled;
545        }
546
547        // All other keys go directly to the selected pane
548        self.route_key_to_pane(key, pane_id)
549    }
550
551    /// Route a key event to a specific pane
552    fn route_key_to_pane(&mut self, key: KeyEvent, pane_id: PaneId) -> EventResult {
553        if let Some(tab) = self.active_tab_mut() {
554            if let Some(pane) = tab.pane_container_mut().get_pane_mut(pane_id) {
555                if pane.handle_key(key) {
556                    return EventResult::Consumed;
557                }
558            }
559        }
560        EventResult::NotHandled
561    }
562
563    /// Handle mouse events
564    fn handle_mouse_event(&mut self, mouse: MouseEvent) -> EventResult {
565        // Check if click is on navigation bar
566        if mouse.kind == MouseEventKind::Down(MouseButton::Left) {
567            // Nav bar is in top 3 rows
568            if mouse.row < 3 {
569                if let Some(tab_index) = self.nav_bar.handle_click(mouse.column, mouse.row) {
570                    self.set_active_tab(tab_index);
571                    return EventResult::Consumed;
572                }
573            }
574        }
575
576        // Handle pane resizing (only in Layout Mode)
577        if matches!(self.mode, InteractionMode::Layout { .. }) {
578            if let Some(tab) = self.active_tab_mut() {
579                let container = tab.pane_container_mut();
580
581                match mouse.kind {
582                    MouseEventKind::Down(MouseButton::Left) => {
583                        // Check if clicking on a divider
584                        if let Some(divider_idx) =
585                            container.find_divider_at(mouse.column, mouse.row)
586                        {
587                            container.start_drag(divider_idx);
588                            return EventResult::Consumed;
589                        }
590                    }
591                    MouseEventKind::Drag(MouseButton::Left) => {
592                        // Update drag if dragging
593                        if container.is_dragging() {
594                            container.update_drag(mouse.column, mouse.row);
595                            return EventResult::Consumed;
596                        }
597                    }
598                    MouseEventKind::Up(MouseButton::Left) => {
599                        // Stop dragging
600                        if container.is_dragging() {
601                            container.stop_drag();
602                            return EventResult::Consumed;
603                        }
604                    }
605                    MouseEventKind::Moved => {
606                        // Update hover state
607                        container.update_hover(mouse.column, mouse.row);
608                    }
609                    _ => {}
610                }
611            }
612        }
613
614        // Handle text selection in focused/selected pane
615        match mouse.kind {
616            MouseEventKind::Down(MouseButton::Left) if mouse.row >= 3 => {
617                // Get the currently selected/focused pane BEFORE borrowing
618                let current_pane = match &self.mode {
619                    InteractionMode::Layout { selected_pane } => *selected_pane,
620                    InteractionMode::Focus { focused_pane } => Some(*focused_pane),
621                };
622
623                // Check if clicking on a pane
624                if let Some(tab) = self.active_tab_mut() {
625                    if let Some(pane_id) =
626                        tab.pane_container().find_pane_at(mouse.column, mouse.row)
627                    {
628                        // If clicking on the current pane, start text selection
629                        if Some(pane_id) == current_pane {
630                            if let Some(pane) = tab.pane_container_mut().get_pane_mut(pane_id) {
631                                if pane.contains_point(mouse.column, mouse.row) {
632                                    let local_mouse = pane.translate_mouse(mouse);
633                                    pane.start_selection(local_mouse.column, local_mouse.row);
634                                    return EventResult::Consumed;
635                                }
636                            }
637                        } else {
638                            // Clicking on a different pane: select it
639                            match &mut self.mode {
640                                InteractionMode::Layout { .. } => {
641                                    self.mode.select_pane(pane_id);
642                                }
643                                InteractionMode::Focus { focused_pane } => {
644                                    if *focused_pane != pane_id {
645                                        self.enter_focus_mode(pane_id);
646                                    }
647                                }
648                            }
649                            return EventResult::Consumed;
650                        }
651                    }
652                }
653            }
654            MouseEventKind::Drag(MouseButton::Left) if mouse.row >= 3 => {
655                // Update selection if dragging in current pane
656                let current_pane = match &self.mode {
657                    InteractionMode::Layout { selected_pane } => *selected_pane,
658                    InteractionMode::Focus { focused_pane } => Some(*focused_pane),
659                };
660
661                if let Some(pane_id) = current_pane {
662                    if let Some(tab) = self.active_tab_mut() {
663                        if let Some(pane) = tab.pane_container_mut().get_pane_mut(pane_id) {
664                            if pane.contains_point(mouse.column, mouse.row) {
665                                let local_mouse = pane.translate_mouse(mouse);
666                                pane.update_selection(local_mouse.column, local_mouse.row);
667                                return EventResult::Consumed;
668                            }
669                        }
670                    }
671                }
672            }
673            MouseEventKind::Up(MouseButton::Left) if mouse.row >= 3 => {
674                // End selection
675                let current_pane = match &self.mode {
676                    InteractionMode::Layout { selected_pane } => *selected_pane,
677                    InteractionMode::Focus { focused_pane } => Some(*focused_pane),
678                };
679
680                if let Some(pane_id) = current_pane {
681                    if let Some(tab) = self.active_tab_mut() {
682                        if let Some(pane) = tab.pane_container_mut().get_pane_mut(pane_id) {
683                            pane.end_selection();
684                            return EventResult::Consumed;
685                        }
686                    }
687                }
688            }
689            _ => {}
690        }
691
692        // If in Focus Mode, route mouse events to focused pane
693        if let InteractionMode::Focus { focused_pane } = self.mode.clone() {
694            if let Some(tab) = self.active_tab_mut() {
695                if let Some(pane) = tab.pane_container_mut().get_pane_mut(focused_pane) {
696                    // Check if mouse is within focused pane
697                    if pane.contains_point(mouse.column, mouse.row) {
698                        // Translate to pane-local coordinates and route
699                        let local_mouse = pane.translate_mouse(mouse);
700                        if pane.handle_mouse(local_mouse) {
701                            return EventResult::Consumed;
702                        }
703                    } else {
704                        // Mouse clicked outside focused pane - change focus
705                        if mouse.kind == MouseEventKind::Down(MouseButton::Left) {
706                            if let Some(new_pane) =
707                                tab.pane_container().find_pane_at(mouse.column, mouse.row)
708                            {
709                                self.enter_focus_mode(new_pane);
710                                return EventResult::Consumed;
711                            }
712                        }
713                    }
714                }
715            }
716        }
717
718        EventResult::NotHandled
719    }
720
721    /// Render the master layout
722    pub fn render(&mut self, frame: &mut ratatui::Frame) {
723        self.global_area = frame.area();
724
725        // Calculate layout: nav bar (3 rows) + active tab (remaining)
726        let chunks = Layout::default()
727            .direction(Direction::Vertical)
728            .constraints([
729                Constraint::Length(3), // Nav bar (1 row content + 2 border)
730                Constraint::Min(5),    // Tab content (minimum 5 rows)
731            ])
732            .split(self.global_area);
733
734        let nav_area = chunks[0];
735        let tab_area = chunks[1];
736
737        // Render navigation bar as menu bar with offset
738        self.nav_bar
739            .render_with_offset(frame, nav_area, self.nav_bar_offset);
740
741        // Render active tab
742        let mode = self.mode.clone();
743        if let Some(tab) = self.active_tab_mut() {
744            tab.render(frame, tab_area, &mode);
745        }
746    }
747}
748
749impl Default for MasterLayout {
750    fn default() -> Self {
751        Self::new()
752    }
753}
754
755#[cfg(test)]
756mod tests {
757    use super::*;
758    use crate::master_layout::{Pane, PaneContent};
759    use crossterm::event::{KeyCode, KeyModifiers, MouseButton, MouseEventKind};
760    use ratatui::{backend::TestBackend, buffer::Buffer, widgets::Widget, Terminal};
761
762    // Mock PaneContent for testing
763    struct MockContent {
764        title: String,
765        key_count: usize,
766        mouse_count: usize,
767        last_key: Option<KeyEvent>,
768    }
769
770    impl MockContent {
771        fn new(title: &str) -> Self {
772            Self {
773                title: title.to_string(),
774                key_count: 0,
775                mouse_count: 0,
776                last_key: None,
777            }
778        }
779    }
780
781    impl Widget for MockContent {
782        fn render(self, _area: Rect, _buf: &mut Buffer) {}
783    }
784
785    impl PaneContent for MockContent {
786        fn handle_key(&mut self, key: KeyEvent) -> bool {
787            self.key_count += 1;
788            self.last_key = Some(key);
789            true
790        }
791
792        fn handle_mouse(&mut self, _mouse: MouseEvent) -> bool {
793            self.mouse_count += 1;
794            true
795        }
796
797        fn title(&self) -> String {
798            self.title.clone()
799        }
800
801        fn render_content(&mut self, _area: Rect, _frame: &mut ratatui::Frame) {
802            // Mock implementation - do nothing
803        }
804    }
805
806    fn create_test_layout() -> MasterLayout {
807        let mut layout = MasterLayout::new();
808
809        // Create a tab with two panes
810        let mut tab = Tab::new("Test Tab");
811        let pane1 = Pane::new(PaneId::new("pane1"), Box::new(MockContent::new("Pane 1")));
812        let pane2 = Pane::new(PaneId::new("pane2"), Box::new(MockContent::new("Pane 2")));
813        tab.add_pane(pane1);
814        tab.add_pane(pane2);
815
816        layout.add_tab(tab);
817        layout
818    }
819
820    #[test]
821    fn test_master_layout_creation() {
822        let layout = MasterLayout::new();
823        assert_eq!(layout.tab_count(), 0);
824        assert_eq!(layout.active_tab_index(), 0);
825        assert!(layout.mode().is_layout());
826    }
827
828    #[test]
829    fn test_add_tab() {
830        let mut layout = MasterLayout::new();
831        let tab = Tab::new("Tab 1");
832
833        layout.add_tab(tab);
834
835        assert_eq!(layout.tab_count(), 1);
836        assert!(layout.active_tab().is_some());
837        assert_eq!(layout.active_tab().unwrap().name(), "Tab 1");
838    }
839
840    #[test]
841    fn test_add_multiple_tabs() {
842        let mut layout = MasterLayout::new();
843
844        layout.add_tab(Tab::new("Tab 1"));
845        layout.add_tab(Tab::new("Tab 2"));
846        layout.add_tab(Tab::new("Tab 3"));
847
848        assert_eq!(layout.tab_count(), 3);
849        assert_eq!(layout.active_tab_index(), 0);
850    }
851
852    #[test]
853    fn test_set_active_tab() {
854        let mut layout = MasterLayout::new();
855        layout.add_tab(Tab::new("Tab 1"));
856        layout.add_tab(Tab::new("Tab 2"));
857        layout.add_tab(Tab::new("Tab 3"));
858
859        layout.set_active_tab(1);
860        assert_eq!(layout.active_tab_index(), 1);
861        assert_eq!(layout.active_tab().unwrap().name(), "Tab 2");
862
863        layout.set_active_tab(2);
864        assert_eq!(layout.active_tab_index(), 2);
865        assert_eq!(layout.active_tab().unwrap().name(), "Tab 3");
866    }
867
868    #[test]
869    fn test_set_active_tab_invalid_index() {
870        let mut layout = MasterLayout::new();
871        layout.add_tab(Tab::new("Tab 1"));
872
873        layout.set_active_tab(10);
874        // Should remain at 0
875        assert_eq!(layout.active_tab_index(), 0);
876    }
877
878    #[test]
879    fn test_mode_transitions() {
880        let mut layout = create_test_layout();
881
882        // Should start in Layout Mode
883        assert!(layout.mode().is_layout());
884
885        // Get a pane ID to focus
886        let pane_id = layout
887            .active_tab()
888            .unwrap()
889            .pane_container()
890            .get_pane_by_index(0)
891            .unwrap()
892            .id();
893
894        // Enter Focus Mode
895        layout.enter_focus_mode(pane_id);
896        assert!(layout.mode().is_focus());
897        assert_eq!(layout.mode().focused_pane(), Some(pane_id));
898
899        // Exit Focus Mode
900        layout.exit_focus_mode();
901        assert!(layout.mode().is_layout());
902        assert_eq!(layout.mode().selected_pane(), Some(pane_id));
903    }
904
905    #[test]
906    fn test_enter_key_focuses_selected_pane() {
907        let mut layout = create_test_layout();
908
909        // Should have a pane selected (first pane auto-selected on tab add)
910        let selected = layout.mode().selected_pane();
911        assert!(selected.is_some());
912
913        // Press Enter
914        let key = KeyEvent::new(KeyCode::Enter, KeyModifiers::empty());
915        let result = layout.handle_key_event(key);
916
917        assert_eq!(result, EventResult::Consumed);
918        assert!(layout.mode().is_focus());
919        assert_eq!(layout.mode().focused_pane(), selected);
920    }
921
922    #[test]
923    fn test_ctrl_a_exits_focus_mode() {
924        let mut layout = create_test_layout();
925
926        let pane_id = layout
927            .active_tab()
928            .unwrap()
929            .pane_container()
930            .get_pane_by_index(0)
931            .unwrap()
932            .id();
933
934        // Enter focus mode
935        layout.enter_focus_mode(pane_id);
936        assert!(layout.mode().is_focus());
937
938        // Press Ctrl-A
939        let key = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL);
940        let result = layout.handle_key_event(key);
941
942        assert_eq!(result, EventResult::Consumed);
943        assert!(layout.mode().is_layout());
944        assert_eq!(layout.mode().selected_pane(), Some(pane_id));
945    }
946
947    #[test]
948    fn test_q_quits() {
949        let mut layout = create_test_layout();
950
951        let key = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::empty());
952        let result = layout.handle_key_event(key);
953
954        assert_eq!(result, EventResult::Quit);
955    }
956
957    #[test]
958    fn test_tab_key_not_handled_in_layout_mode() {
959        // Verify that Tab does NOT cycle panes in Layout Mode
960        let mut layout = create_test_layout();
961
962        let first_selected = layout.mode().selected_pane();
963        assert!(first_selected.is_some());
964
965        // Press Tab - should NOT be handled in Layout Mode
966        let key = KeyEvent::new(KeyCode::Tab, KeyModifiers::empty());
967        let result = layout.handle_key_event(key);
968
969        // Tab should not be consumed by Layout Mode
970        assert_eq!(result, EventResult::NotHandled);
971
972        // Selection should not change
973        let still_selected = layout.mode().selected_pane();
974        assert_eq!(first_selected, still_selected);
975    }
976
977    #[test]
978    fn test_shift_tab_not_handled_in_layout_mode() {
979        // Verify that Shift+Tab does NOT cycle panes in Layout Mode
980        let mut layout = create_test_layout();
981
982        let first_selected = layout.mode().selected_pane();
983
984        // Press Shift+Tab - should NOT be handled in Layout Mode
985        let key = KeyEvent::new(KeyCode::BackTab, KeyModifiers::SHIFT);
986        let result = layout.handle_key_event(key);
987
988        // Shift+Tab should not be consumed
989        assert_eq!(result, EventResult::NotHandled);
990
991        // Selection should not change
992        let still_selected = layout.mode().selected_pane();
993        assert_eq!(first_selected, still_selected);
994    }
995
996    #[test]
997    fn test_digit_keys_switch_tabs_in_layout_mode() {
998        // Verify that number keys ONLY switch tabs in Layout Mode
999        let mut layout = MasterLayout::new();
1000
1001        let mut tab1 = Tab::new("Tab 1");
1002        tab1.add_pane(Pane::new(
1003            PaneId::new("p1"),
1004            Box::new(MockContent::new("P1")),
1005        ));
1006        layout.add_tab(tab1);
1007
1008        let mut tab2 = Tab::new("Tab 2");
1009        tab2.add_pane(Pane::new(
1010            PaneId::new("p2"),
1011            Box::new(MockContent::new("P2")),
1012        ));
1013        layout.add_tab(tab2);
1014
1015        let mut tab3 = Tab::new("Tab 3");
1016        tab3.add_pane(Pane::new(
1017            PaneId::new("p3"),
1018            Box::new(MockContent::new("P3")),
1019        ));
1020        layout.add_tab(tab3);
1021
1022        // Verify we're in Layout Mode
1023        assert!(layout.mode().is_layout());
1024
1025        // Press '2' to switch to second tab
1026        let key = KeyEvent::new(KeyCode::Char('2'), KeyModifiers::empty());
1027        let result = layout.handle_key_event(key);
1028
1029        assert_eq!(result, EventResult::Consumed);
1030        assert_eq!(layout.active_tab_index(), 1);
1031
1032        // Press '3' to switch to third tab
1033        let key = KeyEvent::new(KeyCode::Char('3'), KeyModifiers::empty());
1034        layout.handle_key_event(key);
1035        assert_eq!(layout.active_tab_index(), 2);
1036    }
1037
1038    #[test]
1039    fn test_hjkl_navigation_in_layout_mode() {
1040        let mut layout = create_test_layout();
1041
1042        // Setup: ensure we're in layout mode with a selection
1043        assert!(layout.mode().is_layout());
1044        assert!(layout.mode().selected_pane().is_some());
1045
1046        // Test h key (left)
1047        let key = KeyEvent::new(KeyCode::Char('h'), KeyModifiers::empty());
1048        let result = layout.handle_key_event(key);
1049        assert_eq!(result, EventResult::Consumed);
1050
1051        // Test j key (down)
1052        let key = KeyEvent::new(KeyCode::Char('j'), KeyModifiers::empty());
1053        let result = layout.handle_key_event(key);
1054        assert_eq!(result, EventResult::Consumed);
1055
1056        // Test k key (up)
1057        let key = KeyEvent::new(KeyCode::Char('k'), KeyModifiers::empty());
1058        let result = layout.handle_key_event(key);
1059        assert_eq!(result, EventResult::Consumed);
1060
1061        // Test l key (right)
1062        let key = KeyEvent::new(KeyCode::Char('l'), KeyModifiers::empty());
1063        let result = layout.handle_key_event(key);
1064        assert_eq!(result, EventResult::Consumed);
1065    }
1066
1067    #[test]
1068    fn test_hjkl_ignored_in_focus_mode() {
1069        let mut layout = create_test_layout();
1070
1071        let pane_id = layout
1072            .active_tab()
1073            .unwrap()
1074            .pane_container()
1075            .get_pane_by_index(0)
1076            .unwrap()
1077            .id();
1078
1079        // Enter focus mode
1080        layout.enter_focus_mode(pane_id);
1081
1082        let initial_mode = layout.mode().clone();
1083
1084        // Press h - should be routed to pane, not change selection
1085        let key = KeyEvent::new(KeyCode::Char('h'), KeyModifiers::empty());
1086        layout.handle_key_event(key);
1087
1088        // Should still be in focus mode with same pane
1089        assert!(layout.mode().is_focus());
1090        assert_eq!(layout.mode(), &initial_mode);
1091    }
1092
1093    #[test]
1094    fn test_mouse_click_on_nav_bar() {
1095        let mut layout = MasterLayout::new();
1096        layout.add_tab(Tab::new("Tab 1"));
1097        layout.add_tab(Tab::new("Tab 2"));
1098
1099        // Start on first tab
1100        assert_eq!(layout.active_tab_index(), 0);
1101
1102        // Render to calculate nav bar button areas
1103        let backend = TestBackend::new(80, 24);
1104        let mut terminal = Terminal::new(backend).unwrap();
1105        terminal
1106            .draw(|frame| {
1107                layout.render(frame);
1108            })
1109            .unwrap();
1110
1111        // Get the actual button area for the second tab after rendering
1112        // We need to access nav_bar to find the button position
1113        // For now, we'll test by switching tabs using the API directly
1114        // The click handling is tested indirectly through other tests
1115
1116        // Alternative: Test that clicking in nav bar area is handled
1117        let mouse = MouseEvent {
1118            kind: MouseEventKind::Down(MouseButton::Left),
1119            column: 5,
1120            row: 1,
1121            modifiers: KeyModifiers::empty(),
1122        };
1123
1124        let result = layout.handle_mouse_event(mouse);
1125        // Should be consumed if click hits a button, or NotHandled if it misses
1126        // Since button layout is calculated during render, we just verify it doesn't panic
1127        assert!(result == EventResult::Consumed || result == EventResult::NotHandled);
1128    }
1129
1130    #[test]
1131    fn test_mouse_click_on_pane_focuses() {
1132        let mut layout = create_test_layout();
1133
1134        // Render to calculate pane areas
1135        let backend = TestBackend::new(80, 24);
1136        let mut terminal = Terminal::new(backend).unwrap();
1137        terminal
1138            .draw(|frame| {
1139                layout.render(frame);
1140            })
1141            .unwrap();
1142
1143        // Click on pane area (below nav bar)
1144        let mouse = MouseEvent {
1145            kind: MouseEventKind::Down(MouseButton::Left),
1146            column: 10,
1147            row: 5,
1148            modifiers: KeyModifiers::empty(),
1149        };
1150
1151        layout.handle_mouse_event(mouse);
1152
1153        // Should enter focus mode (if click hit a pane)
1154        // Note: This test is simplified; in real usage, pane areas are calculated during render
1155    }
1156
1157    #[test]
1158    fn test_keys_routed_to_focused_pane() {
1159        let mut layout = create_test_layout();
1160
1161        let pane_id = layout
1162            .active_tab()
1163            .unwrap()
1164            .pane_container()
1165            .get_pane_by_index(0)
1166            .unwrap()
1167            .id();
1168
1169        // Enter focus mode
1170        layout.enter_focus_mode(pane_id);
1171
1172        // Send a key event
1173        let key = KeyEvent::new(KeyCode::Char('x'), KeyModifiers::empty());
1174        let result = layout.handle_key_event(key);
1175
1176        assert_eq!(result, EventResult::Consumed);
1177        // Pane should have received the key (verified by MockContent.key_count in real test)
1178    }
1179
1180    #[test]
1181    fn test_tab_key_routed_to_pane_in_focus_mode() {
1182        // Verify that Tab key goes to the pane in Focus Mode, not navigation
1183        let mut layout = create_test_layout();
1184
1185        let pane_id = layout
1186            .active_tab()
1187            .unwrap()
1188            .pane_container()
1189            .get_pane_by_index(0)
1190            .unwrap()
1191            .id();
1192
1193        // Enter focus mode
1194        layout.enter_focus_mode(pane_id);
1195        assert!(layout.mode().is_focus());
1196
1197        // Press Tab - should be routed to pane, not used for navigation
1198        let key = KeyEvent::new(KeyCode::Tab, KeyModifiers::empty());
1199        let result = layout.handle_key_event(key);
1200
1201        // Tab should be consumed (by pane or by layout attempting to route it)
1202        // The key point is it should NOT switch panes
1203        assert_eq!(result, EventResult::Consumed);
1204
1205        // Should still be in focus mode with the same pane
1206        assert!(layout.mode().is_focus());
1207        assert_eq!(layout.mode().focused_pane(), Some(pane_id));
1208    }
1209
1210    #[test]
1211    fn test_number_keys_routed_to_pane_in_focus_mode() {
1212        // Verify that number keys go to pane in Focus Mode, NOT switch tabs
1213        let mut layout = MasterLayout::new();
1214
1215        let mut tab1 = Tab::new("Tab 1");
1216        tab1.add_pane(Pane::new(
1217            PaneId::new("p1"),
1218            Box::new(MockContent::new("P1")),
1219        ));
1220        layout.add_tab(tab1);
1221
1222        let mut tab2 = Tab::new("Tab 2");
1223        tab2.add_pane(Pane::new(
1224            PaneId::new("p2"),
1225            Box::new(MockContent::new("P2")),
1226        ));
1227        layout.add_tab(tab2);
1228
1229        // Start on first tab
1230        assert_eq!(layout.active_tab_index(), 0);
1231
1232        let pane_id = layout
1233            .active_tab()
1234            .unwrap()
1235            .pane_container()
1236            .get_pane_by_index(0)
1237            .unwrap()
1238            .id();
1239
1240        // Enter focus mode on first tab
1241        layout.enter_focus_mode(pane_id);
1242        assert!(layout.mode().is_focus());
1243
1244        // Press '2' - should go to pane, NOT switch to tab 2
1245        let key = KeyEvent::new(KeyCode::Char('2'), KeyModifiers::empty());
1246        let result = layout.handle_key_event(key);
1247
1248        // Should be consumed by pane
1249        assert_eq!(result, EventResult::Consumed);
1250
1251        // Should still be on first tab (NOT switched to tab 2)
1252        assert_eq!(layout.active_tab_index(), 0);
1253
1254        // Should still be in focus mode with the same pane
1255        assert!(layout.mode().is_focus());
1256        assert_eq!(layout.mode().focused_pane(), Some(pane_id));
1257    }
1258
1259    #[test]
1260    fn test_only_ctrl_a_exits_focus_mode() {
1261        // Verify that ONLY Ctrl-A exits Focus Mode, other keys don't
1262        let mut layout = create_test_layout();
1263
1264        let pane_id = layout
1265            .active_tab()
1266            .unwrap()
1267            .pane_container()
1268            .get_pane_by_index(0)
1269            .unwrap()
1270            .id();
1271
1272        // Enter focus mode
1273        layout.enter_focus_mode(pane_id);
1274        assert!(layout.mode().is_focus());
1275
1276        // Try various keys - none should exit focus mode (except 'q' which quits the app)
1277        let test_keys = vec![
1278            KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()),
1279            KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),
1280            KeyEvent::new(KeyCode::Char('h'), KeyModifiers::empty()),
1281            KeyEvent::new(KeyCode::Tab, KeyModifiers::empty()),
1282            KeyEvent::new(KeyCode::Char('x'), KeyModifiers::empty()),
1283        ];
1284
1285        for key in test_keys {
1286            layout.handle_key_event(key);
1287            // Should still be in focus mode
1288            assert!(
1289                layout.mode().is_focus(),
1290                "Key {:?} should not exit focus mode",
1291                key
1292            );
1293        }
1294
1295        // Now press Ctrl-A - should exit focus mode
1296        let key = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL);
1297        let result = layout.handle_key_event(key);
1298
1299        assert_eq!(result, EventResult::Consumed);
1300        assert!(layout.mode().is_layout());
1301    }
1302
1303    #[test]
1304    fn test_all_keys_passed_to_focused_pane() {
1305        // Verify that ALL keys (including Ctrl+W, Ctrl+C, etc.) are passed to focused pane
1306        let mut layout = create_test_layout();
1307
1308        let pane_id = layout
1309            .active_tab()
1310            .unwrap()
1311            .pane_container()
1312            .get_pane_by_index(0)
1313            .unwrap()
1314            .id();
1315
1316        // Enter focus mode
1317        layout.enter_focus_mode(pane_id);
1318        assert!(layout.mode().is_focus());
1319
1320        // Test various control sequences that should be passed through
1321        let test_keys = vec![
1322            KeyEvent::new(KeyCode::Char('w'), KeyModifiers::CONTROL), // Ctrl+W
1323            KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL), // Ctrl+C
1324            KeyEvent::new(KeyCode::Char('d'), KeyModifiers::CONTROL), // Ctrl+D
1325            KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()),       // Esc
1326            KeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL), // Ctrl+R
1327        ];
1328
1329        for key in test_keys {
1330            let result = layout.handle_key_event(key);
1331
1332            // Key should be consumed (passed to pane)
1333            assert_eq!(
1334                result,
1335                EventResult::Consumed,
1336                "Key {:?} should be passed to pane",
1337                key
1338            );
1339
1340            // Should still be in focus mode
1341            assert!(
1342                layout.mode().is_focus(),
1343                "Key {:?} should not exit focus mode",
1344                key
1345            );
1346
1347            // Verify the pane received the key by checking last_key
1348            let _pane = layout
1349                .active_tab()
1350                .unwrap()
1351                .pane_container()
1352                .get_pane(pane_id)
1353                .unwrap();
1354
1355            // We need to access the MockContent's last_key, but it's boxed
1356            // For now, just verify we're still in focus mode
1357            // The key_count verification in other tests confirms keys are being passed
1358        }
1359    }
1360
1361    #[test]
1362    fn test_q_quits_only_in_layout_mode() {
1363        // Verify Q (without modifiers) quits ONLY in Layout Mode, NOT in Focus Mode
1364        let mut layout = create_test_layout();
1365
1366        // Test in Layout Mode - 'q' should quit
1367        assert!(layout.mode().is_layout());
1368        let key = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::empty());
1369        let result = layout.handle_key_event(key);
1370        assert_eq!(result, EventResult::Quit, "'q' should quit in Layout Mode");
1371
1372        // Reset layout and test in Focus Mode - 'q' should be sent to pane
1373        let mut layout = create_test_layout();
1374        let pane_id = layout
1375            .active_tab()
1376            .unwrap()
1377            .pane_container()
1378            .get_pane_by_index(0)
1379            .unwrap()
1380            .id();
1381
1382        layout.enter_focus_mode(pane_id);
1383        assert!(layout.mode().is_focus());
1384
1385        let key = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::empty());
1386        let result = layout.handle_key_event(key);
1387        assert_eq!(
1388            result,
1389            EventResult::Consumed,
1390            "'q' should be consumed by pane in Focus Mode, NOT quit app"
1391        );
1392
1393        // Verify still in Focus Mode (key was consumed, not used for app control)
1394        assert!(
1395            layout.mode().is_focus(),
1396            "Should still be in Focus Mode after 'q'"
1397        );
1398    }
1399
1400    #[test]
1401    fn test_q_in_focus_mode_regression() {
1402        // Regression test: Ensure 'q' key in Focus Mode does NOT quit the application
1403        // Bug history: Previously, 'q' was checked globally before mode handling,
1404        // causing it to quit even when a terminal was focused
1405        let mut layout = create_test_layout();
1406
1407        // Enter Focus Mode on the first pane
1408        let pane_id = layout
1409            .active_tab()
1410            .unwrap()
1411            .pane_container()
1412            .get_pane_by_index(0)
1413            .unwrap()
1414            .id();
1415        layout.enter_focus_mode(pane_id);
1416
1417        // Verify we're in Focus Mode
1418        assert!(layout.mode().is_focus(), "Should be in Focus Mode");
1419
1420        // Press 'q' - this should go to the pane, NOT quit
1421        let q_key = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::empty());
1422        let result = layout.handle_key_event(q_key);
1423
1424        // Critical assertion: 'q' should be consumed by pane, not trigger quit
1425        assert_ne!(
1426            result,
1427            EventResult::Quit,
1428            "REGRESSION: 'q' in Focus Mode should NOT quit the application"
1429        );
1430        assert_eq!(
1431            result,
1432            EventResult::Consumed,
1433            "'q' should be consumed by the focused pane"
1434        );
1435
1436        // Verify we're still in Focus Mode (didn't quit or exit Focus Mode)
1437        assert!(
1438            layout.mode().is_focus(),
1439            "Should still be in Focus Mode after pressing 'q'"
1440        );
1441
1442        // Press multiple 'q' keys to ensure consistent behavior
1443        for _ in 0..5 {
1444            let result = layout.handle_key_event(q_key);
1445            assert_eq!(
1446                result,
1447                EventResult::Consumed,
1448                "Multiple 'q' presses should all be consumed"
1449            );
1450            assert!(layout.mode().is_focus(), "Should remain in Focus Mode");
1451        }
1452
1453        // Final verification: still in Focus Mode after multiple 'q' presses
1454        assert!(
1455            layout.mode().is_focus(),
1456            "Should remain in Focus Mode after all 'q' presses"
1457        );
1458    }
1459
1460    #[test]
1461    fn test_uppercase_q_in_focus_mode() {
1462        // Regression test: Ensure uppercase 'Q' (Shift+Q) in Focus Mode does NOT quit
1463        // Bug: When pressing Shift+Q, it generates KeyCode::Char('Q') which wasn't being
1464        // handled properly and could slip through to quit the app
1465        let mut layout = create_test_layout();
1466
1467        // Enter Focus Mode
1468        let pane_id = layout
1469            .active_tab()
1470            .unwrap()
1471            .pane_container()
1472            .get_pane_by_index(0)
1473            .unwrap()
1474            .id();
1475        layout.enter_focus_mode(pane_id);
1476        assert!(layout.mode().is_focus());
1477
1478        // Press uppercase 'Q' (Shift+Q) - should go to pane, NOT quit
1479        let uppercase_q = KeyEvent::new(KeyCode::Char('Q'), KeyModifiers::empty());
1480        let result = layout.handle_key_event(uppercase_q);
1481
1482        // Should NOT quit the application
1483        assert_ne!(
1484            result,
1485            EventResult::Quit,
1486            "REGRESSION: Uppercase 'Q' (Shift+Q) in Focus Mode should NOT quit the application"
1487        );
1488        assert_eq!(
1489            result,
1490            EventResult::Consumed,
1491            "Uppercase 'Q' should be consumed by the focused pane"
1492        );
1493
1494        // Should still be in Focus Mode
1495        assert!(
1496            layout.mode().is_focus(),
1497            "Should still be in Focus Mode after pressing 'Q'"
1498        );
1499    }
1500
1501    #[test]
1502    fn test_uppercase_q_quits_in_layout_mode() {
1503        // Both 'q' and 'Q' should quit when in Layout Mode (case-insensitive)
1504        let mut layout = create_test_layout();
1505        assert!(layout.mode().is_layout());
1506
1507        // Test lowercase 'q'
1508        let lowercase_q = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::empty());
1509        let result = layout.handle_key_event(lowercase_q);
1510        assert_eq!(
1511            result,
1512            EventResult::Quit,
1513            "Lowercase 'q' should quit in Layout Mode"
1514        );
1515
1516        // Reset and test uppercase 'Q'
1517        let mut layout = create_test_layout();
1518        let uppercase_q = KeyEvent::new(KeyCode::Char('Q'), KeyModifiers::empty());
1519        let result = layout.handle_key_event(uppercase_q);
1520        assert_eq!(
1521            result,
1522            EventResult::Quit,
1523            "Uppercase 'Q' should quit in Layout Mode"
1524        );
1525    }
1526
1527    #[test]
1528    fn test_render_does_not_panic() {
1529        let mut layout = create_test_layout();
1530
1531        let backend = TestBackend::new(80, 24);
1532        let mut terminal = Terminal::new(backend).unwrap();
1533
1534        terminal
1535            .draw(|frame| {
1536                layout.render(frame);
1537            })
1538            .unwrap();
1539    }
1540
1541    #[test]
1542    fn test_empty_layout_renders() {
1543        let mut layout = MasterLayout::new();
1544
1545        let backend = TestBackend::new(80, 24);
1546        let mut terminal = Terminal::new(backend).unwrap();
1547
1548        terminal
1549            .draw(|frame| {
1550                layout.render(frame);
1551            })
1552            .unwrap();
1553    }
1554
1555    #[test]
1556    fn test_tab_switching_preserves_mode() {
1557        let mut layout = MasterLayout::new();
1558
1559        // Create two tabs with panes
1560        let mut tab1 = Tab::new("Tab 1");
1561        tab1.add_pane(Pane::new(
1562            PaneId::new("t1p1"),
1563            Box::new(MockContent::new("T1P1")),
1564        ));
1565        layout.add_tab(tab1);
1566
1567        let mut tab2 = Tab::new("Tab 2");
1568        tab2.add_pane(Pane::new(
1569            PaneId::new("t2p1"),
1570            Box::new(MockContent::new("T2P1")),
1571        ));
1572        layout.add_tab(tab2);
1573
1574        // Switch to tab 2
1575        layout.set_active_tab(1);
1576
1577        // Mode should still be layout mode with first pane selected
1578        assert!(layout.mode().is_layout());
1579        assert!(layout.mode().selected_pane().is_some());
1580    }
1581
1582    #[test]
1583    fn test_no_panes_no_selection() {
1584        let mut layout = MasterLayout::new();
1585        let tab = Tab::new("Empty Tab");
1586        layout.add_tab(tab);
1587
1588        // No panes, so no selection should be made
1589        assert!(layout.mode().is_layout());
1590    }
1591
1592    #[test]
1593    fn test_event_handling_resize() {
1594        let mut layout = create_test_layout();
1595
1596        let event = Event::Resize(100, 50);
1597        let result = layout.handle_event(event);
1598
1599        assert_eq!(result, EventResult::Consumed);
1600    }
1601
1602    #[test]
1603    fn test_select_next_with_no_tabs() {
1604        let mut layout = MasterLayout::new();
1605        layout.select_next_pane();
1606        // Should not panic
1607    }
1608
1609    #[test]
1610    fn test_focus_selected_with_no_selection() {
1611        let mut layout = MasterLayout::new();
1612        let tab = Tab::new("Empty Tab");
1613        layout.add_tab(tab);
1614
1615        // No panes to select
1616        layout.focus_selected();
1617        // Should remain in layout mode
1618        assert!(layout.mode().is_layout());
1619    }
1620
1621    #[test]
1622    fn test_esc_deselects_in_layout_mode() {
1623        let mut layout = create_test_layout();
1624
1625        // Verify we start with a pane selected
1626        assert!(layout.mode().is_layout());
1627        assert!(layout.mode().selected_pane().is_some());
1628
1629        // Press ESC to clear selection
1630        let key = KeyEvent::new(KeyCode::Esc, KeyModifiers::empty());
1631        let result = layout.handle_key_event(key);
1632
1633        // Verify the ESC key was consumed
1634        assert_eq!(result, EventResult::Consumed);
1635
1636        // Verify we're still in Layout Mode
1637        assert!(layout.mode().is_layout());
1638
1639        // Verify no pane is selected
1640        assert_eq!(layout.mode().selected_pane(), None);
1641    }
1642
1643    #[test]
1644    fn test_mouse_click_selects_pane_in_layout_mode() {
1645        let mut layout = create_test_layout();
1646
1647        // Verify initial state: Layout Mode with first pane selected
1648        assert!(layout.mode().is_layout());
1649        let initial_selection = layout.mode().selected_pane();
1650        assert!(initial_selection.is_some());
1651
1652        // Get the second pane ID for comparison
1653        let _pane2 = layout
1654            .active_tab()
1655            .unwrap()
1656            .pane_container()
1657            .get_pane_by_index(1)
1658            .unwrap()
1659            .id();
1660
1661        // Render to calculate pane areas
1662        let backend = TestBackend::new(80, 24);
1663        let mut terminal = Terminal::new(backend).unwrap();
1664        terminal
1665            .draw(|frame| {
1666                layout.render(frame);
1667            })
1668            .unwrap();
1669
1670        // Simulate mouse click on second pane (below nav bar)
1671        let mouse = MouseEvent {
1672            kind: MouseEventKind::Down(MouseButton::Left),
1673            column: 10,
1674            row: 5,
1675            modifiers: KeyModifiers::empty(),
1676        };
1677
1678        layout.handle_mouse_event(mouse);
1679
1680        // Assert that:
1681        // 1. Mode is still Layout Mode (not Focus Mode)
1682        assert!(
1683            layout.mode().is_layout(),
1684            "Mode should still be Layout Mode after mouse click in Layout Mode"
1685        );
1686
1687        // 2. No pane is focused (verified by checking it's not Focus Mode)
1688        assert!(
1689            layout.mode().focused_pane().is_none(),
1690            "No pane should be focused in Layout Mode"
1691        );
1692
1693        // 3. A pane is selected
1694        let current_selection = layout.mode().selected_pane();
1695        assert!(
1696            current_selection.is_some(),
1697            "A pane should be selected after mouse click"
1698        );
1699    }
1700
1701    #[test]
1702    fn test_keyboard_navigation_then_enter_focuses() {
1703        let mut layout = create_test_layout();
1704
1705        // Step 1: Verify first pane is selected in Layout Mode
1706        assert!(layout.mode().is_layout());
1707        let first_pane = layout.mode().selected_pane();
1708        assert!(first_pane.is_some());
1709
1710        // Render to calculate pane areas (required for directional navigation)
1711        let backend = TestBackend::new(80, 24);
1712        let mut terminal = Terminal::new(backend).unwrap();
1713        terminal
1714            .draw(|frame| {
1715                layout.render(frame);
1716            })
1717            .unwrap();
1718
1719        // Step 2: Press 'l' key to select right pane
1720        let key = KeyEvent::new(KeyCode::Char('l'), KeyModifiers::empty());
1721        let result = layout.handle_key_event(key);
1722        assert_eq!(result, EventResult::Consumed);
1723
1724        // Step 3: Assert second pane is now selected (still in Layout Mode)
1725        let second_pane = layout.mode().selected_pane();
1726        assert!(second_pane.is_some());
1727        assert_ne!(
1728            first_pane, second_pane,
1729            "Second pane should be different from first"
1730        );
1731        assert!(layout.mode().is_layout(), "Should still be in Layout Mode");
1732
1733        // Step 4: Press Enter key
1734        let key = KeyEvent::new(KeyCode::Enter, KeyModifiers::empty());
1735        let result = layout.handle_key_event(key);
1736        assert_eq!(result, EventResult::Consumed);
1737
1738        // Step 5: Assert Mode is now Focus Mode and second pane is focused
1739        assert!(
1740            layout.mode().is_focus(),
1741            "Should be in Focus Mode after Enter"
1742        );
1743        assert_eq!(
1744            layout.mode().focused_pane(),
1745            second_pane,
1746            "Second pane should be focused"
1747        );
1748    }
1749
1750    #[test]
1751    fn test_ctrl_a_deselects_in_layout_mode() {
1752        let mut layout = create_test_layout();
1753
1754        // Verify we start in Layout Mode with a pane selected
1755        assert!(layout.mode().is_layout());
1756        assert!(layout.mode().selected_pane().is_some());
1757
1758        // Press Ctrl-A to deselect
1759        let key = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL);
1760        let result = layout.handle_key_event(key);
1761
1762        // Verify the Ctrl-A key was consumed
1763        assert_eq!(result, EventResult::Consumed);
1764
1765        // Verify we're still in Layout Mode
1766        assert!(layout.mode().is_layout());
1767
1768        // Verify no pane is selected
1769        assert_eq!(layout.mode().selected_pane(), None);
1770    }
1771
1772    #[test]
1773    fn test_click_different_pane_in_focus_mode_switches() {
1774        let mut layout = create_test_layout();
1775
1776        // Get pane IDs from the layout
1777        let pane1_id = layout
1778            .active_tab()
1779            .unwrap()
1780            .pane_container()
1781            .get_pane_by_index(0)
1782            .unwrap()
1783            .id();
1784
1785        let pane2_id = layout
1786            .active_tab()
1787            .unwrap()
1788            .pane_container()
1789            .get_pane_by_index(1)
1790            .unwrap()
1791            .id();
1792
1793        // Enter Focus Mode on first pane
1794        layout.enter_focus_mode(pane1_id);
1795        assert!(layout.mode().is_focus());
1796        assert_eq!(layout.mode().focused_pane(), Some(pane1_id));
1797
1798        // Render to calculate pane areas
1799        let backend = TestBackend::new(80, 24);
1800        let mut terminal = Terminal::new(backend).unwrap();
1801        terminal
1802            .draw(|frame| {
1803                layout.render(frame);
1804            })
1805            .unwrap();
1806
1807        // Simulate clicking on the second pane (somewhere in the pane area)
1808        let mouse = MouseEvent {
1809            kind: MouseEventKind::Down(MouseButton::Left),
1810            column: 50,
1811            row: 12,
1812            modifiers: KeyModifiers::empty(),
1813        };
1814
1815        let result = layout.handle_mouse_event(mouse);
1816        assert_eq!(result, EventResult::Consumed);
1817
1818        // Assert: Still in Focus Mode
1819        assert!(layout.mode().is_focus());
1820
1821        // Assert: Now focused on second pane
1822        assert_eq!(layout.mode().focused_pane(), Some(pane2_id));
1823    }
1824
1825    #[test]
1826    fn test_click_same_pane_in_focus_mode_maintains() {
1827        let mut layout = create_test_layout();
1828
1829        // Get first pane ID
1830        let pane1_id = layout
1831            .active_tab()
1832            .unwrap()
1833            .pane_container()
1834            .get_pane_by_index(0)
1835            .unwrap()
1836            .id();
1837
1838        // Enter Focus Mode on first pane
1839        layout.enter_focus_mode(pane1_id);
1840        assert!(layout.mode().is_focus());
1841        assert_eq!(layout.mode().focused_pane(), Some(pane1_id));
1842
1843        // Render to calculate pane areas
1844        let backend = TestBackend::new(80, 24);
1845        let mut terminal = Terminal::new(backend).unwrap();
1846        terminal
1847            .draw(|frame| {
1848                layout.render(frame);
1849            })
1850            .unwrap();
1851
1852        // Simulate clicking on the same first pane
1853        let mouse = MouseEvent {
1854            kind: MouseEventKind::Down(MouseButton::Left),
1855            column: 10,
1856            row: 8,
1857            modifiers: KeyModifiers::empty(),
1858        };
1859
1860        let result = layout.handle_mouse_event(mouse);
1861        assert_eq!(result, EventResult::Consumed);
1862
1863        // Assert: Still in Focus Mode
1864        assert!(layout.mode().is_focus());
1865
1866        // Assert: Still focused on first pane
1867        assert_eq!(layout.mode().focused_pane(), Some(pane1_id));
1868    }
1869
1870    #[test]
1871    fn test_hjkl_navigation_blocked_in_focus_mode() {
1872        let mut layout = create_test_layout();
1873
1874        let pane1_id = layout
1875            .active_tab()
1876            .unwrap()
1877            .pane_container()
1878            .get_pane_by_index(0)
1879            .unwrap()
1880            .id();
1881
1882        let pane2_id = layout
1883            .active_tab()
1884            .unwrap()
1885            .pane_container()
1886            .get_pane_by_index(1)
1887            .unwrap()
1888            .id();
1889
1890        // Verify we have 2 panes
1891        assert_ne!(pane1_id, pane2_id);
1892
1893        // Enter focus mode on first pane
1894        layout.enter_focus_mode(pane1_id);
1895        assert!(layout.mode().is_focus());
1896        assert_eq!(layout.mode().focused_pane(), Some(pane1_id));
1897
1898        // Press 'l' key (should navigate to second pane in Layout Mode, but not in Focus Mode)
1899        let key = KeyEvent::new(KeyCode::Char('l'), KeyModifiers::empty());
1900        let result = layout.handle_key_event(key);
1901
1902        // Key should be consumed (routed to pane, not processed by layout navigation)
1903        assert_eq!(result, EventResult::Consumed);
1904
1905        // IMPORTANT: Verify navigation is BLOCKED - still in Focus Mode with same pane
1906        assert!(layout.mode().is_focus(), "Should still be in Focus Mode");
1907        assert_eq!(
1908            layout.mode().focused_pane(),
1909            Some(pane1_id),
1910            "Should still be focused on first pane (navigation not allowed in Focus Mode)"
1911        );
1912    }
1913
1914    // === AUTO-FOCUS MODE TESTS ===
1915
1916    #[test]
1917    fn test_with_auto_focus_enables_auto_focus() {
1918        let layout = MasterLayout::new().with_auto_focus(true);
1919        assert!(layout.auto_focus());
1920    }
1921
1922    #[test]
1923    fn test_default_auto_focus_is_disabled() {
1924        let layout = MasterLayout::new();
1925        assert!(!layout.auto_focus());
1926    }
1927
1928    #[test]
1929    fn test_set_auto_focus() {
1930        let mut layout = MasterLayout::new();
1931        assert!(!layout.auto_focus());
1932
1933        layout.set_auto_focus(true);
1934        assert!(layout.auto_focus());
1935
1936        layout.set_auto_focus(false);
1937        assert!(!layout.auto_focus());
1938    }
1939
1940    #[test]
1941    fn test_auto_focus_routes_input_immediately() {
1942        let mut layout = create_test_layout().with_auto_focus(true);
1943
1944        // Should have a pane selected
1945        let selected = layout.mode().selected_pane();
1946        assert!(selected.is_some());
1947
1948        // Press 'j' key - should be routed to pane immediately
1949        let key = KeyEvent::new(KeyCode::Char('j'), KeyModifiers::empty());
1950        let result = layout.handle_key_event(key);
1951
1952        assert_eq!(result, EventResult::Consumed);
1953
1954        // Should still be in layout mode (not focus mode)
1955        assert!(layout.mode().is_layout());
1956    }
1957
1958    #[test]
1959    fn test_auto_focus_tab_switches_and_routes() {
1960        let mut layout = create_test_layout().with_auto_focus(true);
1961
1962        // Get initial selected pane
1963        let first_selected = layout.mode().selected_pane();
1964        assert!(first_selected.is_some());
1965
1966        // Press Tab - should switch to next pane and route the Tab key
1967        let key = KeyEvent::new(KeyCode::Tab, KeyModifiers::empty());
1968        let result = layout.handle_key_event(key);
1969
1970        assert_eq!(result, EventResult::Consumed);
1971
1972        // Selection should have changed
1973        let new_selected = layout.mode().selected_pane();
1974        assert_ne!(first_selected, new_selected);
1975    }
1976
1977    #[test]
1978    fn test_auto_focus_shift_tab_switches_and_routes() {
1979        let mut layout = create_test_layout().with_auto_focus(true);
1980
1981        // Get initial selected pane
1982        let first_selected = layout.mode().selected_pane();
1983        assert!(first_selected.is_some());
1984
1985        // Press Shift+Tab - should switch to previous pane and route the key
1986        let key = KeyEvent::new(KeyCode::BackTab, KeyModifiers::SHIFT);
1987        let result = layout.handle_key_event(key);
1988
1989        assert_eq!(result, EventResult::Consumed);
1990
1991        // Selection should have changed
1992        let new_selected = layout.mode().selected_pane();
1993        assert_ne!(first_selected, new_selected);
1994    }
1995
1996    #[test]
1997    fn test_auto_focus_no_enter_required() {
1998        let mut layout = create_test_layout().with_auto_focus(true);
1999
2000        // Press 'x' key - should be routed to selected pane immediately
2001        // No Enter required
2002        let key = KeyEvent::new(KeyCode::Char('x'), KeyModifiers::empty());
2003        let result = layout.handle_key_event(key);
2004
2005        assert_eq!(result, EventResult::Consumed);
2006
2007        // Should still be in layout mode
2008        assert!(layout.mode().is_layout());
2009    }
2010
2011    #[test]
2012    fn test_auto_focus_no_ctrl_a_required() {
2013        let mut layout = create_test_layout().with_auto_focus(true);
2014
2015        // Press Ctrl-A - should be routed to selected pane
2016        // No Ctrl-A required for anything else (unlike focus mode exit)
2017        let key = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL);
2018        let result = layout.handle_key_event(key);
2019
2020        assert_eq!(result, EventResult::Consumed);
2021
2022        // Should still be in layout mode
2023        assert!(layout.mode().is_layout());
2024    }
2025
2026    #[test]
2027    fn test_auto_focus_q_quits() {
2028        let mut layout = create_test_layout().with_auto_focus(true);
2029
2030        // Press 'q' - should still quit (global key)
2031        let key = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::empty());
2032        let result = layout.handle_key_event(key);
2033
2034        assert_eq!(result, EventResult::Quit);
2035    }
2036
2037    #[test]
2038    fn test_auto_focus_digit_keys_switch_tabs() {
2039        let mut layout = MasterLayout::new().with_auto_focus(true);
2040
2041        // Create two tabs
2042        let mut tab1 = Tab::new("Tab 1");
2043        tab1.add_pane(Pane::new(
2044            PaneId::new("p1"),
2045            Box::new(MockContent::new("P1")),
2046        ));
2047        layout.add_tab(tab1);
2048
2049        let mut tab2 = Tab::new("Tab 2");
2050        tab2.add_pane(Pane::new(
2051            PaneId::new("p2"),
2052            Box::new(MockContent::new("P2")),
2053        ));
2054        layout.add_tab(tab2);
2055
2056        // Start on first tab
2057        assert_eq!(layout.active_tab_index(), 0);
2058
2059        // Press '2' - should switch to second tab (global key still works)
2060        let key = KeyEvent::new(KeyCode::Char('2'), KeyModifiers::empty());
2061        let result = layout.handle_key_event(key);
2062
2063        assert_eq!(result, EventResult::Consumed);
2064        assert_eq!(layout.active_tab_index(), 1);
2065    }
2066
2067    #[test]
2068    fn test_default_behavior_unchanged() {
2069        // Regression test: ensure default behavior (auto_focus: false) is unchanged
2070        let mut layout = create_test_layout();
2071
2072        // Verify auto_focus is disabled
2073        assert!(!layout.auto_focus());
2074
2075        // Should be in layout mode
2076        assert!(layout.mode().is_layout());
2077
2078        // Press 'j' - should NOT route to pane in layout mode
2079        let key = KeyEvent::new(KeyCode::Char('j'), KeyModifiers::empty());
2080        let result = layout.handle_key_event(key);
2081
2082        // Should be consumed (navigation)
2083        assert_eq!(result, EventResult::Consumed);
2084
2085        // Should still be in layout mode
2086        assert!(layout.mode().is_layout());
2087
2088        // Press Enter - should enter focus mode
2089        let key = KeyEvent::new(KeyCode::Enter, KeyModifiers::empty());
2090        let result = layout.handle_key_event(key);
2091
2092        assert_eq!(result, EventResult::Consumed);
2093        assert!(layout.mode().is_focus());
2094    }
2095
2096    #[test]
2097    fn test_auto_focus_hjkl_routes_to_pane() {
2098        let mut layout = create_test_layout().with_auto_focus(true);
2099
2100        // Get initial selected pane
2101        let _first_selected = layout.mode().selected_pane();
2102
2103        // Press 'j' - should route to pane AND navigate
2104        let key = KeyEvent::new(KeyCode::Char('j'), KeyModifiers::empty());
2105        let result = layout.handle_key_event(key);
2106
2107        assert_eq!(result, EventResult::Consumed);
2108
2109        // In auto-focus mode, hjkl still navigates panes
2110        let new_selected = layout.mode().selected_pane();
2111        assert!(new_selected.is_some());
2112    }
2113
2114    #[test]
2115    fn test_auto_focus_with_no_selection() {
2116        let mut layout = MasterLayout::new().with_auto_focus(true);
2117        let tab = Tab::new("Empty Tab");
2118        layout.add_tab(tab);
2119
2120        // No panes, so no selection
2121        assert!(layout.mode().selected_pane().is_none());
2122
2123        // Press 'x' key - should not panic
2124        let key = KeyEvent::new(KeyCode::Char('x'), KeyModifiers::empty());
2125        let result = layout.handle_key_event(key);
2126
2127        // Should return NotHandled (no pane to route to)
2128        assert_eq!(result, EventResult::NotHandled);
2129    }
2130
2131    #[test]
2132    fn test_auto_focus_mouse_behavior() {
2133        // Verify mouse behavior still works in auto-focus mode
2134        let mut layout = create_test_layout().with_auto_focus(true);
2135
2136        // Render to calculate pane areas
2137        let backend = TestBackend::new(80, 24);
2138        let mut terminal = Terminal::new(backend).unwrap();
2139        terminal
2140            .draw(|frame| {
2141                layout.render(frame);
2142            })
2143            .unwrap();
2144
2145        // Click on pane area (below nav bar)
2146        let mouse = MouseEvent {
2147            kind: MouseEventKind::Down(MouseButton::Left),
2148            column: 10,
2149            row: 5,
2150            modifiers: KeyModifiers::empty(),
2151        };
2152
2153        // Should not panic
2154        let _result = layout.handle_mouse_event(mouse);
2155        // Result could be Consumed or NotHandled depending on what was clicked
2156    }
2157}