1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum EventResult {
12 Consumed,
14 NotHandled,
16 Quit,
18}
19
20pub 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 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 pub fn with_keybindings(mut self, keybindings: MasterLayoutKeyBindings) -> Self {
111 self.keybindings = keybindings;
112 self
113 }
114
115 pub fn keybindings(&self) -> &MasterLayoutKeyBindings {
117 &self.keybindings
118 }
119
120 pub fn set_keybindings(&mut self, keybindings: MasterLayoutKeyBindings) {
122 self.keybindings = keybindings;
123 }
124
125 pub fn with_auto_focus(mut self, enabled: bool) -> Self {
139 self.auto_focus = enabled;
140 self
141 }
142
143 pub fn auto_focus(&self) -> bool {
145 self.auto_focus
146 }
147
148 pub fn set_auto_focus(&mut self, enabled: bool) {
150 self.auto_focus = enabled;
151 }
152
153 pub fn set_nav_bar_offset(&mut self, offset: u16) {
155 self.nav_bar_offset = offset;
156 }
157
158 pub fn add_tab(&mut self, tab: Tab) {
160 self.tabs.push(tab);
161
162 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 self.tabs.len() == 1 {
173 self.select_first_pane_in_active_tab();
174 }
175 }
176
177 pub fn tab_count(&self) -> usize {
179 self.tabs.len()
180 }
181
182 pub fn active_tab_index(&self) -> usize {
184 self.active_tab_index
185 }
186
187 pub fn set_active_tab(&mut self, index: usize) {
189 if index < self.tabs.len() {
190 self.active_tab_index = index;
191 for (i, item) in self.nav_bar.items.iter_mut().enumerate() {
193 item.selected = i == index;
194 }
195
196 self.select_first_pane_in_active_tab();
198 }
199 }
200
201 pub fn active_tab(&self) -> Option<&Tab> {
203 self.tabs.get(self.active_tab_index)
204 }
205
206 pub fn active_tab_mut(&mut self) -> Option<&mut Tab> {
208 self.tabs.get_mut(self.active_tab_index)
209 }
210
211 pub fn mode(&self) -> &InteractionMode {
213 &self.mode
214 }
215
216 pub fn enter_layout_mode(&mut self) {
218 if let Some(focused_id) = self.mode.focused_pane() {
219 self.mode = InteractionMode::layout_with_selection(focused_id);
221 } else {
222 if !self.mode.is_layout() {
224 self.mode = InteractionMode::layout();
225 }
226 }
227 }
228
229 pub fn enter_focus_mode(&mut self, pane_id: PaneId) {
231 self.mode = InteractionMode::focus(pane_id);
232 }
233
234 pub fn exit_focus_mode(&mut self) {
236 self.mode.exit_focus();
237 }
238
239 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 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 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 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 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 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 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 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 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 fn handle_key_event(&mut self, key: KeyEvent) -> EventResult {
360 match self.mode.clone() {
362 InteractionMode::Layout { selected_pane } => {
363 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 if self.keybindings.is_quit(&key) {
372 return EventResult::Quit;
373 }
374
375 if let Some(pane_id) = selected_pane {
377 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 if pane.has_selection() && pane.handle_key(key) {
383 return EventResult::Consumed;
384 }
385 }
386 }
387 }
388
389 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 if pane.has_selection() && pane.handle_key(key) {
395 return EventResult::Consumed;
396 }
397 }
398 }
399 }
401 }
402
403 self.handle_layout_mode_key(key)
405 }
406 InteractionMode::Focus { focused_pane } => {
407 if self.keybindings.is_exit_focus_mode(&key) {
410 self.exit_focus_mode();
411 return EventResult::Consumed;
412 }
413
414 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 fn handle_layout_mode_key(&mut self, key: KeyEvent) -> EventResult {
430 if self.keybindings.is_clear_selection(&key) {
432 self.mode = InteractionMode::Layout {
433 selected_pane: None,
434 };
435 return EventResult::Consumed;
436 }
437
438 if self.keybindings.is_deselect_pane(&key) {
440 self.mode = InteractionMode::Layout {
441 selected_pane: None,
442 };
443 return EventResult::Consumed;
444 }
445
446 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 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 if self.keybindings.is_focus_pane(&key) {
475 self.focus_selected();
476 return EventResult::Consumed;
477 }
478
479 EventResult::NotHandled
480 }
481
482 fn handle_auto_focus_layout_key(&mut self, key: KeyEvent, pane_id: PaneId) -> EventResult {
484 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 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 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 self.route_key_to_pane(key, pane_id)
549 }
550
551 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 fn handle_mouse_event(&mut self, mouse: MouseEvent) -> EventResult {
565 if mouse.kind == MouseEventKind::Down(MouseButton::Left) {
567 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 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 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 if container.is_dragging() {
594 container.update_drag(mouse.column, mouse.row);
595 return EventResult::Consumed;
596 }
597 }
598 MouseEventKind::Up(MouseButton::Left) => {
599 if container.is_dragging() {
601 container.stop_drag();
602 return EventResult::Consumed;
603 }
604 }
605 MouseEventKind::Moved => {
606 container.update_hover(mouse.column, mouse.row);
608 }
609 _ => {}
610 }
611 }
612 }
613
614 match mouse.kind {
616 MouseEventKind::Down(MouseButton::Left) if mouse.row >= 3 => {
617 let current_pane = match &self.mode {
619 InteractionMode::Layout { selected_pane } => *selected_pane,
620 InteractionMode::Focus { focused_pane } => Some(*focused_pane),
621 };
622
623 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 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 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 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 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 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 if pane.contains_point(mouse.column, mouse.row) {
698 let local_mouse = pane.translate_mouse(mouse);
700 if pane.handle_mouse(local_mouse) {
701 return EventResult::Consumed;
702 }
703 } else {
704 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 pub fn render(&mut self, frame: &mut ratatui::Frame) {
723 self.global_area = frame.area();
724
725 let chunks = Layout::default()
727 .direction(Direction::Vertical)
728 .constraints([
729 Constraint::Length(3), Constraint::Min(5), ])
732 .split(self.global_area);
733
734 let nav_area = chunks[0];
735 let tab_area = chunks[1];
736
737 self.nav_bar
739 .render_with_offset(frame, nav_area, self.nav_bar_offset);
740
741 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 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 }
804 }
805
806 fn create_test_layout() -> MasterLayout {
807 let mut layout = MasterLayout::new();
808
809 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 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 assert!(layout.mode().is_layout());
884
885 let pane_id = layout
887 .active_tab()
888 .unwrap()
889 .pane_container()
890 .get_pane_by_index(0)
891 .unwrap()
892 .id();
893
894 layout.enter_focus_mode(pane_id);
896 assert!(layout.mode().is_focus());
897 assert_eq!(layout.mode().focused_pane(), Some(pane_id));
898
899 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 let selected = layout.mode().selected_pane();
911 assert!(selected.is_some());
912
913 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 layout.enter_focus_mode(pane_id);
936 assert!(layout.mode().is_focus());
937
938 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 let mut layout = create_test_layout();
961
962 let first_selected = layout.mode().selected_pane();
963 assert!(first_selected.is_some());
964
965 let key = KeyEvent::new(KeyCode::Tab, KeyModifiers::empty());
967 let result = layout.handle_key_event(key);
968
969 assert_eq!(result, EventResult::NotHandled);
971
972 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 let mut layout = create_test_layout();
981
982 let first_selected = layout.mode().selected_pane();
983
984 let key = KeyEvent::new(KeyCode::BackTab, KeyModifiers::SHIFT);
986 let result = layout.handle_key_event(key);
987
988 assert_eq!(result, EventResult::NotHandled);
990
991 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 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 assert!(layout.mode().is_layout());
1024
1025 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 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 assert!(layout.mode().is_layout());
1044 assert!(layout.mode().selected_pane().is_some());
1045
1046 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 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 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 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 layout.enter_focus_mode(pane_id);
1081
1082 let initial_mode = layout.mode().clone();
1083
1084 let key = KeyEvent::new(KeyCode::Char('h'), KeyModifiers::empty());
1086 layout.handle_key_event(key);
1087
1088 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 assert_eq!(layout.active_tab_index(), 0);
1101
1102 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 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 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 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 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 }
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 layout.enter_focus_mode(pane_id);
1171
1172 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 }
1179
1180 #[test]
1181 fn test_tab_key_routed_to_pane_in_focus_mode() {
1182 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 layout.enter_focus_mode(pane_id);
1195 assert!(layout.mode().is_focus());
1196
1197 let key = KeyEvent::new(KeyCode::Tab, KeyModifiers::empty());
1199 let result = layout.handle_key_event(key);
1200
1201 assert_eq!(result, EventResult::Consumed);
1204
1205 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 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 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 layout.enter_focus_mode(pane_id);
1242 assert!(layout.mode().is_focus());
1243
1244 let key = KeyEvent::new(KeyCode::Char('2'), KeyModifiers::empty());
1246 let result = layout.handle_key_event(key);
1247
1248 assert_eq!(result, EventResult::Consumed);
1250
1251 assert_eq!(layout.active_tab_index(), 0);
1253
1254 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 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 layout.enter_focus_mode(pane_id);
1274 assert!(layout.mode().is_focus());
1275
1276 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 assert!(
1289 layout.mode().is_focus(),
1290 "Key {:?} should not exit focus mode",
1291 key
1292 );
1293 }
1294
1295 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 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 layout.enter_focus_mode(pane_id);
1318 assert!(layout.mode().is_focus());
1319
1320 let test_keys = vec![
1322 KeyEvent::new(KeyCode::Char('w'), KeyModifiers::CONTROL), KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL), KeyEvent::new(KeyCode::Char('d'), KeyModifiers::CONTROL), KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()), KeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL), ];
1328
1329 for key in test_keys {
1330 let result = layout.handle_key_event(key);
1331
1332 assert_eq!(
1334 result,
1335 EventResult::Consumed,
1336 "Key {:?} should be passed to pane",
1337 key
1338 );
1339
1340 assert!(
1342 layout.mode().is_focus(),
1343 "Key {:?} should not exit focus mode",
1344 key
1345 );
1346
1347 let _pane = layout
1349 .active_tab()
1350 .unwrap()
1351 .pane_container()
1352 .get_pane(pane_id)
1353 .unwrap();
1354
1355 }
1359 }
1360
1361 #[test]
1362 fn test_q_quits_only_in_layout_mode() {
1363 let mut layout = create_test_layout();
1365
1366 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 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 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 let mut layout = create_test_layout();
1406
1407 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 assert!(layout.mode().is_focus(), "Should be in Focus Mode");
1419
1420 let q_key = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::empty());
1422 let result = layout.handle_key_event(q_key);
1423
1424 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 assert!(
1438 layout.mode().is_focus(),
1439 "Should still be in Focus Mode after pressing 'q'"
1440 );
1441
1442 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 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 let mut layout = create_test_layout();
1466
1467 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 let uppercase_q = KeyEvent::new(KeyCode::Char('Q'), KeyModifiers::empty());
1480 let result = layout.handle_key_event(uppercase_q);
1481
1482 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 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 let mut layout = create_test_layout();
1505 assert!(layout.mode().is_layout());
1506
1507 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 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 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 layout.set_active_tab(1);
1576
1577 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 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 }
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 layout.focus_selected();
1617 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 assert!(layout.mode().is_layout());
1627 assert!(layout.mode().selected_pane().is_some());
1628
1629 let key = KeyEvent::new(KeyCode::Esc, KeyModifiers::empty());
1631 let result = layout.handle_key_event(key);
1632
1633 assert_eq!(result, EventResult::Consumed);
1635
1636 assert!(layout.mode().is_layout());
1638
1639 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 assert!(layout.mode().is_layout());
1649 let initial_selection = layout.mode().selected_pane();
1650 assert!(initial_selection.is_some());
1651
1652 let _pane2 = layout
1654 .active_tab()
1655 .unwrap()
1656 .pane_container()
1657 .get_pane_by_index(1)
1658 .unwrap()
1659 .id();
1660
1661 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 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!(
1683 layout.mode().is_layout(),
1684 "Mode should still be Layout Mode after mouse click in Layout Mode"
1685 );
1686
1687 assert!(
1689 layout.mode().focused_pane().is_none(),
1690 "No pane should be focused in Layout Mode"
1691 );
1692
1693 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 assert!(layout.mode().is_layout());
1707 let first_pane = layout.mode().selected_pane();
1708 assert!(first_pane.is_some());
1709
1710 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 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 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 let key = KeyEvent::new(KeyCode::Enter, KeyModifiers::empty());
1735 let result = layout.handle_key_event(key);
1736 assert_eq!(result, EventResult::Consumed);
1737
1738 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 assert!(layout.mode().is_layout());
1756 assert!(layout.mode().selected_pane().is_some());
1757
1758 let key = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL);
1760 let result = layout.handle_key_event(key);
1761
1762 assert_eq!(result, EventResult::Consumed);
1764
1765 assert!(layout.mode().is_layout());
1767
1768 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 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 layout.enter_focus_mode(pane1_id);
1795 assert!(layout.mode().is_focus());
1796 assert_eq!(layout.mode().focused_pane(), Some(pane1_id));
1797
1798 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 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!(layout.mode().is_focus());
1820
1821 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 let pane1_id = layout
1831 .active_tab()
1832 .unwrap()
1833 .pane_container()
1834 .get_pane_by_index(0)
1835 .unwrap()
1836 .id();
1837
1838 layout.enter_focus_mode(pane1_id);
1840 assert!(layout.mode().is_focus());
1841 assert_eq!(layout.mode().focused_pane(), Some(pane1_id));
1842
1843 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 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!(layout.mode().is_focus());
1865
1866 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 assert_ne!(pane1_id, pane2_id);
1892
1893 layout.enter_focus_mode(pane1_id);
1895 assert!(layout.mode().is_focus());
1896 assert_eq!(layout.mode().focused_pane(), Some(pane1_id));
1897
1898 let key = KeyEvent::new(KeyCode::Char('l'), KeyModifiers::empty());
1900 let result = layout.handle_key_event(key);
1901
1902 assert_eq!(result, EventResult::Consumed);
1904
1905 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 #[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 let selected = layout.mode().selected_pane();
1946 assert!(selected.is_some());
1947
1948 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 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 let first_selected = layout.mode().selected_pane();
1964 assert!(first_selected.is_some());
1965
1966 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 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 let first_selected = layout.mode().selected_pane();
1983 assert!(first_selected.is_some());
1984
1985 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 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 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 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 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 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 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 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 assert_eq!(layout.active_tab_index(), 0);
2058
2059 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 let mut layout = create_test_layout();
2071
2072 assert!(!layout.auto_focus());
2074
2075 assert!(layout.mode().is_layout());
2077
2078 let key = KeyEvent::new(KeyCode::Char('j'), KeyModifiers::empty());
2080 let result = layout.handle_key_event(key);
2081
2082 assert_eq!(result, EventResult::Consumed);
2084
2085 assert!(layout.mode().is_layout());
2087
2088 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 let _first_selected = layout.mode().selected_pane();
2102
2103 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 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 assert!(layout.mode().selected_pane().is_none());
2122
2123 let key = KeyEvent::new(KeyCode::Char('x'), KeyModifiers::empty());
2125 let result = layout.handle_key_event(key);
2126
2127 assert_eq!(result, EventResult::NotHandled);
2129 }
2130
2131 #[test]
2132 fn test_auto_focus_mouse_behavior() {
2133 let mut layout = create_test_layout().with_auto_focus(true);
2135
2136 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 let mouse = MouseEvent {
2147 kind: MouseEventKind::Down(MouseButton::Left),
2148 column: 10,
2149 row: 5,
2150 modifiers: KeyModifiers::empty(),
2151 };
2152
2153 let _result = layout.handle_mouse_event(mouse);
2155 }
2157}