Center cursor on screen
This commit is contained in:
parent
a4b79d4e81
commit
5acb4c6396
4 changed files with 41 additions and 1 deletions
|
|
@ -17,10 +17,11 @@ Procedure when bumping the version number:
|
||||||
### Added
|
### Added
|
||||||
- New messages are now marked as unseen
|
- New messages are now marked as unseen
|
||||||
- Sub-trees can now be folded
|
- Sub-trees can now be folded
|
||||||
|
- More readline-esque editor key bindings
|
||||||
- Key bindings to move to prev/next sibling
|
- Key bindings to move to prev/next sibling
|
||||||
|
- Key binding to center cursor on screen
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Improved editor key bindings
|
|
||||||
- Slowed down room history download speed
|
- Slowed down room history download speed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ use super::{ChatMsg, Reaction};
|
||||||
enum Correction {
|
enum Correction {
|
||||||
MakeCursorVisible,
|
MakeCursorVisible,
|
||||||
MoveCursorToVisibleArea,
|
MoveCursorToVisibleArea,
|
||||||
|
CenterCursor,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InnerTreeViewState<M: Msg, S: MsgStore<M>> {
|
struct InnerTreeViewState<M: Msg, S: MsgStore<M>> {
|
||||||
|
|
@ -75,6 +76,7 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
bindings.binding("ctrl+y/e", "scroll up/down a line");
|
bindings.binding("ctrl+y/e", "scroll up/down a line");
|
||||||
bindings.binding("ctrl+u/d", "scroll up/down half a screen");
|
bindings.binding("ctrl+u/d", "scroll up/down half a screen");
|
||||||
bindings.binding("ctrl+b/f", "scroll up/down one screen");
|
bindings.binding("ctrl+b/f", "scroll up/down one screen");
|
||||||
|
bindings.binding("z", "center cursor on screen");
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_movement_key_event(&mut self, frame: &mut Frame, event: KeyEvent) -> bool {
|
async fn handle_movement_key_event(&mut self, frame: &mut Frame, event: KeyEvent) -> bool {
|
||||||
|
|
@ -97,6 +99,7 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
key!(Ctrl + 'd') => self.scroll_down((chat_height / 2).into()),
|
key!(Ctrl + 'd') => self.scroll_down((chat_height / 2).into()),
|
||||||
key!(Ctrl + 'b') => self.scroll_up(chat_height.saturating_sub(1).into()),
|
key!(Ctrl + 'b') => self.scroll_up(chat_height.saturating_sub(1).into()),
|
||||||
key!(Ctrl + 'f') => self.scroll_down(chat_height.saturating_sub(1).into()),
|
key!(Ctrl + 'f') => self.scroll_down(chat_height.saturating_sub(1).into()),
|
||||||
|
key!('z') => self.center_cursor(),
|
||||||
_ => return false,
|
_ => return false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -379,6 +379,10 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
self.correction = Some(Correction::MoveCursorToVisibleArea);
|
self.correction = Some(Correction::MoveCursorToVisibleArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn center_cursor(&mut self) {
|
||||||
|
self.correction = Some(Correction::CenterCursor);
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn parent_for_normal_reply(&self) -> Option<Option<M::Id>> {
|
pub async fn parent_for_normal_reply(&self) -> Option<Option<M::Id>> {
|
||||||
match &self.cursor {
|
match &self.cursor {
|
||||||
Cursor::Bottom => Some(None),
|
Cursor::Bottom => Some(None),
|
||||||
|
|
|
||||||
|
|
@ -368,6 +368,33 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn scroll_so_cursor_is_centered(&self, frame: &mut Frame, blocks: &mut TreeBlocks<M::Id>) {
|
||||||
|
if matches!(self.cursor, Cursor::Bottom) {
|
||||||
|
return; // Cursor is locked to bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = blocks
|
||||||
|
.blocks()
|
||||||
|
.find(&BlockId::from_cursor(&self.cursor))
|
||||||
|
.expect("no cursor found");
|
||||||
|
|
||||||
|
let height = frame.size().height as i32;
|
||||||
|
let scrolloff = scrolloff(height);
|
||||||
|
|
||||||
|
let min_line = -block.focus.start + scrolloff;
|
||||||
|
let max_line = height - block.focus.end - scrolloff;
|
||||||
|
|
||||||
|
// If the message is higher than the available space, the top of the
|
||||||
|
// message should always be visible. I'm not using top_line.clamp(...)
|
||||||
|
// because the order of the min and max matters.
|
||||||
|
let top_line = block.top_line;
|
||||||
|
let new_top_line = (height - block.height) / 2;
|
||||||
|
let new_top_line = new_top_line.min(max_line).max(min_line);
|
||||||
|
if new_top_line != top_line {
|
||||||
|
blocks.blocks_mut().offset(new_top_line - top_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Try to obtain a [`Cursor::Msg`] pointing to the block.
|
/// Try to obtain a [`Cursor::Msg`] pointing to the block.
|
||||||
fn msg_id(block: &Block<BlockId<M::Id>>) -> Option<M::Id> {
|
fn msg_id(block: &Block<BlockId<M::Id>>) -> Option<M::Id> {
|
||||||
match &block.id {
|
match &block.id {
|
||||||
|
|
@ -518,6 +545,11 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(Correction::CenterCursor) => {
|
||||||
|
self.scroll_so_cursor_is_centered(frame, &mut blocks);
|
||||||
|
self.fill_screen_and_clamp_scrolling(nick, frame, &mut blocks)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue