1use crossbeam::channel::{unbounded, Sender};
2use cursive::direction::{Absolute, Direction};
3use cursive::event::{AnyCb, Event, EventResult, Key};
4use cursive::view::{CannotFocus, Selector, View, ViewNotFound};
5use cursive::views::NamedView;
6use cursive::{Printer, Vec2};
7use log::debug;
8use num::clamp;
9
10use crate::error;
11use crate::Bar;
12use crate::TabBar;
13use crate::TabView;
14
15#[derive(Clone, Copy, Debug)]
16pub enum Align {
17 Start,
18 Center,
19 End,
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub enum Placement {
24 VerticalLeft,
25 VerticalRight,
26 HorizontalTop,
27 HorizontalBottom,
28}
29
30impl Align {
31 pub fn get_offset(self, content: usize, container: usize) -> usize {
32 if container < content {
33 0
34 } else {
35 match self {
36 Align::Start => 0,
37 Align::Center => (container - content) / 2,
38 Align::End => container - content,
39 }
40 }
41 }
42}
43
44pub struct TabPanel {
61 bar: TabBar,
62 bar_size: Vec2,
63 tab_size: Vec2,
64 tx: Sender<String>,
65 tabs: TabView,
66 bar_focused: bool,
67 bar_align: Align,
68 bar_placement: Placement,
69}
70
71impl Default for TabPanel {
72 fn default() -> Self {
73 Self::new()
74 }
75}
76
77impl TabPanel {
78 pub fn new() -> Self {
81 let mut tabs = TabView::new();
82 let (tx, rx) = unbounded();
83 let (active_tx, active_rx) = unbounded();
84 tabs.set_bar_rx(rx);
85 tabs.set_active_key_tx(active_tx);
86 Self {
87 bar: TabBar::new(active_rx)
88 .with_placement(Placement::HorizontalTop)
89 .with_alignment(Align::Start),
90 bar_size: Vec2::new(1, 1),
91 tab_size: Vec2::new(1, 1),
92 tabs,
93 tx,
94 bar_focused: true,
95 bar_align: Align::Start,
96 bar_placement: Placement::HorizontalTop,
97 }
98 }
99
100 pub fn active_tab(&self) -> Option<&str> {
103 self.tabs.active_tab()
104 }
105
106 pub fn active_view(&self) -> Option<&dyn View> {
108 self.tabs.active_view()
109 }
110
111 pub fn active_view_mut(&mut self) -> Option<&mut dyn View> {
113 self.tabs.active_view_mut()
114 }
115
116 pub fn views(&self) -> Vec<&dyn View> {
117 self.tabs.views()
118 }
119
120 pub fn views_mut(&mut self) -> Vec<&mut dyn View> {
121 self.tabs.views_mut()
122 }
123
124 pub fn set_active_tab(&mut self, id: &str) -> Result<(), error::IdNotFound> {
127 self.tabs.set_active_tab(id)
128 }
129
130 pub fn with_active_tab(mut self, id: &str) -> Result<Self, Self> {
134 match self.tabs.set_active_tab(id) {
135 Ok(_) => Ok(self),
136 Err(_) => Err(self),
137 }
138 }
139
140 pub fn add_tab<T: View>(&mut self, view: NamedView<T>) {
143 let id = view.name();
144 self.bar.add_button(self.tx.clone(), id);
145 self.tabs.add_tab(view);
146 }
147
148 pub fn with_tab<T: View>(mut self, view: NamedView<T>) -> Self {
151 let id = view.name();
152 self.bar.add_button(self.tx.clone(), id);
153 self.tabs.add_tab(view);
154 self
155 }
156
157 pub fn swap_tabs(&mut self, fst: &str, snd: &str) {
160 self.tabs.swap_tabs(fst, snd);
161 self.bar.swap_button(fst, snd);
162 }
163
164 pub fn add_tab_at<T: View>(&mut self, view: NamedView<T>, pos: usize) {
168 let id = view.name();
169 self.bar.add_button_at(self.tx.clone(), id, pos);
170 self.tabs.add_tab_at(view, pos);
171 }
172
173 pub fn with_tab_at<T: View>(mut self, view: NamedView<T>, pos: usize) -> Self {
177 let id = view.name();
178 self.bar.add_button_at(self.tx.clone(), id, pos);
179 self.tabs.add_tab_at(view, pos);
180 self
181 }
182
183 pub fn remove_tab(&mut self, id: &str) -> Result<(), error::IdNotFound> {
185 self.bar.remove_button(id);
186 self.tabs.remove_tab(id)
187 }
188
189 pub fn next(&mut self) {
191 self.tabs.next()
192 }
193
194 pub fn prev(&mut self) {
196 self.tabs.prev()
197 }
198
199 pub fn with_bar_alignment(mut self, align: Align) -> Self {
201 self.set_bar_alignment(align);
202
203 self
204 }
205
206 pub fn set_bar_alignment(&mut self, align: Align) {
208 self.bar_align = align;
209 self.bar.set_alignment(align);
210 }
211
212 pub fn with_bar_placement(mut self, placement: Placement) -> Self {
213 self.set_bar_placement(placement);
214 self
215 }
216
217 pub fn set_bar_placement(&mut self, placement: Placement) {
218 self.bar_placement = placement;
219 self.bar.set_placement(placement);
220 }
221
222 pub fn tab_order(&self) -> Vec<String> {
224 self.tabs.tab_order()
225 }
226
227 fn draw_outer_panel(&self, printer: &Printer) {
229 match self.bar_placement {
230 Placement::HorizontalTop => {
231 printer.print_vline((0, 0), printer.size.y, "│");
233 printer.print_vline((printer.size.x - 1, 0), printer.size.y, "│");
234 printer.print_hline((0, printer.size.y - 1), printer.size.x, "─");
236
237 printer.print((0, self.bar_size.y - 1), "┌");
238 printer.print((printer.size.x - 1, self.bar_size.y - 1), "┐");
239 printer.print((0, printer.size.y - 1), "└");
240 printer.print((printer.size.x - 1, printer.size.y - 1), "┘");
241 }
242 Placement::HorizontalBottom => {
243 printer.print_vline((0, 0), printer.size.y, "│");
245 printer.print_vline((printer.size.x - 1, 0), printer.size.y, "│");
246 let lowest = clamp(printer.size.y - self.bar_size.y, 0, printer.size.y - 1);
248 printer.print_hline((0, 0), printer.size.x, "─");
249 printer.print((0, 0), "┌");
250 printer.print((printer.size.x - 1, 0), "┐");
251 printer.print((0, lowest), "└");
252 printer.print((printer.size.x - 1, lowest), "┘");
253 }
254 Placement::VerticalLeft => {
255 printer.print_vline((printer.size.x - 1, 0), printer.size.y, "│");
257 printer.print_hline((self.bar_size.x - 1, 0), printer.size.x, "─");
259 printer.print_hline(
260 (self.bar_size.x - 1, printer.size.y - 1),
261 printer.size.x,
262 "─",
263 );
264 printer.print((self.bar_size.x - 1, 0), "┌");
265 printer.print((printer.size.x - 1, 0), "┐");
266 printer.print((self.bar_size.x - 1, printer.size.y - 1), "└");
267 printer.print((printer.size.x - 1, printer.size.y - 1), "┘");
268 }
269 Placement::VerticalRight => {
270 printer.print_vline((0, 0), printer.size.y, "│");
272 printer.print_hline((0, 0), printer.size.x, "─");
274 printer.print_hline((0, printer.size.y - 1), printer.size.x, "─");
276
277 let right = clamp(printer.size.x - self.bar_size.x, 0, printer.size.x - 1);
278 printer.print((0, 0), "┌");
279 printer.print((right, 0), "┐");
280 printer.print_hline((right + 1, 0), printer.size.x, " ");
281 printer.print((0, printer.size.y - 1), "└");
282 printer.print((right, printer.size.y - 1), "┘");
283 printer.print_hline((right + 1, printer.size.y - 1), printer.size.x, " ");
284 }
285 }
286 }
287
288 fn on_event_focused(&mut self, evt: Event) -> EventResult {
289 match self.bar.on_event(evt.relativized(match self.bar_placement {
290 Placement::HorizontalTop | Placement::VerticalLeft => Vec2::new(0, 0),
291 Placement::HorizontalBottom => self.tab_size.keep_y() + Vec2::new(0, 1),
292 Placement::VerticalRight => self.tab_size.keep_x() + Vec2::new(1, 0),
293 })) {
294 EventResult::Consumed(cb) => EventResult::Consumed(cb),
295 EventResult::Ignored => match evt {
296 Event::Key(Key::Down) if self.bar_placement == Placement::HorizontalTop => {
297 if let Ok(result) = self.tabs.take_focus(Direction::up()) {
298 self.bar_focused = false;
299 result.and(EventResult::consumed())
300 } else {
301 EventResult::Ignored
302 }
303 }
304 Event::Key(Key::Up) if self.bar_placement == Placement::HorizontalBottom => {
305 if let Ok(result) = self.tabs.take_focus(Direction::down()) {
306 self.bar_focused = false;
307 result.and(EventResult::consumed())
308 } else {
309 EventResult::Ignored
310 }
311 }
312 Event::Key(Key::Left) if self.bar_placement == Placement::VerticalRight => {
313 if let Ok(result) = self.tabs.take_focus(Direction::right()) {
314 self.bar_focused = false;
315 result.and(EventResult::consumed())
316 } else {
317 EventResult::Ignored
318 }
319 }
320 Event::Key(Key::Right) if self.bar_placement == Placement::VerticalLeft => {
321 if let Ok(result) = self.tabs.take_focus(Direction::left()) {
322 self.bar_focused = false;
323 result.and(EventResult::consumed())
324 } else {
325 EventResult::Ignored
326 }
327 }
328 _ => EventResult::Ignored,
329 },
330 }
331 }
332
333 fn on_event_unfocused(&mut self, evt: Event) -> EventResult {
334 match self
335 .tabs
336 .on_event(evt.relativized(match self.bar_placement {
337 Placement::HorizontalTop => Vec2::new(1, self.bar_size.y),
338 Placement::VerticalLeft => Vec2::new(self.bar_size.x, 1),
339 Placement::HorizontalBottom | Placement::VerticalRight => Vec2::new(1, 1),
340 })) {
341 EventResult::Consumed(cb) => EventResult::Consumed(cb),
342 EventResult::Ignored => match evt {
343 Event::Key(Key::Up) if self.bar_placement == Placement::HorizontalTop => {
344 self.bar_focused = true;
345 EventResult::Consumed(None)
346 }
347 Event::Key(Key::Down) if self.bar_placement == Placement::HorizontalBottom => {
348 self.bar_focused = true;
349 EventResult::Consumed(None)
350 }
351 Event::Key(Key::Left) if self.bar_placement == Placement::VerticalLeft => {
352 self.bar_focused = true;
353 EventResult::Consumed(None)
354 }
355 Event::Key(Key::Right) if self.bar_placement == Placement::VerticalRight => {
356 self.bar_focused = true;
357 EventResult::Consumed(None)
358 }
359 _ => EventResult::Ignored,
360 },
361 }
362 }
363
364 fn check_focus_grab(&mut self, event: &Event) -> EventResult {
365 if let Event::Mouse {
366 offset,
367 position,
368 event,
369 } = *event
370 {
371 debug!(
372 "mouse event: offset: {:?} , position: {:?}",
373 offset, position
374 );
375 if !event.grabs_focus() {
376 return EventResult::Ignored;
377 }
378
379 match self.bar_placement {
380 Placement::VerticalRight | Placement::HorizontalBottom => {
381 if position > offset && self.tab_size.fits(position - offset) {
382 if let Ok(res) = self.tabs.take_focus(Direction::none()) {
383 self.bar_focused = false;
384 return res;
385 }
386 } else {
387 self.bar_focused = true;
388 }
389 }
390 Placement::HorizontalTop | Placement::VerticalLeft => {
391 if position.fits(offset)
394 && (self.bar_size - Vec2::new(1, 1)).fits(position - offset)
395 {
396 self.bar_focused = true;
397 } else if let Ok(res) = self.tabs.take_focus(Direction::none()) {
398 self.bar_focused = false;
399 return res;
400 }
401 }
402 }
403 }
404 EventResult::Ignored
405 }
406}
407
408impl View for TabPanel {
409 fn draw(&self, printer: &Printer) {
410 self.draw_outer_panel(printer);
411 let printer_bar = printer
412 .offset(match self.bar_placement {
413 Placement::HorizontalTop => (1, 0),
414 Placement::HorizontalBottom => (
415 1,
416 clamp(printer.size.y - self.bar_size.y, 0, printer.size.y - 1),
417 ),
418 Placement::VerticalLeft => (0, 1),
419 Placement::VerticalRight => (
420 clamp(printer.size.x - self.bar_size.x, 0, printer.size.x - 1),
421 1,
422 ),
423 })
424 .cropped(match self.bar_placement {
425 Placement::HorizontalTop | Placement::HorizontalBottom => {
426 (printer.size.x - 2, self.bar_size.y)
427 }
428 Placement::VerticalRight | Placement::VerticalLeft => {
429 (self.bar_size.x, printer.size.y - 2)
430 }
431 })
432 .focused(self.bar_focused);
433 let printer_tab = printer
434 .offset(match self.bar_placement {
435 Placement::VerticalLeft => (self.bar_size.x, 1),
436 Placement::VerticalRight => (1, 1),
437 Placement::HorizontalBottom => (1, 1),
438 Placement::HorizontalTop => (1, self.bar_size.y),
439 })
440 .cropped(match self.bar_placement {
442 Placement::VerticalLeft | Placement::VerticalRight => {
443 (printer.size.x - self.bar_size.x - 1, printer.size.y - 2)
444 }
445 Placement::HorizontalBottom | Placement::HorizontalTop => {
446 (printer.size.x - 2, printer.size.y - self.bar_size.y - 1)
447 }
448 })
449 .focused(!self.bar_focused);
450 self.bar.draw(&printer_bar);
451 self.tabs.draw(&printer_tab);
452 }
453
454 fn layout(&mut self, vec: Vec2) {
455 self.bar.layout(match self.bar_placement {
456 Placement::VerticalRight | Placement::VerticalLeft => {
457 Vec2::new(self.bar_size.x, vec.y - 2)
458 }
459 Placement::HorizontalBottom | Placement::HorizontalTop => {
460 Vec2::new(vec.x - 2, self.bar_size.y)
461 }
462 });
463 self.tabs.layout(match self.bar_placement {
464 Placement::VerticalRight | Placement::VerticalLeft => {
465 self.tab_size = Vec2::new(vec.x - self.bar_size.x - 1, vec.y - 2);
466 self.tab_size
467 }
468 Placement::HorizontalBottom | Placement::HorizontalTop => {
469 self.tab_size = Vec2::new(vec.x - 2, vec.y - self.bar_size.y - 1);
470 self.tab_size
471 }
472 });
473 }
474
475 fn needs_relayout(&self) -> bool {
476 self.bar.needs_relayout() || self.tabs.needs_relayout()
477 }
478
479 fn required_size(&mut self, cst: Vec2) -> Vec2 {
480 let tab_size = self.tabs.required_size(cst);
481 self.bar_size = self.bar.required_size(cst);
482 match self.bar_placement {
483 Placement::HorizontalTop | Placement::HorizontalBottom => self
484 .bar_size
485 .stack_vertical(&tab_size)
486 .stack_vertical(&Vec2::new(tab_size.x + 2, 1)),
487 Placement::VerticalLeft | Placement::VerticalRight => self
488 .bar_size
489 .stack_horizontal(&tab_size)
490 .stack_vertical(&Vec2::new(1, tab_size.y + 2)),
491 }
492 }
493
494 fn on_event(&mut self, evt: Event) -> EventResult {
495 let result = self.check_focus_grab(&evt);
496
497 result.and(if self.bar_focused {
498 self.on_event_focused(evt)
499 } else {
500 self.on_event_unfocused(evt)
501 })
502 }
503
504 fn take_focus(&mut self, d: Direction) -> Result<EventResult, CannotFocus> {
505 let tabs_take_focus = |panel: &mut TabPanel, d: Direction| {
506 let result = panel.tabs.take_focus(d);
507
508 if result.is_ok() {
509 panel.bar_focused = false;
510 } else {
511 panel.bar_focused = true;
512 }
513
514 result
515 };
516
517 let mut result = Ok(EventResult::consumed());
518
519 match self.bar_placement {
520 Placement::HorizontalBottom => match d {
521 Direction::Abs(Absolute::Up) => {
522 result = tabs_take_focus(self, d);
523 }
524 Direction::Abs(Absolute::Left) | Direction::Abs(Absolute::Right) => {
525 if !self.bar_focused {
526 result = tabs_take_focus(self, d);
527 }
528 }
529 Direction::Abs(Absolute::Down) => {
530 self.bar_focused = true;
531 }
532 _ => (),
533 },
534 Placement::HorizontalTop => match d {
535 Direction::Abs(Absolute::Down) => {
536 result = tabs_take_focus(self, d);
537 }
538 Direction::Abs(Absolute::Left) | Direction::Abs(Absolute::Right) => {
539 if !self.bar_focused {
540 result = tabs_take_focus(self, d);
541 }
542 }
543 Direction::Abs(Absolute::Up) => {
544 self.bar_focused = true;
545 }
546 _ => (),
547 },
548 Placement::VerticalLeft => match d {
549 Direction::Abs(Absolute::Right) => {
550 result = tabs_take_focus(self, d);
551 }
552 Direction::Abs(Absolute::Up) | Direction::Abs(Absolute::Down) => {
553 if !self.bar_focused {
554 result = tabs_take_focus(self, d);
555 }
556 }
557 Direction::Abs(Absolute::Left) => self.bar_focused = true,
558 _ => {}
559 },
560 Placement::VerticalRight => match d {
561 Direction::Abs(Absolute::Left) => {
562 result = tabs_take_focus(self, d);
563 }
564 Direction::Abs(Absolute::Up) | Direction::Abs(Absolute::Down) => {
565 if !self.bar_focused {
566 result = tabs_take_focus(self, d)
567 }
568 }
569 Direction::Abs(Absolute::Right) => self.bar_focused = true,
570 _ => {}
571 },
572 }
573
574 return Ok(result.unwrap_or(EventResult::Ignored));
575 }
576
577 fn focus_view(&mut self, slt: &Selector) -> Result<EventResult, ViewNotFound> {
578 self.tabs.focus_view(slt)
579 }
580
581 fn call_on_any<'a>(&mut self, slt: &Selector, cb: AnyCb<'a>) {
582 self.bar.call_on_any(slt, cb);
583 self.tabs.call_on_any(slt, cb);
584 }
585}