From 816bf5be1c146f7f623c6ab6eecc7c3759ee6d07 Mon Sep 17 00:00:00 2001 From: Joscha Date: Mon, 1 Aug 2022 22:51:40 +0200 Subject: [PATCH] Fix cursor appearing on rerender when at bottom When sitting at the bottom of a room with cursor = Cursor::Bottom, a rerender would make the cursor jump to the lowest visible message. This of course should only happen when the screen is scrolled, not on almost every rerender. --- src/ui/chat/tree.rs | 12 ++++++----- src/ui/chat/tree/cursor.rs | 12 ++++++----- src/ui/chat/tree/layout.rs | 44 +++++++++++++++++++++----------------- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/ui/chat/tree.rs b/src/ui/chat/tree.rs index b2916d5..7b5a440 100644 --- a/src/ui/chat/tree.rs +++ b/src/ui/chat/tree.rs @@ -24,6 +24,11 @@ use super::ChatMsg; // State // /////////// +enum Correction { + MakeCursorVisible, + MoveCursorToVisibleArea, +} + struct InnerTreeViewState> { store: S, @@ -31,14 +36,11 @@ struct InnerTreeViewState> { last_cursor_line: i32, cursor: Cursor, - /// Set to true if the chat should be scrolled such that the cursor is fully - /// visible (if possible). If set to false, then the cursor itself is moved - /// to a different message such that it remains visible. - make_cursor_visible: bool, /// Scroll the view on the next render. Positive values scroll up and /// negative values scroll down. scroll: i32, + correction: Option, editor: EditorState, } @@ -50,8 +52,8 @@ impl> InnerTreeViewState { last_cursor: Cursor::Bottom, last_cursor_line: 0, cursor: Cursor::Bottom, - make_cursor_visible: false, scroll: 0, + correction: None, editor: EditorState::new(), } } diff --git a/src/ui/chat/tree/cursor.rs b/src/ui/chat/tree/cursor.rs index 48d3fd8..0febfca 100644 --- a/src/ui/chat/tree/cursor.rs +++ b/src/ui/chat/tree/cursor.rs @@ -2,7 +2,7 @@ use crate::store::{Msg, MsgStore, Tree}; -use super::InnerTreeViewState; +use super::{Correction, InnerTreeViewState}; #[derive(Debug, Clone, Copy)] pub enum Cursor { @@ -177,7 +177,7 @@ impl> InnerTreeViewState { self.cursor = Cursor::Msg(id); } } - self.make_cursor_visible = true; + self.correction = Some(Correction::MakeCursorVisible); } pub async fn move_cursor_down(&mut self) { @@ -208,28 +208,30 @@ impl> InnerTreeViewState { } _ => {} } - self.make_cursor_visible = true; + self.correction = Some(Correction::MakeCursorVisible); } pub async fn move_cursor_to_top(&mut self) { if let Some(first_tree_id) = self.store.first_tree_id().await { self.cursor = Cursor::Msg(first_tree_id); - self.make_cursor_visible = true; + self.correction = Some(Correction::MakeCursorVisible); } } pub async fn move_cursor_to_bottom(&mut self) { self.cursor = Cursor::Bottom; // Not really necessary; only here for consistency with other methods - self.make_cursor_visible = true; + self.correction = Some(Correction::MakeCursorVisible); } pub fn scroll_up(&mut self, amount: i32) { self.scroll += amount; + self.correction = Some(Correction::MoveCursorToVisibleArea); } pub fn scroll_down(&mut self, amount: i32) { self.scroll -= amount; + self.correction = Some(Correction::MoveCursorToVisibleArea); } } diff --git a/src/ui/chat/tree/layout.rs b/src/ui/chat/tree/layout.rs index 3ed353d..d8ddb37 100644 --- a/src/ui/chat/tree/layout.rs +++ b/src/ui/chat/tree/layout.rs @@ -7,7 +7,7 @@ use crate::ui::widgets::text::Text; use crate::ui::ChatMsg; use super::tree_blocks::{BlockId, Root, TreeBlocks}; -use super::{widgets, Cursor, InnerTreeViewState}; +use super::{widgets, Correction, Cursor, InnerTreeViewState}; impl> InnerTreeViewState { async fn cursor_path(&self, cursor: &Cursor) -> Path { @@ -410,33 +410,37 @@ impl> InnerTreeViewState { .await; } - if self.make_cursor_visible { - self.scroll_so_cursor_is_visible(frame, &mut blocks); - self.fill_screen_and_clamp_scrolling(frame, &mut blocks) - .await; - } else { - let new_cursor_msg_id = self.move_cursor_so_it_is_visible(frame, &blocks); - if let Some(cursor_msg_id) = new_cursor_msg_id { - // 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; + match self.correction { + Some(Correction::MakeCursorVisible) => { + self.scroll_so_cursor_is_visible(frame, &mut blocks); self.fill_screen_and_clamp_scrolling(frame, &mut blocks) .await; } + Some(Correction::MoveCursorToVisibleArea) => { + let new_cursor_msg_id = self.move_cursor_so_it_is_visible(frame, &blocks); + if let Some(cursor_msg_id) = new_cursor_msg_id { + // 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.scroll = 0; + self.correction = None; + + 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; + } + } + None => {} } self.last_cursor = self.cursor.clone(); self.last_cursor_line = self.cursor_line(&blocks); - self.make_cursor_visible = false; self.scroll = 0; + self.correction = None; blocks }