diff --git a/examples/example.rs b/examples/example.rs index 28836b9..b2be042 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -44,6 +44,19 @@ impl<'a> App<'a> { ) .expect("all item identifiers are unique"), TreeItem::new_leaf("h", "Hotel"), + TreeItem::new( + "i", + "India", + vec![ + TreeItem::new_leaf("j", "Juliett"), + TreeItem::new_leaf("k", "Kilo"), + TreeItem::new_leaf("l", "Lima"), + TreeItem::new_leaf("m", "Mike"), + TreeItem::new_leaf("n", "November"), + ], + ) + .expect("all item identifiers are unique"), + TreeItem::new_leaf("o", "Oscar"), ], } } @@ -95,8 +108,8 @@ fn run_app(terminal: &mut Terminal, mut app: App) -> io::Result<( f.render_stateful_widget(items, area, &mut app.state); })?; - if let Event::Key(key) = event::read()? { - match key.code { + match event::read()? { + Event::Key(key) => match key.code { KeyCode::Char('q') => return Ok(()), KeyCode::Char('\n' | ' ') => app.state.toggle_selected(), KeyCode::Left => app.state.key_left(), @@ -109,8 +122,16 @@ fn run_app(terminal: &mut Terminal, mut app: App) -> io::Result<( KeyCode::End => { app.state.select_last(&app.items); } + KeyCode::PageDown => app.state.scroll_down(3), + KeyCode::PageUp => app.state.scroll_up(3), _ => {} - } + }, + Event::Mouse(mouse) => match mouse.kind { + event::MouseEventKind::ScrollDown => app.state.scroll_down(1), + event::MouseEventKind::ScrollUp => app.state.scroll_up(1), + _ => {} + }, + _ => {} } } } diff --git a/src/lib.rs b/src/lib.rs index 7400669..cfbd592 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,7 @@ pub struct TreeState { offset: usize, opened: HashSet>, selected: Vec, + ensure_selected_in_view_on_next_render: bool, } impl TreeState @@ -85,12 +86,7 @@ where pub fn select(&mut self, identifier: Vec) -> bool { let changed = self.selected != identifier; self.selected = identifier; - - // TODO: ListState does this. Is this relevant? - if self.selected.is_empty() { - self.offset = 0; - } - + self.ensure_selected_in_view_on_next_render = true; changed } @@ -126,6 +122,7 @@ where /// See also [`toggle`](TreeState::toggle) pub fn toggle_selected(&mut self) { self.toggle(self.selected()); + self.ensure_selected_in_view_on_next_render = true; } pub fn close_all(&mut self) { @@ -210,6 +207,21 @@ where self.select(new_identifier) } + /// Ensure the selected [`TreeItem`] is visible on next render + pub fn scroll_selected_into_view(&mut self) { + self.ensure_selected_in_view_on_next_render = true; + } + + /// Scroll the specified amount of lines up + pub fn scroll_up(&mut self, lines: usize) { + self.offset = self.offset.saturating_sub(lines); + } + + /// Scroll the specified amount of lines down + pub fn scroll_down(&mut self, lines: usize) { + self.offset = self.offset.saturating_add(lines); + } + /// Handles the up arrow key. /// Moves up in the current depth or to its parent. pub fn key_up(&mut self, items: &[TreeItem]) { @@ -235,12 +247,14 @@ where // Select the parent by removing the leaf from selection self.selected.pop(); } + self.ensure_selected_in_view_on_next_render = true; } /// Handles the right arrow key. /// Opens the currently selected. pub fn key_right(&mut self) { self.open(self.selected()); + self.ensure_selected_in_view_on_next_render = true; } } @@ -579,7 +593,13 @@ where .unwrap_or(0) }; - let mut start = state.offset.min(selected_index); + // Ensure last line is still visible + let mut start = state.offset.min(visible.len().saturating_sub(1)); + + if state.ensure_selected_in_view_on_next_render { + start = start.min(selected_index); + } + let mut end = start; let mut height = 0; for item in visible.iter().skip(start) { @@ -591,7 +611,7 @@ where end += 1; } - while selected_index >= end { + while state.ensure_selected_in_view_on_next_render && selected_index >= end { height = height.saturating_add(visible[end].item.height()); end += 1; while height > available_height { @@ -601,6 +621,7 @@ where } state.offset = start; + state.ensure_selected_in_view_on_next_render = false; let blank_symbol = " ".repeat(self.highlight_symbol.width());