cursive_extras/views/tabs/
layer.rs1use crossbeam_channel::{bounded, Receiver, Sender};
2use cursive_core::{
3 Cursive, Printer, Vec2, View,
4 align::HAlign,
5 traits::{Scrollable, With, Resizable},
6 direction::{Absolute, Direction},
7 event::{
8 AnyCb, Event,
9 EventResult, Key,
10 MouseButton, MouseEvent
11 },
12 view::{
13 CannotFocus,
14 Position,
15 Selector,
16 ViewNotFound
17 },
18 views::{
19 BoxedView,
20 LayerPosition,
21 SelectView,
22 Dialog,
23 OnEventView
24 }
25};
26use rust_utils::chainable;
27use crate::c_focus;
28use super::{
29 TitleButtonType,
30 TabContainer,
31 TabFocus,
32 TabIter,
33 TabIterMut,
34 ViewButton,
35 ButtonAction,
36 TitleButton
37};
38
39pub struct TabLayer {
43 tabs: TabContainer,
45
46 shown_title_buttons: Vec<TitleButton>,
48
49 title_buttons: Vec<TitleButton>,
51
52 title_align: HAlign,
54
55 buttons: Vec<ViewButton>,
57
58 button_align: HAlign,
60
61 focus: TabFocus,
63
64 id_snd: Sender<usize>,
66 id_rcv: Receiver<usize>
67}
68
69impl TabLayer {
70 #[must_use]
72 pub fn new() -> TabLayer {
73 let (id_snd, id_rcv) = bounded(1);
74 TabLayer {
75 tabs: TabContainer::new(),
76 title_buttons: vec![],
77 shown_title_buttons: vec![],
78 title_align: HAlign::Center,
79 buttons: vec![],
80 button_align: HAlign::Left,
81 focus: TabFocus::Content,
82 id_snd,
83 id_rcv
84 }
85 }
86
87 #[chainable]
89 pub fn add_button<F: Fn(&mut Cursive) + Send + Sync + 'static>(&mut self, text: &str, func: F) {
90 let new_button = ViewButton::from_fn(text, func);
91 if self.has_next_prev_buttons() {
92 let last_idx = self.buttons.len() - 2;
93 self.buttons.insert(last_idx, new_button);
94 }
95 else { self.buttons.push(new_button) }
96 }
97
98 #[chainable]
100 pub fn add_dismiss_button(&mut self, text: &str) {
101 let new_button = ViewButton::from_fn(text, |r| {
102 if r.screen().len() <= 1 { r.quit(); }
103 else { r.pop_layer(); }
104 });
105 if self.has_next_prev_buttons() {
106 let last_idx = self.buttons.len() - 2;
107 self.buttons.insert(last_idx, new_button);
108 }
109 else { self.buttons.push(new_button) }
110 }
111
112 #[chainable]
114 pub fn add_next_prev_buttons(&mut self, show: bool) {
115 if show {
116 self.buttons.insert(0, ViewButton::new("Previous", ButtonAction::Prev));
117 self.buttons.push(ViewButton::new("Next", ButtonAction::Next));
118 }
119 else if self.has_next_prev_buttons() {
120 self.buttons.remove(0);
121 self.buttons.pop();
122 }
123 }
124
125 #[chainable]
127 pub fn add_close_button(&mut self, show: bool) {
128 if show {
129 let new_button = ViewButton::new("Close", ButtonAction::Close);
130 if self.has_next_prev_buttons() {
131 let last_idx = self.buttons.len() - 2;
132 self.buttons.insert(last_idx, new_button);
133 }
134 else { self.buttons.push(new_button) }
135 }
136 else {
137 self.buttons.retain(|b| matches!(b.action, ButtonAction::Close));
138 }
139 }
140
141 #[chainable]
143 pub fn set_button_align(&mut self, align: HAlign) { self.button_align = align; }
144
145 #[chainable]
147 pub fn set_title_align(&mut self, align: HAlign) { self.title_align = align; }
148
149 pub fn clear_buttons(&mut self) { self.buttons.clear(); }
151
152 pub fn cur_id(&self) -> Option<usize> { self.tabs.cur_id() }
154
155 pub fn cur_view(&self) -> Option<&BoxedView> { self.tabs.cur_view() }
157
158 pub fn cur_view_mut(&mut self) -> Option<&mut BoxedView> { self.tabs.cur_view_mut() }
160
161
162 pub fn cur_title(&self) -> &str {
166 if let Some(id) = self.cur_id() {
167 for button in &self.title_buttons {
168 if let TitleButtonType::Tab(tab_id) = button.b_type {
169 if tab_id == id {
170 return &button.label;
171 }
172 }
173 }
174 ""
175 }
176 else { "" }
177 }
178 pub fn views(&self) -> TabIter { self.tabs.views() }
180
181 pub fn views_mut(&mut self) -> TabIterMut { self.tabs.views_mut() }
183
184 pub fn set_cur_tab(&mut self, id: usize) { self.tabs.set_cur_tab(id); }
186
187 pub fn set_title(&mut self, id: usize, new_title: &str) {
189 for button in &mut self.title_buttons {
190 let upd_title = if let TitleButtonType::Tab(tab_id) = button.b_type { tab_id == id }
191 else { false };
192 if upd_title { button.set_label(new_title) }
193 }
194 }
195
196 pub fn add_tab<V: View>(&mut self, title: &str, view: V) -> usize {
198 let new_id = self.tabs.add_tab(view);
199 self.set_cur_tab(new_id);
200 self.title_buttons.push(TitleButton::new(title, new_id));
201 new_id
202 }
203
204 #[must_use]
208 pub fn tab<V: View>(mut self, title: &str, view: V) -> TabLayer {
209 self.add_tab(title, view);
210 self.set_cur_tab(0);
211 self
212 }
213
214 pub fn remove_tab(&mut self, id: usize) {
216 self.tabs.remove_tab(id);
217 self.title_buttons.retain(|t_bt| {
218 if let TitleButtonType::Tab(old_id) = t_bt.b_type {
219 old_id != id
220 }
221 else { true }
222 });
223 }
224
225 pub fn ids(&self) -> Vec<usize> { self.tabs.ids() }
227
228 fn has_next_prev_buttons(&self) -> bool {
230 matches!(self.buttons.last(), Some(ViewButton { action: ButtonAction::Next, ..}))
231 }
232
233 fn open_overflow(&self) -> EventResult {
235 let id_snd = self.id_snd.clone();
236
237 let mut tabs_menu = SelectView::new()
238 .on_submit(move |root, id| {
239 id_snd.send(*id).unwrap();
240 root.pop_layer();
241 });
242
243 for button in &self.title_buttons {
244 if let TitleButtonType::Tab(tab_id) = button.b_type {
245 tabs_menu.add_item(&button.label, tab_id);
246 }
247 }
248
249 let ofs = self.shown_title_buttons[3].rect.top_left() + (0, 1);
252
253 EventResult::with_cb_once(move |root| {
254 if root.fps().is_none() { root.set_fps(30); }
257
258 let current_offset = root
259 .screen()
260 .layer_offset(LayerPosition::FromFront(0))
261 .unwrap_or_else(Vec2::zero);
262 let offset = ofs.signed() - current_offset;
263
264 root.screen_mut().add_layer_at(
265 Position::parent(offset),
266 c_focus!(
267 Dialog::around(tabs_menu.scrollable())
268 .wrap_with(OnEventView::new)
269 .on_event(Event::Key(Key::Esc), |r| { r.pop_layer(); })
270 .max_height(20)
271 )
272 )
273 })
274 }
275
276 fn exec_button_action(&mut self, selected: usize) -> EventResult {
278 match &self.buttons[selected].action {
279 ButtonAction::Close => {
280 let tab_num = self.cur_id().unwrap();
281 if self.tabs.views().count() > 1 {
282 self.remove_tab(tab_num);
283 }
284 return EventResult::consumed();
285 }
286
287 ButtonAction::Next => self.tabs.next(),
288 ButtonAction::Prev => self.tabs.prev(),
289 ButtonAction::CallBack(cb) => return EventResult::Consumed(Some(cb.clone()))
290 }
291 EventResult::consumed()
292 }
293
294 fn button_event(&mut self, event: &Event) -> EventResult {
296 let num_title_buttons = self.shown_title_buttons.len();
297 let num_buttons = self.buttons.len();
298
299 match event {
300 Event::Key(Key::Enter) => {
301 match self.focus {
302 TabFocus::TitleBar(selected) => {
303 let button = &self.shown_title_buttons[selected];
304 return match button.b_type {
305 TitleButtonType::Tab(id) => {
306 self.set_cur_tab(id);
307 EventResult::consumed()
308 }
309
310 TitleButtonType::Overflow => self.open_overflow()
311 }
312 }
313
314 TabFocus::Buttons(selected) => return self.exec_button_action(selected),
315 TabFocus::Content => { }
316 }
317 }
318
319 Event::Key(Key::Down) => {
320 if matches!(self.focus, TabFocus::TitleBar(_)) {
321 self.focus = TabFocus::Content;
322 if let Ok(result) = self.tabs.take_focus(Direction::none()) {
323 return result.and(EventResult::consumed());
324 }
325 }
326 }
327
328 Event::Key(Key::Left) => {
329 match self.focus {
330 TabFocus::TitleBar(ref mut selected) => {
331 if *selected == 0 { *selected = num_title_buttons; }
332 *selected -= 1;
333 return EventResult::consumed();
334 }
335
336 TabFocus::Buttons(ref mut selected) => {
337 if *selected == 0 { *selected = num_buttons; }
338 *selected -= 1;
339 return EventResult::consumed();
340 }
341
342 TabFocus::Content => { }
343 }
344 }
345
346 Event::Key(Key::Right) => {
347 match self.focus {
348 TabFocus::TitleBar(ref mut selected) => {
349 *selected += 1;
350 if *selected >= num_title_buttons { *selected = 0; }
351 return EventResult::consumed();
352 }
353
354 TabFocus::Buttons(ref mut selected) => {
355 *selected += 1;
356 if *selected >= num_buttons { *selected = 0; }
357 return EventResult::consumed();
358 }
359
360 TabFocus::Content => { }
361 }
362 }
363
364 Event::Key(Key::End) => {
365 match self.focus {
366 TabFocus::TitleBar(ref mut selected) => {
367 *selected = num_title_buttons - 1;
368 return EventResult::consumed();
369 }
370
371 TabFocus::Buttons(ref mut selected) => {
372 *selected = num_buttons - 1;
373 return EventResult::consumed();
374 }
375
376 TabFocus::Content => { }
377 }
378 }
379
380 Event::Key(Key::Home) => {
381 match self.focus {
382 TabFocus::TitleBar(ref mut selected) | TabFocus::Buttons(ref mut selected) => {
383 *selected = 0;
384 return EventResult::consumed();
385 }
386
387 TabFocus::Content => { }
388 }
389 }
390
391 Event::Key(Key::Tab) => {
392 match self.focus {
393 TabFocus::TitleBar(_) => self.focus = TabFocus::Content,
394 TabFocus::Content => self.focus = TabFocus::Buttons(0),
395 TabFocus::Buttons(ref mut selected) => {
396 if *selected >= self.buttons.len() - 1 { return EventResult::Ignored }
397 *selected += 1;
398 }
399 }
400 return EventResult::consumed();
401 }
402
403 Event::Refresh => {
404 if let Ok(id) = self.id_rcv.try_recv() {
405 self.set_cur_tab(id);
406 self.tabs.set_as_first(id);
407 return EventResult::consumed();
408 }
409 }
410
411 Event::Key(Key::Up) => {
412 if matches!(self.focus, TabFocus::Buttons(_)) {
413 self.focus = TabFocus::Content;
414 return EventResult::consumed();
415 }
416 }
417
418 Event::FocusLost => self.focus = TabFocus::Content,
419 _ => { }
420 }
421 EventResult::Ignored
422 }
423
424 fn view_event(&mut self, event: &Event) -> EventResult {
426 match self.tabs.on_event(event.relativized((1, 1))) {
427 EventResult::Ignored => {
428 match event {
429 Event::Key(Key::Up) => {
430 self.focus = TabFocus::TitleBar(0);
431 return EventResult::consumed().and(self.tabs.on_event(Event::FocusLost));
432 }
433
434 Event::Key(Key::Down) => {
435 if !self.buttons.is_empty() {
436 self.focus = TabFocus::Buttons(0);
437 return EventResult::consumed().and(self.tabs.on_event(Event::FocusLost));
438 }
439 }
440
441 Event::Key(Key::Tab) => {
442 if matches!(self.focus, TabFocus::Content) {
443 self.focus = TabFocus::Buttons(0);
444 return EventResult::consumed();
445 }
446 }
447
448 Event::FocusLost => {
449 self.focus = TabFocus::Content;
450 return EventResult::consumed().and(self.tabs.on_event(Event::FocusLost));
451 }
452
453 _ => { }
454 }
455 EventResult::Ignored
456 }
457
458 res => res
459 }
460 }
461
462 fn mouse_event(&mut self, event: &Event) -> Option<EventResult> {
464 if let Event::Mouse {
465 offset, position,
466 event: MouseEvent::Release(button),
467 } = event {
468 let pos = position.checked_sub(offset)?;
469
470 for (i, t_button) in self.shown_title_buttons.iter().enumerate() {
471 if t_button.has_mouse_pos(pos) {
472 return if *button == MouseButton::Left {
473 let res = match t_button.b_type {
474 TitleButtonType::Tab(id) => {
475 self.set_cur_tab(id);
476 EventResult::consumed()
477 }
478
479 TitleButtonType::Overflow => self.open_overflow()
480 };
481 self.focus = TabFocus::TitleBar(i);
482 Some(res)
483 }
484 else {
485 self.focus = TabFocus::TitleBar(i);
486 Some(EventResult::consumed())
487 }
488 }
489 }
490
491 for (i, b_button) in self.buttons.iter().enumerate() {
492 if b_button.has_mouse_pos(pos) {
493 return if *button == MouseButton::Left {
494 self.focus = TabFocus::Buttons(i);
495 Some(self.exec_button_action(i))
496 }
497 else {
498 self.focus = TabFocus::Buttons(i);
499 Some(EventResult::consumed())
500 }
501 }
502 }
503 self.focus = TabFocus::Content;
504 }
505 None
506 }
507}
508
509impl View for TabLayer {
510 fn draw(&self, printer: &Printer) {
511 if !self.title_buttons.is_empty() {
513 let mut f_index: Option<usize> = None;
514 for (i, button) in self.shown_title_buttons.iter().enumerate() {
515 let selected = if let TabFocus::TitleBar(sel) = self.focus { i == sel && printer.focused }
516 else { false };
517 let focused = if let TitleButtonType::Tab(id) = button.b_type {
518 if let Some(cur_id) = self.cur_id() {
519 cur_id == id
520 }
521 else { false }
522 }
523 else { false };
524 if focused { f_index = Some(i); }
526 else {
527 button.draw(printer, selected, false, false, false);
528 }
529 }
530
531 if let Some(index) = f_index {
533 let selected = if let TabFocus::TitleBar(sel) = self.focus { index == sel && printer.focused }
534 else { false };
535 self.shown_title_buttons[index]
536 .draw(printer, selected, true, false, false);
537 }
538 }
539 printer.print_hline((0, 1), printer.size.x, "─");
540
541 let y = if self.buttons.is_empty() { printer.size.y + 1 }
542 else { printer.size.y };
543 let tabs_printer = printer
544 .cropped((printer.size.x, y))
545 .offset((0, 2))
546 .focused(self.focus == TabFocus::Content);
547
548 self.tabs.draw(&tabs_printer);
549
550 if !self.buttons.is_empty() {
552 for (i, button) in self.buttons.iter().enumerate() {
553 let selected = if let TabFocus::Buttons(sel) = self.focus { i == sel && printer.focused }
554 else { false };
555 button.draw(printer, selected);
556 }
557 }
558 }
559
560 fn layout(&mut self, size: Vec2) {
561 super::align_title_buttons(&mut self.shown_title_buttons, &self.title_buttons, self.title_align, size.x, false);
563
564 super::align_buttons(&mut self.buttons, self.button_align, size, false);
566
567 self.tabs.layout(size - (0, 3));
569 }
570
571 fn take_focus(&mut self, source: Direction) -> Result<EventResult, CannotFocus> {
572 let focus_tab = |dialog: &mut Self, d: Direction| {
573 let result = dialog.tabs.take_focus(d);
574 let focus = if result.is_err() { TabFocus::TitleBar(0) }
575 else { TabFocus::Content };
576 dialog.focus = focus;
577 result
578 };
579
580 let mut result: Result<EventResult, CannotFocus> = Ok(EventResult::consumed());
581 match source {
582 Direction::Abs(Absolute::Down) => result = focus_tab(self, source),
583
584 Direction::Abs(Absolute::Left) | Direction::Abs(Absolute::Right) => {
585 if !matches!(self.focus, TabFocus::TitleBar(_)) {
586 result = focus_tab(self, source);
587 }
588 }
589
590 Direction::Abs(Absolute::Up) => self.focus = TabFocus::TitleBar(0),
591 _ => { }
592 }
593
594 Ok(result.unwrap_or(EventResult::Ignored))
595 }
596
597 fn on_event(&mut self, event: Event) -> EventResult {
598 self.mouse_event(&event).unwrap_or(
599 if self.focus == TabFocus::Content { self.view_event(&event) }
600 else { self.button_event(&event) }
601 )
602 }
603
604 fn required_size(&mut self, constraint: Vec2) -> Vec2 { constraint }
605 fn needs_relayout(&self) -> bool { self.tabs.needs_relayout() }
606 fn focus_view(&mut self, sel: &Selector) -> Result<EventResult, ViewNotFound> { self.tabs.focus_view(sel) }
607 fn call_on_any(&mut self, sel: &Selector, cb: AnyCb) { self.tabs.call_on_any(sel, cb); }
608}
609
610impl Default for TabLayer {
611 fn default() -> TabLayer { TabLayer::new() }
612}