1use super::{actions::*, after::*, split_panel::*, which::*};
2
3use {
4 cursive::{direction::*, event::*, theme::*, view::*, *},
5 std::cmp::*,
6};
7
8const TYPE_NAME: &str = "SplitPanel";
9
10const TOP_LEFT: &str = "┌";
11const BOTTOM_LEFT: &str = "└";
12const TOP_RIGHT: &str = "┐";
13const BOTTOM_RIGHT: &str = "┘";
14const VERTICAL: &str = "│";
15const VERTICAL_THICK: &str = "┃";
16const HORIZONTAL: &str = "─";
17const HORIZONTAL_THICK: &str = "━";
18const TOP_HINGE: &str = "┰";
19const BOTTOM_HINGE: &str = "┸";
20const LEFT_HINGE: &str = "┝";
21const RIGHT_HINGE: &str = "┥";
22
23impl View for SplitPanel {
24 fn type_name(&self) -> &'static str {
25 TYPE_NAME
26 }
27
28 fn focus_view(&mut self, selector: &Selector) -> Result<EventResult, ViewNotFound> {
29 if self.front.view.focus_view(selector).is_ok() {
31 Ok(self.set_focus(Some(WhichPane::Front)))
32 } else if self.back.view.focus_view(selector).is_ok() {
33 Ok(self.set_focus(Some(WhichPane::Back)))
34 } else {
35 Err(ViewNotFound)
36 }
37 }
38
39 fn take_focus(&mut self, source: Direction) -> Result<EventResult, CannotFocus> {
40 match source.relative(self.orientation).unwrap_or(Relative::Front) {
41 Relative::Front => match self.front.view.take_focus(source) {
42 Ok(event_result) => Ok(event_result),
43 Err(_) => self.back.view.take_focus(source),
44 },
45
46 Relative::Back => match self.back.view.take_focus(source) {
47 Ok(event_result) => Ok(event_result),
48 Err(_) => self.front.view.take_focus(source),
49 },
50 }
51 }
52
53 fn needs_relayout(&self) -> bool {
54 self.needs_relayout || self.front.view.needs_relayout() || self.back.view.needs_relayout()
55 }
56
57 fn layout(&mut self, mut size: Vec2) {
58 self.size = Some(size);
59
60 if self.border {
62 size = size - (2, 2);
63 }
64 let extent = *size.get(self.orientation);
65
66 let divider = self.layout_divider(extent);
68
69 self.front.start = 0;
71 self.front.extent = divider;
72
73 self.back.start = divider + 1;
75 if self.back.start >= extent {
76 self.back.start = 0;
78 self.back.extent = 0;
79 } else {
80 self.back.extent = extent - self.back.start;
81 }
82
83 self.front.layout(size, self.orientation);
84 self.back.layout(size, self.orientation);
85
86 self.needs_relayout = false;
87 }
88
89 fn required_size(&mut self, constraint: Vec2) -> Vec2 {
90 let front = self.front.view.required_size(constraint);
91 let back = self.back.view.required_size(constraint);
92
93 let size = Vec2::from(match self.orientation {
94 Orientation::Horizontal => (front.x + back.x, max(front.y, back.y)),
95 Orientation::Vertical => (max(front.x, back.x), front.y + back.y),
96 });
97
98 if self.border {
99 size + match self.orientation {
101 Orientation::Horizontal => (3, 2),
102 Orientation::Vertical => (2, 3),
103 }
104 } else {
105 size + match self.orientation {
107 Orientation::Horizontal => (1, 0),
108 Orientation::Vertical => (0, 1),
109 }
110 }
111 }
112
113 fn important_area(&self, size: Vec2) -> Rect {
114 match self.focus {
115 Some(WhichPane::Front) => self.front.important_area(size, self.orientation, self.border),
116 Some(WhichPane::Back) => self.back.important_area(size, self.orientation, self.border),
117 None => Rect::from_size((0, 0), size),
118 }
119 }
120
121 fn on_event(&mut self, event: Event) -> EventResult {
122 match if self.movable_divider { self.actions.get(&event) } else { None } {
123 Some(action) => match action {
124 Action::MoveDividerToFront => self.move_divider(-1),
125 Action::MoveDividerToBack => self.move_divider(1),
126 },
127
128 None => match event {
129 Event::Mouse { offset, position, event } => {
130 if self.moving_divider {
131 if matches!(event, MouseEvent::Release(_)) {
134 self.moving_divider = false;
136 } else if let Some(position) = position.checked_sub(offset) {
137 let mut divider = *position.get(self.orientation);
138
139 if self.border && divider != 0 {
140 divider -= 1;
141 }
142
143 self.set_divider(divider);
144 }
145
146 EventResult::consumed()
147 } else if event.button() == Some(MouseButton::Left)
148 && self.is_start_moving_divider(offset, position)
149 {
150 self.moving_divider = true;
153 EventResult::consumed()
154 } else if let Some((offset, position)) =
155 self.front.mouse_event(offset, position, self.orientation, self.border, self.size)
156 {
157 let prior = if event.grabs_focus() {
160 self.set_focus(Some(WhichPane::Front))
161 } else {
162 EventResult::Ignored
163 };
164
165 self.front.view.on_event(Event::Mouse { offset, position, event }).after(prior)
166 } else if let Some((offset, position)) =
167 self.back.mouse_event(offset, position, self.orientation, self.border, self.size)
168 {
169 let prior = if event.grabs_focus() {
172 self.set_focus(Some(WhichPane::Back))
173 } else {
174 EventResult::Ignored
175 };
176
177 self.back.view.on_event(Event::Mouse { offset, position, event }).after(prior)
178 } else {
179 EventResult::Ignored
180 }
181 }
182
183 event => match self.focus {
184 Some(WhichPane::Front) => self.front.view.on_event(event),
185 Some(WhichPane::Back) => self.back.view.on_event(event),
186 None => {
187 let prior = self.set_focus(Some(WhichPane::Front));
189 self.front.view.on_event(event).after(prior)
190 }
191 },
192 },
193 }
194 }
195
196 fn call_on_any(&mut self, selector: &Selector, callback: AnyCb) {
197 self.front.view.call_on_any(selector, callback);
198 self.back.view.call_on_any(selector, callback);
199 }
200
201 fn draw(&self, printer: &Printer) {
202 let mut _shrinked = None;
203 let printer = if self.draw_lines(printer) {
204 _shrinked = Some(printer.shrinked_centered((2, 2)));
205 &_shrinked.expect("shrinked")
206 } else {
207 printer
208 };
209
210 self.front.draw(printer, self.orientation, self.is_focused(WhichPane::Front));
211 self.back.draw(printer, self.orientation, self.is_focused(WhichPane::Back));
212 }
213}
214
215impl SplitPanel {
216 fn is_focused(&self, which: WhichPane) -> bool {
218 self.focus.map(|focus| focus == which).unwrap_or_default()
219 }
220
221 fn set_focus(&mut self, focus: Option<WhichPane>) -> EventResult {
223 let old_focus = self.focus;
224 self.focus = focus;
225 if self.focus != old_focus
226 && let Some(old_focus) = old_focus
227 {
228 return match old_focus {
229 WhichPane::Front => self.front.view.on_event(Event::FocusLost),
230 WhichPane::Back => self.back.view.on_event(Event::FocusLost),
231 };
232 }
233
234 EventResult::consumed()
235 }
236
237 fn is_start_moving_divider(&self, offset: Vec2, position: Vec2) -> bool {
239 if !self.movable_divider || self.moving_divider {
240 return false;
241 }
242
243 if let Some(mut divider) = self.divider
244 && let Some(size) = self.size
245 && size.x != 0
246 && size.y != 0
247 && let Some(position) = position.checked_sub(offset)
248 {
249 if self.border {
250 divider += 1;
251 let corner = size - (1, 1);
252 match self.orientation {
253 Orientation::Horizontal => position.y > 0 && position.y < corner.y && position.x == divider,
254 Orientation::Vertical => position.x > 0 && position.x < corner.x && position.y == divider,
255 }
256 } else {
257 match self.orientation {
258 Orientation::Horizontal => position.x == divider,
259 Orientation::Vertical => position.y == divider,
260 }
261 }
262 } else {
263 false
264 }
265 }
266
267 fn move_divider(&mut self, delta: isize) -> EventResult {
269 if !self.movable_divider {
270 return EventResult::Ignored;
271 }
272
273 match self.divider {
274 Some(divider) => {
275 if delta > 0 {
276 self.set_divider(divider + delta as usize);
277 } else if let Some(divider) = divider.checked_add_signed(delta) {
278 self.set_divider(divider);
279 }
280 }
281
282 None => self.set_divider(0),
283 }
284
285 EventResult::consumed()
286 }
287
288 fn layout_divider(&mut self, extent: usize) -> usize {
290 match self.divider {
291 Some(divider) => {
292 if divider >= extent {
293 if extent == 0 {
294 self.set_divider(0);
296 } else {
297 self.set_divider(extent - 1);
299 }
300 }
301 }
302
303 None => self.set_divider(extent / 2),
305 }
306
307 self.divider.expect("divider")
308 }
309
310 fn draw_lines(&self, printer: &Printer) -> bool {
314 let Some(mut divider) = self.divider else {
315 return false;
317 };
318
319 if printer.size.x == 0 || printer.size.y == 0 {
320 return false;
321 }
322
323 let corner = printer.size - (1, 1);
324
325 if self.border {
326 divider += 1;
327
328 printer.print((0, 0), TOP_LEFT);
330 printer.print((corner.x, 0), TOP_RIGHT);
331 printer.print((0, corner.y), BOTTOM_LEFT);
332 printer.print(corner, BOTTOM_RIGHT);
333
334 let mut _divider_printer = None;
335 let divider_printer = if self.moving_divider {
336 let mut divider_printer = printer.clone();
337 divider_printer.set_effect(Effect::Reverse);
338 _divider_printer = Some(divider_printer);
339 &_divider_printer.expect("divider_printer")
340 } else {
341 printer
342 };
343
344 match self.orientation {
345 Orientation::Horizontal => {
346 printer.print((divider, 0), TOP_HINGE);
348 printer.print((divider, corner.y), BOTTOM_HINGE);
349
350 if let Some(length) = divider.checked_sub(1) {
351 printer.print_hline((1, 0), length, HORIZONTAL);
353 printer.print_hline((1, corner.y), length, HORIZONTAL);
355 }
356
357 if let Some(length) = corner.x.checked_sub(divider + 1) {
358 printer.print_hline((divider + 1, 0), length, HORIZONTAL);
360 printer.print_hline((divider + 1, corner.y), length, HORIZONTAL);
362 }
363
364 if let Some(length) = corner.y.checked_sub(1) {
365 printer.print_vline((0, 1), length, VERTICAL);
367 divider_printer.print_vline((divider, 1), length, VERTICAL_THICK);
369 printer.print_vline((corner.x, 1), length, VERTICAL);
371 }
372 }
373
374 Orientation::Vertical => {
375 printer.print((0, divider), LEFT_HINGE);
377 printer.print((corner.x, divider), RIGHT_HINGE);
378
379 if let Some(length) = divider.checked_sub(1) {
380 printer.print_vline((0, 1), length, VERTICAL);
382 printer.print_vline((corner.x, 1), length, VERTICAL);
384 }
385
386 if let Some(length) = corner.y.checked_sub(divider + 1) {
387 printer.print_vline((0, divider + 1), length, VERTICAL);
389 printer.print_vline((corner.x, divider + 1), length, VERTICAL);
391 }
392
393 if let Some(length) = corner.x.checked_sub(1) {
394 printer.print_hline((1, 0), length, HORIZONTAL);
396 divider_printer.print_hline((1, divider), length, HORIZONTAL_THICK);
398 printer.print_hline((1, corner.y), length, HORIZONTAL);
400 }
401 }
402 }
403
404 true
405 } else {
406 match self.orientation {
407 Orientation::Horizontal => printer.print_vline((divider, 0), corner.y, VERTICAL),
408 Orientation::Vertical => printer.print_vline((divider, 0), corner.y, VERTICAL),
409 }
410
411 false
412 }
413 }
414}