Move cursor so it is visible when scrolling
This commit is contained in:
parent
76bcd853cf
commit
c11325aa8b
4 changed files with 87 additions and 17 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::{vec_deque, VecDeque};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use toss::frame::Frame;
|
use toss::frame::Frame;
|
||||||
|
|
@ -66,6 +66,10 @@ impl<I> Blocks<I> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> vec_deque::Iter<Block<I>> {
|
||||||
|
self.blocks.iter()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn offset(&mut self, delta: i32) {
|
pub fn offset(&mut self, delta: i32) {
|
||||||
self.top_line += delta;
|
self.top_line += delta;
|
||||||
self.bottom_line += delta;
|
self.bottom_line += delta;
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,7 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
KeyCode::Char('j') | KeyCode::Down => self.move_cursor_down().await,
|
KeyCode::Char('j') | KeyCode::Down => self.move_cursor_down().await,
|
||||||
KeyCode::Char('g') | KeyCode::Home => self.move_cursor_to_top().await,
|
KeyCode::Char('g') | KeyCode::Home => self.move_cursor_to_top().await,
|
||||||
KeyCode::Char('G') | KeyCode::End => self.move_cursor_to_bottom().await,
|
KeyCode::Char('G') | KeyCode::End => self.move_cursor_to_bottom().await,
|
||||||
KeyCode::Char('y') if event.modifiers == KeyModifiers::CONTROL => {
|
KeyCode::Char('y') if event.modifiers == KeyModifiers::CONTROL => self.scroll_up(1),
|
||||||
self.scroll_up(1).await
|
|
||||||
}
|
|
||||||
KeyCode::Char('e') if event.modifiers == KeyModifiers::CONTROL => self.scroll_down(1),
|
KeyCode::Char('e') if event.modifiers == KeyModifiers::CONTROL => self.scroll_down(1),
|
||||||
_ => return false,
|
_ => return false,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -224,17 +224,8 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
self.make_cursor_visible = true;
|
self.make_cursor_visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn scroll_up(&mut self, amount: i32) {
|
pub fn scroll_up(&mut self, amount: i32) {
|
||||||
self.scroll += amount;
|
self.scroll += amount;
|
||||||
|
|
||||||
if let Cursor::Bottom = self.cursor {
|
|
||||||
// Move cursor to bottommost message, if it exists
|
|
||||||
if let Some(mut id) = self.store.last_tree_id().await {
|
|
||||||
let tree = self.store.tree(&id).await;
|
|
||||||
while Self::find_last_child(&tree, &mut id) {}
|
|
||||||
self.cursor = Cursor::Msg(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_down(&mut self, amount: i32) {
|
pub fn scroll_down(&mut self, amount: i32) {
|
||||||
|
|
|
||||||
|
|
@ -308,6 +308,70 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to obtain a [`Cursor::Msg`] pointing to the block.
|
||||||
|
fn msg_id(block: &Block<BlockId<M::Id>>) -> Option<M::Id> {
|
||||||
|
match &block.id {
|
||||||
|
BlockId::Msg(id) => Some(id.clone()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visible(block: &Block<BlockId<M::Id>>, height: i32) -> bool {
|
||||||
|
(1 - block.height..height).contains(&block.top_line)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_cursor_so_it_is_visible(
|
||||||
|
&mut self,
|
||||||
|
frame: &mut Frame,
|
||||||
|
blocks: &TreeBlocks<M::Id>,
|
||||||
|
) -> Option<M::Id> {
|
||||||
|
if !matches!(self.cursor, Cursor::Bottom | Cursor::Msg(_)) {
|
||||||
|
// In all other cases, there is no need to make the cursor visible
|
||||||
|
// since scrolling behaves differently enough.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let height = frame.size().height as i32;
|
||||||
|
|
||||||
|
let new_cursor = if matches!(self.cursor, Cursor::Bottom) {
|
||||||
|
blocks
|
||||||
|
.blocks()
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.filter(|b| Self::visible(b, height))
|
||||||
|
.find_map(Self::msg_id)
|
||||||
|
} else {
|
||||||
|
let block = blocks
|
||||||
|
.blocks()
|
||||||
|
.find(&BlockId::from_cursor(&self.cursor))
|
||||||
|
.expect("no cursor found");
|
||||||
|
|
||||||
|
if Self::visible(block, height) {
|
||||||
|
return None;
|
||||||
|
} else if block.top_line < 0 {
|
||||||
|
blocks
|
||||||
|
.blocks()
|
||||||
|
.iter()
|
||||||
|
.filter(|b| Self::visible(b, height))
|
||||||
|
.find_map(Self::msg_id)
|
||||||
|
} else {
|
||||||
|
blocks
|
||||||
|
.blocks()
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.filter(|b| Self::visible(b, height))
|
||||||
|
.find_map(Self::msg_id)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(id) = new_cursor {
|
||||||
|
self.cursor = Cursor::Msg(id.clone());
|
||||||
|
Some(id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn relayout(&mut self, frame: &mut Frame) -> TreeBlocks<M::Id> {
|
pub async fn relayout(&mut self, frame: &mut Frame) -> TreeBlocks<M::Id> {
|
||||||
// The basic idea is this:
|
// The basic idea is this:
|
||||||
//
|
//
|
||||||
|
|
@ -350,9 +414,22 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
self.fill_screen_and_clamp_scrolling(frame, &mut blocks)
|
self.fill_screen_and_clamp_scrolling(frame, &mut blocks)
|
||||||
.await;
|
.await;
|
||||||
} else {
|
} else {
|
||||||
// self.move_cursor_so_it_is_visible(&mut blocks); // TODO
|
let new_cursor_msg_id = self.move_cursor_so_it_is_visible(frame, &blocks);
|
||||||
self.fill_screen_and_clamp_scrolling(frame, &mut blocks)
|
if let Some(cursor_msg_id) = new_cursor_msg_id {
|
||||||
.await;
|
// Moving the cursor invalidates our current blocks, so we sadly
|
||||||
|
// have to either perform an expensive operation or redraw the
|
||||||
|
// entire thing. I'm choosing the latter for now.
|
||||||
|
|
||||||
|
self.last_cursor = self.cursor.clone();
|
||||||
|
self.last_cursor_line = self.cursor_line(&blocks);
|
||||||
|
self.make_cursor_visible = false;
|
||||||
|
self.scroll = 0;
|
||||||
|
|
||||||
|
let last_cursor_path = self.store.path(&cursor_msg_id).await;
|
||||||
|
blocks = self.layout_last_cursor_seed(frame, &last_cursor_path).await;
|
||||||
|
self.fill_screen_and_clamp_scrolling(frame, &mut blocks)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.last_cursor = self.cursor.clone();
|
self.last_cursor = self.cursor.clone();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue