1use crate::app::LayoutState;
2use ratatui::layout::{Constraint, Direction, Layout, Rect};
3
4pub struct UiAreas {
5 pub search_results: Rect,
6 pub preview: Option<Rect>,
7 pub search_bar: Rect,
8 pub show_search: bool,
9}
10
11pub fn split_main_layout(area: Rect, show_preview: bool, layout: &LayoutState) -> UiAreas {
12 if show_preview && layout.preview_fullscreen {
13 return UiAreas {
14 search_bar: Rect::default(),
15 search_results: Rect::default(),
16 preview: Some(area),
17 show_search: false,
18 };
19 }
20
21 if show_preview {
22 let results_pct = 100 - layout.preview_percent;
23 let preview_pct = layout.preview_percent;
24
25 let (left_pct, right_pct) = if layout.panes_swapped {
26 (preview_pct, results_pct)
27 } else {
28 (results_pct, preview_pct)
29 };
30
31 let columns = Layout::default()
32 .direction(Direction::Horizontal)
33 .constraints([
34 Constraint::Percentage(left_pct),
35 Constraint::Percentage(right_pct),
36 ])
37 .split(area);
38
39 let (results_col, preview_col) = if layout.panes_swapped {
40 (columns[1], columns[0])
41 } else {
42 (columns[0], columns[1])
43 };
44
45 let (search_bar, search_results) =
46 split_results_column(results_col, layout.search_bar_at_bottom);
47
48 UiAreas {
49 search_bar,
50 search_results,
51 preview: Some(preview_col),
52 show_search: true,
53 }
54 } else {
55 let (search_bar, search_results) = split_results_column(area, layout.search_bar_at_bottom);
56
57 UiAreas {
58 search_bar,
59 search_results,
60 preview: None,
61 show_search: true,
62 }
63 }
64}
65
66fn split_results_column(col: Rect, bar_at_bottom: bool) -> (Rect, Rect) {
67 let parts = if bar_at_bottom {
68 Layout::default()
69 .direction(Direction::Vertical)
70 .constraints([Constraint::Min(0), Constraint::Length(3)])
71 .split(col)
72 } else {
73 Layout::default()
74 .direction(Direction::Vertical)
75 .constraints([Constraint::Length(3), Constraint::Min(0)])
76 .split(col)
77 };
78
79 if bar_at_bottom {
80 (parts[1], parts[0])
81 } else {
82 (parts[0], parts[1])
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn fullscreen_preview_hides_search_regions() {
92 let layout = LayoutState {
93 preview_fullscreen: true,
94 ..LayoutState::default()
95 };
96 let area = Rect::new(0, 0, 120, 40);
97
98 let areas = split_main_layout(area, true, &layout);
99
100 assert!(!areas.show_search);
101 assert_eq!(areas.preview, Some(area));
102 }
103
104 #[test]
105 fn hidden_preview_uses_single_search_column() {
106 let area = Rect::new(0, 0, 120, 40);
107 let areas = split_main_layout(area, false, &LayoutState::default());
108
109 assert!(areas.show_search);
110 assert!(areas.preview.is_none());
111 assert_eq!(areas.search_bar.height, 3);
112 assert_eq!(areas.search_results.height, 37);
113 }
114
115 #[test]
116 fn swapped_panes_move_preview_to_left_column() {
117 let layout = LayoutState {
118 panes_swapped: true,
119 preview_percent: 40,
120 ..LayoutState::default()
121 };
122 let area = Rect::new(0, 0, 100, 30);
123
124 let areas = split_main_layout(area, true, &layout);
125 let preview = areas.preview.expect("preview area");
126
127 assert_eq!(preview.x, 0);
128 assert_eq!(preview.width, 40);
129 assert_eq!(areas.search_results.x, 40);
130 }
131
132 #[test]
133 fn search_bar_can_move_to_bottom() {
134 let layout = LayoutState {
135 search_bar_at_bottom: true,
136 ..LayoutState::default()
137 };
138 let area = Rect::new(0, 0, 80, 20);
139
140 let areas = split_main_layout(area, false, &layout);
141
142 assert_eq!(areas.search_bar.y, 17);
143 assert_eq!(areas.search_bar.height, 3);
144 assert_eq!(areas.search_results.y, 0);
145 }
146}