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.x = size.x.checked_sub(2).unwrap_or_default();
63 size.y = size.y.checked_sub(2).unwrap_or_default();
64 }
65 let extent = *size.get(self.orientation);
66
67 let divider = self.layout_divider(extent);
69
70 self.front.start = 0;
72 self.front.extent = divider;
73
74 self.back.start = divider + 1;
76 if self.back.start >= extent {
77 self.back.start = 0;
79 self.back.extent = 0;
80 } else {
81 self.back.extent = extent - self.back.start;
82 }
83
84 self.front.layout(size, self.orientation);
85 self.back.layout(size, self.orientation);
86
87 self.needs_relayout = false;
88 }
89
90 fn required_size(&mut self, constraint: Vec2) -> Vec2 {
91 let front = self.front.view.required_size(constraint);
92 let back = self.back.view.required_size(constraint);
93
94 let size = Vec2::from(match self.orientation {
95 Orientation::Horizontal => (front.x + back.x, max(front.y, back.y)),
96 Orientation::Vertical => (max(front.x, back.x), front.y + back.y),
97 });
98
99 if self.border {
100 size + match self.orientation {
102 Orientation::Horizontal => (3, 2),
103 Orientation::Vertical => (2, 3),
104 }
105 } else {
106 size + match self.orientation {
108 Orientation::Horizontal => (1, 0),
109 Orientation::Vertical => (0, 1),
110 }
111 }
112 }
113
114 fn important_area(&self, size: Vec2) -> Rect {
115 match self.focus {
116 Some(WhichPane::Front) => self.front.important_area(size, self.orientation, self.border),
117 Some(WhichPane::Back) => self.back.important_area(size, self.orientation, self.border),
118 None => Rect::from_size((0, 0), size),
119 }
120 }
121
122 fn on_event(&mut self, event: Event) -> EventResult {
123 match if self.movable_divider { self.actions.get(&event) } else { None } {
124 Some(action) => match action {
125 Action::MoveDividerToFront => self.move_divider(-1),
126 Action::MoveDividerToBack => self.move_divider(1),
127 },
128
129 None => match event {
130 Event::Mouse { offset, position, event } => {
131 if self.moving_divider {
132 if matches!(event, MouseEvent::Release(_)) {
135 self.moving_divider = false;
137 } else if let Some(position) = position.checked_sub(offset) {
138 let mut divider = *position.get(self.orientation);
139
140 if self.border && divider != 0 {
141 divider -= 1;
142 }
143
144 self.set_divider(divider);
145 }
146
147 EventResult::consumed()
148 } else if event.button() == Some(MouseButton::Left)
149 && self.is_start_moving_divider(offset, position)
150 {
151 self.moving_divider = true;
154 EventResult::consumed()
155 } else if let Some((offset, position)) =
156 self.front.mouse_event(offset, position, self.orientation, self.border, self.size)
157 {
158 let prior = if event.grabs_focus() {
161 self.set_focus(Some(WhichPane::Front))
162 } else {
163 EventResult::Ignored
164 };
165
166 self.front.view.on_event(Event::Mouse { offset, position, event }).after(prior)
167 } else if let Some((offset, position)) =
168 self.back.mouse_event(offset, position, self.orientation, self.border, self.size)
169 {
170 let prior = if event.grabs_focus() {
173 self.set_focus(Some(WhichPane::Back))
174 } else {
175 EventResult::Ignored
176 };
177
178 self.back.view.on_event(Event::Mouse { offset, position, event }).after(prior)
179 } else {
180 EventResult::Ignored
181 }
182 }
183
184 event => match self.focus {
185 Some(WhichPane::Front) => self.front.view.on_event(event),
186 Some(WhichPane::Back) => self.back.view.on_event(event),
187 None => {
188 let prior = self.set_focus(Some(WhichPane::Front));
190 self.front.view.on_event(event).after(prior)
191 }
192 },
193 },
194 }
195 }
196
197 fn call_on_any(&mut self, selector: &Selector, callback: AnyCb) {
198 self.front.view.call_on_any(selector, callback);
199 self.back.view.call_on_any(selector, callback);
200 }
201
202 fn draw(&self, printer: &Printer) {
203 let mut _shrinked = None;
204 let printer = if self.draw_lines(printer) {
205 _shrinked = Some(printer.shrinked_centered((2, 2)));
206 &_shrinked.expect("shrinked")
207 } else {
208 printer
209 };
210
211 self.front.draw(printer, self.orientation, self.is_focused(WhichPane::Front));
212 self.back.draw(printer, self.orientation, self.is_focused(WhichPane::Back));
213 }
214}
215
216impl SplitPanel {
217 fn is_focused(&self, which: WhichPane) -> bool {
219 self.focus.map(|focus| focus == which).unwrap_or_default()
220 }
221
222 fn set_focus(&mut self, focus: Option<WhichPane>) -> EventResult {
224 let old_focus = self.focus;
225 self.focus = focus;
226 if self.focus != old_focus
227 && let Some(old_focus) = old_focus
228 {
229 return match old_focus {
230 WhichPane::Front => self.front.view.on_event(Event::FocusLost),
231 WhichPane::Back => self.back.view.on_event(Event::FocusLost),
232 };
233 }
234
235 EventResult::consumed()
236 }
237
238 fn is_start_moving_divider(&self, offset: Vec2, position: Vec2) -> bool {
240 if !self.movable_divider || self.moving_divider {
241 return false;
242 }
243
244 if let Some(mut divider) = self.divider
245 && let Some(size) = self.size
246 && size.x != 0
247 && size.y != 0
248 && let Some(position) = position.checked_sub(offset)
249 {
250 if self.border {
251 divider += 1;
252 let corner = size - (1, 1);
253 match self.orientation {
254 Orientation::Horizontal => position.y > 0 && position.y < corner.y && position.x == divider,
255 Orientation::Vertical => position.x > 0 && position.x < corner.x && position.y == divider,
256 }
257 } else {
258 match self.orientation {
259 Orientation::Horizontal => position.x == divider,
260 Orientation::Vertical => position.y == divider,
261 }
262 }
263 } else {
264 false
265 }
266 }
267
268 fn move_divider(&mut self, delta: isize) -> EventResult {
270 if !self.movable_divider {
271 return EventResult::Ignored;
272 }
273
274 match self.divider {
275 Some(divider) => {
276 if delta > 0 {
277 self.set_divider(divider + delta as usize);
278 } else if let Some(divider) = divider.checked_add_signed(delta) {
279 self.set_divider(divider);
280 }
281 }
282
283 None => self.set_divider(0),
284 }
285
286 EventResult::consumed()
287 }
288
289 fn layout_divider(&mut self, extent: usize) -> usize {
291 match self.divider {
292 Some(divider) => {
293 if divider >= extent {
294 if extent == 0 {
295 self.set_divider(0);
297 } else {
298 self.set_divider(extent - 1);
300 }
301 }
302 }
303
304 None => self.set_divider(extent / 2),
306 }
307
308 self.divider.expect("divider")
309 }
310
311 fn draw_lines(&self, printer: &Printer) -> bool {
315 let Some(mut divider) = self.divider else {
316 return false;
318 };
319
320 if printer.size.x == 0 || printer.size.y == 0 {
321 return false;
322 }
323
324 let corner = printer.size - (1, 1);
325
326 if self.border {
327 divider += 1;
328
329 printer.print((0, 0), TOP_LEFT);
331 printer.print((corner.x, 0), TOP_RIGHT);
332 printer.print((0, corner.y), BOTTOM_LEFT);
333 printer.print(corner, BOTTOM_RIGHT);
334
335 let mut _divider_printer = None;
336 let divider_printer = if self.moving_divider {
337 let mut divider_printer = printer.clone();
338 divider_printer.set_effect(Effect::Reverse);
339 _divider_printer = Some(divider_printer);
340 &_divider_printer.expect("divider_printer")
341 } else {
342 printer
343 };
344
345 match self.orientation {
346 Orientation::Horizontal => {
347 printer.print((divider, 0), TOP_HINGE);
349 printer.print((divider, corner.y), BOTTOM_HINGE);
350
351 if let Some(length) = divider.checked_sub(1) {
352 printer.print_hline((1, 0), length, HORIZONTAL);
354 printer.print_hline((1, corner.y), length, HORIZONTAL);
356 }
357
358 if let Some(length) = corner.x.checked_sub(divider + 1) {
359 printer.print_hline((divider + 1, 0), length, HORIZONTAL);
361 printer.print_hline((divider + 1, corner.y), length, HORIZONTAL);
363 }
364
365 if let Some(length) = corner.y.checked_sub(1) {
366 printer.print_vline((0, 1), length, VERTICAL);
368 divider_printer.print_vline((divider, 1), length, VERTICAL_THICK);
370 printer.print_vline((corner.x, 1), length, VERTICAL);
372 }
373 }
374
375 Orientation::Vertical => {
376 printer.print((0, divider), LEFT_HINGE);
378 printer.print((corner.x, divider), RIGHT_HINGE);
379
380 if let Some(length) = divider.checked_sub(1) {
381 printer.print_vline((0, 1), length, VERTICAL);
383 printer.print_vline((corner.x, 1), length, VERTICAL);
385 }
386
387 if let Some(length) = corner.y.checked_sub(divider + 1) {
388 printer.print_vline((0, divider + 1), length, VERTICAL);
390 printer.print_vline((corner.x, divider + 1), length, VERTICAL);
392 }
393
394 if let Some(length) = corner.x.checked_sub(1) {
395 printer.print_hline((1, 0), length, HORIZONTAL);
397 divider_printer.print_hline((1, divider), length, HORIZONTAL_THICK);
399 printer.print_hline((1, corner.y), length, HORIZONTAL);
401 }
402 }
403 }
404
405 true
406 } else {
407 match self.orientation {
408 Orientation::Horizontal => printer.print_vline((divider, 0), corner.y, VERTICAL),
409 Orientation::Vertical => printer.print_vline((divider, 0), corner.y, VERTICAL),
410 }
411
412 false
413 }
414 }
415}