cursive_multiplex/
lib.rs

1//! # cursive view multiplexer
2//!
3//! This crate provides a view for the [cursive tui crate](https://github.com/gyscos/cursive).
4//! It provides an easier way to display nesting view structures as for example in tmux in cursive.
5//! All that has to be done is to insert the view into cursive and later to operate on the reference of it, to add, remove, switch views.
6//!
7//! Similar to tmux the user is able to resize, and switch between the current views, given they are focusable.
8//!
9//! # Usage example
10//! ```rust
11//! extern crate cursive;
12//! extern crate cursive_multiplex;
13//!
14//! use cursive_multiplex::Mux;
15//! use cursive::views::TextView;
16//! use cursive::Cursive;
17//!
18//! fn main() {
19//!     let mut mux = Mux::new();
20//!     let node1 = mux.add_right_of(TextView::new("Hello World"), mux.root().build().unwrap()).unwrap();
21//!     let mut siv = cursive::default();
22//!     mux.add_right_of(TextView::new("Hello from me too!".to_string()), node1);
23//!     siv.add_fullscreen_layer(mux);
24//!
25//!     // When your finished setting up
26//!     // siv.run();
27//! }
28//! ```
29
30#![doc(
31    html_logo_url = "https://raw.githubusercontent.com/deinstapel/cursive-multiplex/master/assets/cursive-multiplex.png"
32)]
33
34#[macro_use]
35extern crate log;
36
37mod actions;
38mod error;
39mod id;
40mod node;
41mod path;
42
43use cursive_core::direction::{Absolute, Direction};
44use cursive_core::event::{AnyCb, Event, EventResult, Key, MouseButton, MouseEvent};
45use cursive_core::view::{CannotFocus, Selector, View, ViewNotFound};
46use cursive_core::{Printer, Vec2};
47pub use error::*;
48pub use id::Id;
49use node::Node;
50pub use path::Path;
51use std::collections::VecDeque;
52use std::convert::TryFrom;
53
54#[derive(Debug, PartialEq, Clone)]
55enum Orientation {
56    Vertical,
57    Horizontal,
58}
59
60/// View holding information and managing multiplexer.
61pub struct Mux {
62    tree: indextree::Arena<Node>,
63    root: indextree::NodeId,
64    default_split_ratio: f32,
65    focus: indextree::NodeId,
66    history: VecDeque<(indextree::NodeId, indextree::NodeId, Absolute)>,
67    history_length: usize,
68    invalidated: bool,
69    focus_up: Event,
70    focus_down: Event,
71    focus_left: Event,
72    focus_right: Event,
73    resize_left: Event,
74    resize_right: Event,
75    resize_up: Event,
76    resize_down: Event,
77    zoom: Event,
78    zoomed: bool,
79}
80
81impl View for Mux {
82    fn draw(&self, printer: &Printer) {
83        debug!("Current Focus: {}", self.focus);
84        debug!("Is the current pane focused? {}", self.zoomed);
85        // println!("Mux currently focused: {}", printer.focused);
86        if self.zoomed {
87            if let Some(focused) = self.tree.get(self.focus) {
88                focused.get().draw(printer);
89            }
90        } else {
91            self.rec_draw(printer, self.root)
92        }
93    }
94
95    fn needs_relayout(&self) -> bool {
96        self.invalidated
97    }
98
99    fn required_size(&mut self, constraint: Vec2) -> Vec2 {
100        constraint
101    }
102
103    fn layout(&mut self, constraint: Vec2) {
104        self.invalidated = false;
105        if self.zoomed {
106            if let Some(focused) = self.tree.get_mut(self.focus) {
107                focused.get_mut().layout_view(constraint);
108            }
109        } else {
110            self.rec_layout(self.root, constraint, Vec2::zero());
111        }
112    }
113
114    fn take_focus(&mut self, _source: Direction) -> Result<EventResult, CannotFocus> {
115        Ok(EventResult::consumed())
116    }
117
118    fn focus_view(&mut self, _: &Selector) -> Result<EventResult, ViewNotFound> {
119        Ok(EventResult::consumed())
120    }
121
122    fn call_on_any<'a>(&mut self, slct: &Selector, cb: AnyCb<'a>) {
123        let nodes: Vec<Id> = self.root.descendants(&self.tree).collect();
124        for node in nodes {
125            if let Some(node_c) = self.tree.get_mut(node) {
126                node_c.get_mut().call_on_any(slct, cb);
127            }
128        }
129    }
130
131    fn on_event(&mut self, evt: Event) -> EventResult {
132        // pre_check if focus has to be changed, we dont want views react to mouse click out of their reach
133        let mut result = EventResult::Ignored;
134        if let Event::Mouse {
135            offset,
136            position,
137            event: MouseEvent::Press(MouseButton::Left),
138        } = evt
139        {
140            if let Some(off_pos) = position.checked_sub(offset) {
141                if let Some(pane) = self.clicked_pane(off_pos) {
142                    if let Ok(res) = self.tree.get_mut(pane).unwrap().get_mut().take_focus() {
143                        if self.focus != pane {
144                            result = res;
145                            self.focus = pane;
146                            self.invalidated = true;
147                        }
148                    }
149                }
150            }
151        }
152        let result = result.and(
153            self.tree
154                .get_mut(self.focus)
155                .unwrap()
156                .get_mut()
157                .on_event(evt.clone(), self.zoomed),
158        );
159        match result {
160            EventResult::Ignored => match evt {
161                _ if self.focus_left == evt => self.move_focus(Absolute::Left),
162                _ if self.focus_right == evt => self.move_focus(Absolute::Right),
163                _ if self.focus_up == evt => self.move_focus(Absolute::Up),
164                _ if self.focus_down == evt => self.move_focus(Absolute::Down),
165                _ if self.resize_left == evt => self.resize(Absolute::Left),
166                _ if self.resize_right == evt => self.resize(Absolute::Right),
167                _ if self.resize_up == evt => self.resize(Absolute::Up),
168                _ if self.resize_down == evt => self.resize(Absolute::Down),
169                _ if self.zoom == evt => self.zoom_focus(),
170                _ => EventResult::Ignored,
171            },
172            result => result,
173        }
174    }
175}
176
177impl Mux {
178    /// # Example
179    /// ```
180    /// # extern crate cursive;
181    /// # fn main () {
182    /// let mut mux = cursive_multiplex::Mux::new();
183    /// # }
184    /// ```
185    pub fn new() -> Self {
186        let mut new_tree = indextree::Arena::new();
187        let new_root = new_tree.new_node(Node::new_empty(Orientation::Horizontal, 0.5));
188        Mux {
189            tree: new_tree,
190            root: new_root,
191            default_split_ratio: 0.5,
192            history: VecDeque::new(),
193            history_length: 50,
194            invalidated: true,
195            focus: new_root,
196            focus_up: Event::Alt(Key::Up),
197            focus_down: Event::Alt(Key::Down),
198            focus_left: Event::Alt(Key::Left),
199            focus_right: Event::Alt(Key::Right),
200            resize_left: Event::Ctrl(Key::Left),
201            resize_right: Event::Ctrl(Key::Right),
202            resize_up: Event::Ctrl(Key::Up),
203            resize_down: Event::Ctrl(Key::Down),
204            zoom: Event::CtrlChar('x'),
205            zoomed: false,
206        }
207    }
208
209    pub fn active_view(&self) -> Option<&dyn View> {
210        self.tree
211            .get(self.focus)
212            .map(|node| node.get())
213            .and_then(|node| node.view.as_deref())
214    }
215
216    pub fn active_view_mut(&mut self) -> Option<&mut dyn View> {
217        self.tree
218            .get_mut(self.focus)
219            .map(|node| node.get_mut())
220            .and_then(|node| node.view.as_deref_mut())
221    }
222
223    /// Chainable setter for the default split ratio.
224    /// Note: Only values from 0 to 1 are valid, if your value exceeds this range it will be clamped.
225    pub fn with_default_split_ratio<T: Into<f32>>(mut self, split: T) -> Self {
226        self.default_split_ratio = split.into().clamp(0.0, 1.0);
227        self.tree.get_mut(self.root).unwrap().get_mut().split_ratio = self.default_split_ratio;
228        self
229    }
230
231    /// Setter for the default split ratio.
232    /// Note: Only values from 0 to 1 are valid, if your value exceeds this range it will be clamped.
233    pub fn set_default_split_ratio<T: Into<f32>>(&mut self, split: T) {
234        self.default_split_ratio = split.into().clamp(0.0, 1.0);
235        self.tree.get_mut(self.root).unwrap().get_mut().split_ratio = self.default_split_ratio;
236    }
237
238    /// Chainable setter for action
239    pub fn with_move_focus_up(mut self, evt: Event) -> Self {
240        self.focus_up = evt;
241        self
242    }
243    /// Chainable setter for action
244    pub fn with_move_focus_down(mut self, evt: Event) -> Self {
245        self.focus_down = evt;
246        self
247    }
248    /// Chainable setter for action
249    pub fn with_move_focus_left(mut self, evt: Event) -> Self {
250        self.focus_left = evt;
251        self
252    }
253    /// Chainable setter for action
254    pub fn with_move_focus_right(mut self, evt: Event) -> Self {
255        self.focus_right = evt;
256        self
257    }
258    /// Chainable setter for action
259    pub fn with_resize_up(mut self, evt: Event) -> Self {
260        self.resize_up = evt;
261        self
262    }
263    /// Chainable setter for action
264    pub fn with_resize_down(mut self, evt: Event) -> Self {
265        self.resize_down = evt;
266        self
267    }
268    /// Chainable setter for action
269    pub fn with_resize_left(mut self, evt: Event) -> Self {
270        self.resize_left = evt;
271        self
272    }
273    /// Chainable setter for action
274    pub fn with_resize_right(mut self, evt: Event) -> Self {
275        self.resize_right = evt;
276        self
277    }
278
279    /// Chainable setter for action
280    pub fn with_zoom(mut self, evt: Event) -> Self {
281        self.zoom = evt;
282        self
283    }
284
285    /// Setter for action
286    pub fn set_move_focus_up(&mut self, evt: Event) {
287        self.focus_up = evt;
288    }
289    /// Setter for action
290    pub fn set_move_focus_down(&mut self, evt: Event) {
291        self.focus_down = evt;
292    }
293    /// Setter for action
294    pub fn set_move_focus_left(&mut self, evt: Event) {
295        self.focus_left = evt;
296    }
297    /// Setter for action
298    pub fn set_move_focus_right(&mut self, evt: Event) {
299        self.focus_right = evt;
300    }
301    /// Setter for action
302    pub fn set_resize_up(&mut self, evt: Event) {
303        self.resize_up = evt;
304    }
305    /// Setter for action
306    pub fn set_resize_down(&mut self, evt: Event) {
307        self.resize_down = evt;
308    }
309    /// Setter for action
310    pub fn set_resize_left(&mut self, evt: Event) {
311        self.resize_left = evt;
312    }
313    /// Setter for action
314    pub fn set_resize_right(&mut self, evt: Event) {
315        self.resize_right = evt;
316    }
317
318    /// Setter for action
319    pub fn set_zoom(&mut self, evt: Event) {
320        self.zoom = evt;
321    }
322
323    /// Chainable setter for the focus the mux should have
324    pub fn with_focus(mut self, id: Id) -> Self {
325        let nodes: Vec<Id> = self.root.descendants(&self.tree).collect();
326        if nodes.contains(&id) {
327            self.focus = id;
328            self.invalidated = true;
329        }
330        self
331    }
332
333    /// Setter for the focus the mux should have
334    pub fn set_focus(&mut self, id: Id) {
335        let nodes: Vec<Id> = self.root.descendants(&self.tree).collect();
336        if nodes.contains(&id) {
337            self.focus = id;
338            self.invalidated = true;
339        }
340    }
341
342    /// Returns the current focused view id.
343    /// By default the newest node added to the multiplexer gets focused.
344    /// Focus can also be changed by the user.
345    /// # Example
346    /// ```
347    /// # extern crate cursive;
348    /// # fn main () {
349    /// let mut mux = cursive_multiplex::Mux::new();
350    /// let node1 = mux.add_right_of(cursive::views::TextArea::new(), mux.root().build().unwrap()).unwrap();
351    /// let current_focus = mux.focus();
352    /// assert_eq!(current_focus, node1);
353    /// # }
354    /// ```
355    pub fn focus(&self) -> Id {
356        self.focus
357    }
358
359    fn rec_layout(&mut self, root: Id, constraint: Vec2, start_point: Vec2) {
360        match root.children(&self.tree).count() {
361            1 => self.rec_layout(
362                root.children(&self.tree).next().unwrap(),
363                constraint,
364                start_point,
365            ),
366            2 => {
367                let left = root.children(&self.tree).next().unwrap();
368                let right = root.children(&self.tree).last().unwrap();
369                let const1;
370                let const2;
371                let root_data = &self.tree.get(root).unwrap().get();
372                let orit = root_data.orientation.clone();
373                match orit {
374                    Orientation::Horizontal => {
375                        const1 = Vec2::new(
376                            Mux::add_offset(
377                                (constraint.x as f32 * root_data.split_ratio) as usize,
378                                root_data.split_ratio_offset,
379                            ),
380                            constraint.y,
381                        );
382                        const2 = Vec2::new(
383                            {
384                                let size = Mux::add_offset(
385                                    (constraint.x as f32 * root_data.split_ratio) as usize,
386                                    -root_data.split_ratio_offset,
387                                );
388                                if constraint.x % 2 == 0 {
389                                    match size.checked_sub(1) {
390                                        Some(res) => res,
391                                        None => size,
392                                    }
393                                } else {
394                                    size
395                                }
396                            },
397                            constraint.y,
398                        );
399                    }
400                    Orientation::Vertical => {
401                        const1 = Vec2::new(
402                            constraint.x,
403                            Mux::add_offset(
404                                (constraint.y as f32 * root_data.split_ratio) as usize,
405                                root_data.split_ratio_offset,
406                            ),
407                        );
408                        const2 = Vec2::new(constraint.x, {
409                            let size = Mux::add_offset(
410                                (constraint.y as f32 * root_data.split_ratio) as usize,
411                                -root_data.split_ratio_offset,
412                            );
413                            if constraint.y % 2 == 0 {
414                                match size.checked_sub(1) {
415                                    Some(res) => res,
416                                    None => size,
417                                }
418                            } else {
419                                size
420                            }
421                        });
422                    }
423                }
424                self.tree
425                    .get_mut(root)
426                    .unwrap()
427                    .get_mut()
428                    .layout_view(constraint);
429                self.rec_layout(left, const1, start_point);
430                self.rec_layout(
431                    right,
432                    const2,
433                    match orit {
434                        Orientation::Vertical => start_point + const1.keep_y(),
435                        Orientation::Horizontal => start_point + const1.keep_x(),
436                    },
437                );
438            }
439            0 => {
440                self.tree
441                    .get_mut(root)
442                    .unwrap()
443                    .get_mut()
444                    .layout_view(constraint);
445                self.tree
446                    .get_mut(root)
447                    .unwrap()
448                    .get_mut()
449                    .set_pos(start_point);
450            }
451            _ => debug!("Illegal Number of Child Nodes"),
452        }
453    }
454
455    fn add_offset(split: usize, offset: i16) -> usize {
456        if offset < 0 {
457            match usize::try_from(offset.abs()) {
458                Ok(u) => {
459                    if split < u {
460                        split
461                    } else {
462                        split - u
463                    }
464                }
465                Err(_) => split,
466            }
467        } else {
468            match usize::try_from(offset) {
469                Ok(u) => split + u,
470                Err(_) => split,
471            }
472        }
473    }
474
475    fn rec_draw(&self, printer: &Printer, root: Id) {
476        match root.children(&self.tree).count() {
477            1 => self.rec_draw(printer, root.children(&self.tree).next().unwrap()),
478            2 => {
479                debug!("Print Children Nodes");
480                let left = root.children(&self.tree).next().unwrap();
481                let right = root.children(&self.tree).last().unwrap();
482                let printer1;
483                let printer2;
484                let root_data = &self.tree.get(root).unwrap().get();
485                match root_data.orientation {
486                    Orientation::Horizontal => {
487                        printer1 = printer.cropped(Vec2::new(
488                            Mux::add_offset(
489                                (printer.size.x as f32 * root_data.split_ratio) as usize,
490                                root_data.split_ratio_offset,
491                            ),
492                            printer.size.y,
493                        ));
494                        printer2 = printer
495                            .offset(Vec2::new(
496                                Mux::add_offset(
497                                    (printer.size.x as f32 * root_data.split_ratio) as usize,
498                                    root_data.split_ratio_offset,
499                                ) + 1,
500                                0,
501                            ))
502                            .cropped(Vec2::new(
503                                Mux::add_offset(
504                                    (printer.size.x as f32 * root_data.split_ratio) as usize,
505                                    -root_data.split_ratio_offset,
506                                ),
507                                printer.size.y,
508                            ));
509                    }
510                    Orientation::Vertical => {
511                        printer1 = printer.cropped(Vec2::new(
512                            printer.size.x,
513                            Mux::add_offset(
514                                (printer.size.y as f32 * root_data.split_ratio) as usize,
515                                root_data.split_ratio_offset,
516                            ),
517                        ));
518                        printer2 = printer
519                            .offset(Vec2::new(
520                                0,
521                                Mux::add_offset(
522                                    (printer.size.y as f32 * root_data.split_ratio) as usize,
523                                    root_data.split_ratio_offset,
524                                ) + 1,
525                            ))
526                            .cropped(Vec2::new(
527                                printer.size.x,
528                                Mux::add_offset(
529                                    (printer.size.y as f32 * root_data.split_ratio) as usize,
530                                    -root_data.split_ratio_offset,
531                                ),
532                            ));
533                    }
534                }
535                match self.tree.get(root).unwrap().get().orientation {
536                    Orientation::Vertical => {
537                        if printer.size.y > 1 {
538                            printer.print_hline(
539                                Vec2::new(
540                                    0,
541                                    Mux::add_offset(
542                                        (printer.size.y as f32 * root_data.split_ratio) as usize,
543                                        root_data.split_ratio_offset,
544                                    ),
545                                ),
546                                printer.size.x,
547                                "─",
548                            );
549                        }
550                    }
551                    Orientation::Horizontal => {
552                        if printer.size.x > 1 {
553                            printer.print_vline(
554                                Vec2::new(
555                                    Mux::add_offset(
556                                        (printer.size.x as f32 * root_data.split_ratio) as usize,
557                                        root_data.split_ratio_offset,
558                                    ),
559                                    0,
560                                ),
561                                printer.size.y,
562                                "│",
563                            );
564                        }
565                    }
566                }
567                self.rec_draw(&printer1, left);
568                self.rec_draw(&printer2, right);
569            }
570            0 => {
571                self.tree
572                    .get(root)
573                    .unwrap()
574                    .get()
575                    .draw(&printer.focused(self.focus == root));
576            }
577            _ => debug!("Illegal Number of Child Nodes"),
578        }
579    }
580}
581
582impl Default for Mux {
583    fn default() -> Self {
584        Mux::new()
585    }
586}
587
588#[cfg(test)]
589mod tree {
590    use super::Mux;
591    use cursive_core::event::{Event, EventResult, Key};
592    use cursive_core::traits::View;
593    use cursive_core::views::DummyView;
594
595    #[test]
596    fn test_remove() {
597        // General Remove test
598        let mut test_mux = Mux::new();
599        let node1 = test_mux.add_below(DummyView, test_mux.root).unwrap();
600        let node2 = test_mux.add_below(DummyView, node1).unwrap();
601        let node3 = test_mux.add_below(DummyView, node2).unwrap();
602
603        print_tree(&test_mux);
604        test_mux.remove_id(node3).unwrap();
605        print_tree(&test_mux);
606        match test_mux.remove_id(node3) {
607            Ok(_) => {
608                print_tree(&test_mux);
609                println!("Delete should have removed: {}", node3);
610                assert!(false);
611            }
612            Err(_) => {}
613        }
614    }
615
616    #[test]
617    fn test_switch() {
618        let mut mux = Mux::new();
619        let node1 = mux.add_right_of(DummyView, mux.root).unwrap();
620        let node2 = mux.add_right_of(DummyView, node1).unwrap();
621        let node3 = mux.add_left_of(DummyView, node2).unwrap();
622
623        mux.switch_views(node1, node3).unwrap();
624    }
625
626    #[test]
627    fn test_zoom() {
628        let mut mux = Mux::new();
629        let node1 = mux.add_right_of(DummyView, mux.root).unwrap();
630        let node2 = mux.add_right_of(DummyView, node1).unwrap();
631        let _node3 = mux.add_left_of(DummyView, node2).unwrap();
632
633        match mux.on_event(mux.zoom.clone()) {
634            EventResult::Consumed(_) => {}
635            EventResult::Ignored => assert!(false),
636        }
637    }
638
639    #[test]
640    fn test_nesting() {
641        println!("Nesting Test");
642
643        let mut mux = Mux::new();
644
645        let mut nodes = Vec::new();
646
647        for _ in 0..10 {
648            print_tree(&mux);
649            match mux.add_right_of(
650                DummyView,
651                if let Some(x) = nodes.last() {
652                    *x
653                } else {
654                    mux.root
655                },
656            ) {
657                Ok(node) => {
658                    nodes.push(node);
659                }
660                Err(_) => {
661                    assert!(false);
662                }
663            }
664            match mux.add_right_of(DummyView, *nodes.last().unwrap()) {
665                Ok(node) => {
666                    nodes.push(node);
667                }
668                Err(_) => {
669                    assert!(false);
670                }
671            }
672        }
673
674        for node in nodes.iter() {
675            mux.focus = *node;
676            direction_test(&mut mux);
677        }
678    }
679
680    fn print_tree(mux: &Mux) {
681        print!("Current Tree: ");
682        for node in mux.root.descendants(&mux.tree) {
683            print!("{},", node);
684        }
685        println!();
686    }
687
688    fn direction_test(mux: &mut Mux) {
689        // This is a shotgun approach to have a look if any unforeseen focus moves could happen, resulting in a uncertain state
690        mux.on_event(Event::Key(Key::Up));
691        mux.on_event(Event::Key(Key::Left));
692        mux.on_event(Event::Key(Key::Down));
693        mux.on_event(Event::Key(Key::Right));
694        mux.on_event(Event::Key(Key::Up));
695        mux.on_event(Event::Key(Key::Left));
696        mux.on_event(Event::Key(Key::Left));
697        mux.on_event(Event::Key(Key::Down));
698        mux.on_event(Event::Key(Key::Right));
699        mux.on_event(Event::Key(Key::Up));
700        mux.on_event(Event::Key(Key::Left));
701    }
702}