Skip to main content

cursive_tree/view/
view.rs

1use super::{
2    super::{backend::*, model::*},
3    tree::*,
4};
5
6use cursive::{direction::*, event::*, theme::*, view::*, *};
7
8const TYPE_NAME: &str = "TreeView";
9
10impl<BackendT> View for TreeView<BackendT>
11where
12    BackendT: 'static + TreeBackend + Send + Sync,
13    BackendT::Context: Clone + Send + Sync,
14    BackendT::Error: Send + Sync,
15    BackendT::ID: Send + Sync,
16    BackendT::Data: Send + Sync,
17{
18    fn type_name(&self) -> &'static str {
19        TYPE_NAME
20    }
21
22    fn take_focus(&mut self, _source: Direction) -> Result<EventResult, CannotFocus> {
23        if self.model.is_empty() { Err(CannotFocus) } else { Ok(EventResult::consumed()) }
24    }
25
26    fn required_size(&mut self, _constraint: Vec2) -> Vec2 {
27        self.model.extents()
28    }
29
30    fn important_area(&self, view_size: Vec2) -> Rect {
31        if let Some(selected_row) = self.selected_row {
32            Rect::from_size((0, selected_row), (view_size.x, 1))
33        } else {
34            Rect::from_size((0, 0), view_size)
35        }
36    }
37
38    fn on_event(&mut self, event: Event) -> EventResult {
39        let selected_row = self.selected_row;
40
41        match event {
42            Event::Key(Key::Up) => self.move_selection(-1),
43            Event::Key(Key::Down) => self.move_selection(1),
44            Event::Key(Key::PageUp) => self.move_selection(-(self.page as isize)),
45            Event::Key(Key::PageDown) => self.move_selection(self.page as isize),
46            Event::Key(Key::Home) => self.select_top(),
47            Event::Key(Key::End) => self.select_bottom(),
48
49            Event::Key(Key::Left) => {
50                if let Some(node) = self.selected_node_mut() {
51                    node.collapse(Some(1));
52                }
53            }
54
55            Event::Key(Key::Right) => {
56                let context = self.model.context.clone();
57                if let Some(node) = self.selected_node_mut() {
58                    return Self::on_expand_branch(node, Some(1), context);
59                }
60            }
61
62            Event::Char('-') => {
63                if let Some(node) = self.selected_node_mut() {
64                    node.collapse(None);
65                }
66            }
67
68            Event::Char('+') => {
69                let context = self.model.context.clone();
70                if let Some(node) = self.selected_node_mut() {
71                    return Self::on_expand_branch(node, None, context);
72                }
73            }
74
75            Event::Key(Key::Enter) => {
76                let context = self.model.context.clone();
77                if let Some(node) = self.selected_node_mut() {
78                    return Self::on_toggle_branch_state(node, context);
79                }
80            }
81
82            Event::Mouse { offset, position, event: MouseEvent::Press(_) } => {
83                self.selected_row = if let Some(position) = position.checked_sub(offset)
84                    && let Some(node) = self.model.at_row(position.y)
85                {
86                    if node.kind.is_branch() && (position.x < node.depth * 2 + 2) {
87                        // If we click to the left of the representation then toggle state
88                        let context = self.model.context.clone();
89                        if let Some(path) = self.model.path(node)
90                            && let Some(node) = self.model.at_path_mut(path)
91                        {
92                            return Self::on_toggle_branch_state(node, context);
93                        } else {
94                            None
95                        }
96                    } else {
97                        // Otherwise just select
98                        Some(position.y)
99                    }
100                } else {
101                    None
102                };
103            }
104
105            _ => return EventResult::Ignored,
106        }
107
108        EventResult::Consumed(if self.selected_row != selected_row {
109            // Selection has changed
110            Some(Callback::from_fn_once(|cursive| {
111                BackendT::handle_selection_changed(cursive);
112            }))
113        } else {
114            None
115        })
116    }
117
118    fn draw(&self, printer: &Printer) {
119        let mut start = Vec2::default();
120        for node in self.model.iter(true) {
121            if start.y >= printer.offset.y + printer.size.y {
122                // We're beyond the print area
123                break;
124            }
125
126            // Start printing only when we're within the print area
127            if start.y >= printer.content_offset.y {
128                // Symbol
129
130                start.x = node.depth * 2;
131                match node.symbol(self.model.context.clone()) {
132                    Symbol::Representation(symbol) => printer.print_styled(start, &symbol),
133                    Symbol::String(symbol) => printer.print(start, symbol),
134                };
135                start.x += 2;
136
137                // Representation
138
139                let mut representation = &node.representation;
140
141                let debug_representation = if self.debug
142                    && let Some(path) = self.model.path(node)
143                {
144                    let path: Vec<_> = path.into_iter().map(|i| i.to_string()).collect();
145                    let mut representation = representation.clone();
146                    representation.append(format!(" {}", path.join(".")));
147                    Some(representation)
148                } else {
149                    None
150                };
151
152                if let Some(debug_representation) = &debug_representation {
153                    representation = debug_representation;
154                }
155
156                let highlight = if self.is_selected(start.y) {
157                    Some(if printer.focused { PaletteStyle::Highlight } else { PaletteStyle::HighlightInactive })
158                } else {
159                    None
160                };
161
162                let print = |printer: &Printer| printer.print_styled(start, representation);
163
164                match highlight {
165                    Some(highlight) => printer.with_style(highlight, print),
166                    None => print(printer),
167                }
168            }
169
170            start.y += node.representation_size.y;
171        }
172    }
173}