feat: scroll independently from selection

This commit is contained in:
EdJoPaTo
2024-02-03 02:24:44 +01:00
parent 14b9e42cb2
commit 01726df5ed
2 changed files with 53 additions and 11 deletions
+24 -3
View File
@@ -44,6 +44,19 @@ impl<'a> App<'a> {
) )
.expect("all item identifiers are unique"), .expect("all item identifiers are unique"),
TreeItem::new_leaf("h", "Hotel"), 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<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result<(
f.render_stateful_widget(items, area, &mut app.state); f.render_stateful_widget(items, area, &mut app.state);
})?; })?;
if let Event::Key(key) = event::read()? { match event::read()? {
match key.code { Event::Key(key) => match key.code {
KeyCode::Char('q') => return Ok(()), KeyCode::Char('q') => return Ok(()),
KeyCode::Char('\n' | ' ') => app.state.toggle_selected(), KeyCode::Char('\n' | ' ') => app.state.toggle_selected(),
KeyCode::Left => app.state.key_left(), KeyCode::Left => app.state.key_left(),
@@ -109,8 +122,16 @@ fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result<(
KeyCode::End => { KeyCode::End => {
app.state.select_last(&app.items); 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),
_ => {}
},
_ => {} _ => {}
} }
} }
} }
}
+29 -8
View File
@@ -41,6 +41,7 @@ pub struct TreeState<Identifier> {
offset: usize, offset: usize,
opened: HashSet<Vec<Identifier>>, opened: HashSet<Vec<Identifier>>,
selected: Vec<Identifier>, selected: Vec<Identifier>,
ensure_selected_in_view_on_next_render: bool,
} }
impl<Identifier> TreeState<Identifier> impl<Identifier> TreeState<Identifier>
@@ -85,12 +86,7 @@ where
pub fn select(&mut self, identifier: Vec<Identifier>) -> bool { pub fn select(&mut self, identifier: Vec<Identifier>) -> bool {
let changed = self.selected != identifier; let changed = self.selected != identifier;
self.selected = identifier; self.selected = identifier;
self.ensure_selected_in_view_on_next_render = true;
// TODO: ListState does this. Is this relevant?
if self.selected.is_empty() {
self.offset = 0;
}
changed changed
} }
@@ -126,6 +122,7 @@ where
/// See also [`toggle`](TreeState::toggle) /// See also [`toggle`](TreeState::toggle)
pub fn toggle_selected(&mut self) { pub fn toggle_selected(&mut self) {
self.toggle(self.selected()); self.toggle(self.selected());
self.ensure_selected_in_view_on_next_render = true;
} }
pub fn close_all(&mut self) { pub fn close_all(&mut self) {
@@ -210,6 +207,21 @@ where
self.select(new_identifier) 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. /// Handles the up arrow key.
/// Moves up in the current depth or to its parent. /// Moves up in the current depth or to its parent.
pub fn key_up(&mut self, items: &[TreeItem<Identifier>]) { pub fn key_up(&mut self, items: &[TreeItem<Identifier>]) {
@@ -235,12 +247,14 @@ where
// Select the parent by removing the leaf from selection // Select the parent by removing the leaf from selection
self.selected.pop(); self.selected.pop();
} }
self.ensure_selected_in_view_on_next_render = true;
} }
/// Handles the right arrow key. /// Handles the right arrow key.
/// Opens the currently selected. /// Opens the currently selected.
pub fn key_right(&mut self) { pub fn key_right(&mut self) {
self.open(self.selected()); self.open(self.selected());
self.ensure_selected_in_view_on_next_render = true;
} }
} }
@@ -579,7 +593,13 @@ where
.unwrap_or(0) .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 end = start;
let mut height = 0; let mut height = 0;
for item in visible.iter().skip(start) { for item in visible.iter().skip(start) {
@@ -591,7 +611,7 @@ where
end += 1; 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()); height = height.saturating_add(visible[end].item.height());
end += 1; end += 1;
while height > available_height { while height > available_height {
@@ -601,6 +621,7 @@ where
} }
state.offset = start; state.offset = start;
state.ensure_selected_in_view_on_next_render = false;
let blank_symbol = " ".repeat(self.highlight_symbol.width()); let blank_symbol = " ".repeat(self.highlight_symbol.width());