reovim_plugin_microscope/microscope/
layout.rs1#[derive(Debug, Clone, Copy)]
27pub struct LayoutConfig {
28 pub height_ratio: f32,
30 pub results_width_ratio: f32,
32 pub min_height: u16,
34 pub min_width: u16,
36 pub show_preview: bool,
38 pub padding: u16,
40}
41
42impl Default for LayoutConfig {
43 fn default() -> Self {
44 Self {
45 height_ratio: 0.4, results_width_ratio: 0.4, min_height: 10,
48 min_width: 40,
49 show_preview: true,
50 padding: 0,
51 }
52 }
53}
54
55#[derive(Debug, Clone, Copy, Default)]
57pub struct LayoutBounds {
58 pub results: PanelBounds,
60 pub preview: Option<PanelBounds>,
62 pub status: PanelBounds,
64 pub total: PanelBounds,
66}
67
68#[derive(Debug, Clone, Copy, Default)]
70pub struct PanelBounds {
71 pub x: u16,
72 pub y: u16,
73 pub width: u16,
74 pub height: u16,
75}
76
77impl PanelBounds {
78 #[must_use]
80 pub const fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
81 Self {
82 x,
83 y,
84 width,
85 height,
86 }
87 }
88
89 #[must_use]
91 pub const fn inner(&self) -> Self {
92 Self {
93 x: self.x + 1,
94 y: self.y + 1,
95 width: self.width.saturating_sub(2),
96 height: self.height.saturating_sub(2),
97 }
98 }
99
100 #[must_use]
102 pub const fn contains(&self, x: u16, y: u16) -> bool {
103 x >= self.x && x < self.x + self.width && y >= self.y && y < self.y + self.height
104 }
105}
106
107#[must_use]
109#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
110pub fn calculate_layout(
111 screen_width: u16,
112 screen_height: u16,
113 config: &LayoutConfig,
114) -> LayoutBounds {
115 let total_height = ((f32::from(screen_height) * config.height_ratio) as u16)
117 .max(config.min_height)
118 .min(screen_height.saturating_sub(config.padding * 2));
119
120 let total_width = screen_width
121 .saturating_sub(config.padding * 2)
122 .max(config.min_width);
123
124 let x = config.padding;
126 let y = screen_height.saturating_sub(total_height + config.padding);
127
128 let total = PanelBounds::new(x, y, total_width, total_height);
129
130 let status_height = 1;
132 let content_height = total_height.saturating_sub(status_height);
133
134 let status = PanelBounds::new(x, y + content_height, total_width, status_height);
135
136 let (results, preview) = if config.show_preview && total_width > 60 {
138 let results_width = ((f32::from(total_width) * config.results_width_ratio) as u16).max(20);
139 let preview_width = total_width.saturating_sub(results_width);
140
141 let results = PanelBounds::new(x, y, results_width, content_height);
142 let preview = PanelBounds::new(x + results_width, y, preview_width, content_height);
143
144 (results, Some(preview))
145 } else {
146 let results = PanelBounds::new(x, y, total_width, content_height);
148 (results, None)
149 };
150
151 LayoutBounds {
152 results,
153 preview,
154 status,
155 total,
156 }
157}
158
159#[must_use]
161pub const fn visible_item_count(panel: &PanelBounds) -> usize {
162 panel.height.saturating_sub(4) as usize
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn test_default_config() {
172 let config = LayoutConfig::default();
173 assert!((config.height_ratio - 0.4).abs() < f32::EPSILON);
174 assert!((config.results_width_ratio - 0.4).abs() < f32::EPSILON);
175 assert!(config.show_preview);
176 }
177
178 #[test]
179 fn test_calculate_layout() {
180 let config = LayoutConfig::default();
181 let bounds = calculate_layout(100, 50, &config);
182
183 assert_eq!(bounds.total.y + bounds.total.height, 50);
185
186 assert!(bounds.preview.is_some());
188
189 assert_eq!(bounds.results.width, 40);
191 }
192
193 #[test]
194 fn test_no_preview_narrow_screen() {
195 let config = LayoutConfig::default();
196 let bounds = calculate_layout(50, 30, &config);
197
198 assert!(bounds.preview.is_none());
200 assert_eq!(bounds.results.width, 50);
201 }
202
203 #[test]
204 fn test_panel_inner() {
205 let panel = PanelBounds::new(10, 20, 30, 15);
206 let inner = panel.inner();
207
208 assert_eq!(inner.x, 11);
209 assert_eq!(inner.y, 21);
210 assert_eq!(inner.width, 28);
211 assert_eq!(inner.height, 13);
212 }
213
214 #[test]
215 fn test_visible_item_count() {
216 let panel = PanelBounds::new(0, 0, 40, 20);
217 assert_eq!(visible_item_count(&panel), 16); }
219}