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.
This commit is contained in:
Joscha 2022-08-01 22:51:40 +02:00
parent 7988daf34d
commit 816bf5be1c
3 changed files with 38 additions and 30 deletions

View file

@ -24,6 +24,11 @@ use super::ChatMsg;
// State // // State //
/////////// ///////////
enum Correction {
MakeCursorVisible,
MoveCursorToVisibleArea,
}
struct InnerTreeViewState<M: Msg, S: MsgStore<M>> { struct InnerTreeViewState<M: Msg, S: MsgStore<M>> {
store: S, store: S,
@ -31,14 +36,11 @@ struct InnerTreeViewState<M: Msg, S: MsgStore<M>> {
last_cursor_line: i32, last_cursor_line: i32,
cursor: Cursor<M::Id>, cursor: Cursor<M::Id>,
/// 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 /// Scroll the view on the next render. Positive values scroll up and
/// negative values scroll down. /// negative values scroll down.
scroll: i32, scroll: i32,
correction: Option<Correction>,
editor: EditorState, editor: EditorState,
} }
@ -50,8 +52,8 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
last_cursor: Cursor::Bottom, last_cursor: Cursor::Bottom,
last_cursor_line: 0, last_cursor_line: 0,
cursor: Cursor::Bottom, cursor: Cursor::Bottom,
make_cursor_visible: false,
scroll: 0, scroll: 0,
correction: None,
editor: EditorState::new(), editor: EditorState::new(),
} }
} }

View file

@ -2,7 +2,7 @@
use crate::store::{Msg, MsgStore, Tree}; use crate::store::{Msg, MsgStore, Tree};
use super::InnerTreeViewState; use super::{Correction, InnerTreeViewState};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum Cursor<I> { pub enum Cursor<I> {
@ -177,7 +177,7 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
self.cursor = Cursor::Msg(id); self.cursor = Cursor::Msg(id);
} }
} }
self.make_cursor_visible = true; self.correction = Some(Correction::MakeCursorVisible);
} }
pub async fn move_cursor_down(&mut self) { pub async fn move_cursor_down(&mut self) {
@ -208,28 +208,30 @@ impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
} }
_ => {} _ => {}
} }
self.make_cursor_visible = true; self.correction = Some(Correction::MakeCursorVisible);
} }
pub async fn move_cursor_to_top(&mut self) { pub async fn move_cursor_to_top(&mut self) {
if let Some(first_tree_id) = self.store.first_tree_id().await { if let Some(first_tree_id) = self.store.first_tree_id().await {
self.cursor = Cursor::Msg(first_tree_id); 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) { pub async fn move_cursor_to_bottom(&mut self) {
self.cursor = Cursor::Bottom; self.cursor = Cursor::Bottom;
// Not really necessary; only here for consistency with other methods // 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) { pub fn scroll_up(&mut self, amount: i32) {
self.scroll += amount; self.scroll += amount;
self.correction = Some(Correction::MoveCursorToVisibleArea);
} }
pub fn scroll_down(&mut self, amount: i32) { pub fn scroll_down(&mut self, amount: i32) {
self.scroll -= amount; self.scroll -= amount;
self.correction = Some(Correction::MoveCursorToVisibleArea);
} }
} }

View file

@ -7,7 +7,7 @@ use crate::ui::widgets::text::Text;
use crate::ui::ChatMsg; use crate::ui::ChatMsg;
use super::tree_blocks::{BlockId, Root, TreeBlocks}; use super::tree_blocks::{BlockId, Root, TreeBlocks};
use super::{widgets, Cursor, InnerTreeViewState}; use super::{widgets, Correction, Cursor, InnerTreeViewState};
impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> { impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
async fn cursor_path(&self, cursor: &Cursor<M::Id>) -> Path<M::Id> { async fn cursor_path(&self, cursor: &Cursor<M::Id>) -> Path<M::Id> {
@ -410,11 +410,13 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
.await; .await;
} }
if self.make_cursor_visible { match self.correction {
Some(Correction::MakeCursorVisible) => {
self.scroll_so_cursor_is_visible(frame, &mut blocks); self.scroll_so_cursor_is_visible(frame, &mut blocks);
self.fill_screen_and_clamp_scrolling(frame, &mut blocks) self.fill_screen_and_clamp_scrolling(frame, &mut blocks)
.await; .await;
} else { }
Some(Correction::MoveCursorToVisibleArea) => {
let new_cursor_msg_id = self.move_cursor_so_it_is_visible(frame, &blocks); let new_cursor_msg_id = self.move_cursor_so_it_is_visible(frame, &blocks);
if let Some(cursor_msg_id) = new_cursor_msg_id { if let Some(cursor_msg_id) = new_cursor_msg_id {
// Moving the cursor invalidates our current blocks, so we sadly // Moving the cursor invalidates our current blocks, so we sadly
@ -423,8 +425,8 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
self.last_cursor = self.cursor.clone(); self.last_cursor = self.cursor.clone();
self.last_cursor_line = self.cursor_line(&blocks); self.last_cursor_line = self.cursor_line(&blocks);
self.make_cursor_visible = false;
self.scroll = 0; self.scroll = 0;
self.correction = None;
let last_cursor_path = self.store.path(&cursor_msg_id).await; let last_cursor_path = self.store.path(&cursor_msg_id).await;
blocks = self.layout_last_cursor_seed(frame, &last_cursor_path).await; blocks = self.layout_last_cursor_seed(frame, &last_cursor_path).await;
@ -432,11 +434,13 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
.await; .await;
} }
} }
None => {}
}
self.last_cursor = self.cursor.clone(); self.last_cursor = self.cursor.clone();
self.last_cursor_line = self.cursor_line(&blocks); self.last_cursor_line = self.cursor_line(&blocks);
self.make_cursor_visible = false;
self.scroll = 0; self.scroll = 0;
self.correction = None;
blocks blocks
} }