feat: scroll independently from selection
This commit is contained in:
+24
-3
@@ -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
@@ -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());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user