Fix crash when cursor moves inside folded subtree

This commit is contained in:
Joscha 2023-05-03 00:02:09 +02:00
parent 2afda17d4b
commit e6585286e3
4 changed files with 46 additions and 12 deletions

View file

@ -178,7 +178,7 @@ where
} }
/// Expand blocks such that the screen is full for any offset where the /// Expand blocks such that the screen is full for any offset where the
/// specified block is visible. /// specified block is visible. The block must exist.
pub async fn expand_to_fill_screen_around_block<Id, R>(r: &mut R, id: &Id) -> Result<(), R::Error> pub async fn expand_to_fill_screen_around_block<Id, R>(r: &mut R, id: &Id) -> Result<(), R::Error>
where where
Id: Eq, Id: Eq,
@ -196,6 +196,8 @@ where
Ok(()) Ok(())
} }
/// Scroll so that the top of the block is at the specified value. Returns
/// `true` if successful, or `false` if the block could not be found.
pub fn scroll_to_set_block_top<Id, R>(r: &mut R, id: &Id, top: i32) -> bool pub fn scroll_to_set_block_top<Id, R>(r: &mut R, id: &Id, top: i32) -> bool
where where
Id: Eq, Id: Eq,

View file

@ -439,7 +439,7 @@ where
let mut renderer = TreeRenderer::new( let mut renderer = TreeRenderer::new(
context, context,
&self.state.store, &self.state.store,
&self.state.folded, &mut self.state.folded,
self.cursor, self.cursor,
self.editor, self.editor,
frame.widthdb(), frame.widthdb(),

View file

@ -80,7 +80,7 @@ pub struct TreeRenderer<'a, M: Msg, S: MsgStore<M>> {
context: TreeContext<M::Id>, context: TreeContext<M::Id>,
store: &'a S, store: &'a S,
folded: &'a HashSet<M::Id>, folded: &'a mut HashSet<M::Id>,
cursor: &'a mut Cursor<M::Id>, cursor: &'a mut Cursor<M::Id>,
editor: &'a mut EditorState, editor: &'a mut EditorState,
widthdb: &'a mut WidthDb, widthdb: &'a mut WidthDb,
@ -107,7 +107,7 @@ where
pub fn new( pub fn new(
context: TreeContext<M::Id>, context: TreeContext<M::Id>,
store: &'a S, store: &'a S,
folded: &'a HashSet<M::Id>, folded: &'a mut HashSet<M::Id>,
cursor: &'a mut Cursor<M::Id>, cursor: &'a mut Cursor<M::Id>,
editor: &'a mut EditorState, editor: &'a mut EditorState,
widthdb: &'a mut WidthDb, widthdb: &'a mut WidthDb,
@ -279,12 +279,30 @@ where
Ok(Some(path.into_first())) Ok(Some(path.into_first()))
} }
async fn prepare_initial_tree(&mut self, root_id: &Option<M::Id>) -> Result<(), S::Error> { /// Render the tree containing the cursor to the blocks and set the top and
/// bottom root id accordingly. This function will always render a block
/// that has the cusor id.
async fn prepare_initial_tree(
&mut self,
cursor_id: &TreeBlockId<M::Id>,
root_id: &Option<M::Id>,
) -> Result<(), S::Error> {
self.top_root_id = root_id.clone(); self.top_root_id = root_id.clone();
self.bottom_root_id = root_id.clone(); self.bottom_root_id = root_id.clone();
let blocks = if let Some(root_id) = root_id { let blocks = if let Some(root_id) = root_id {
let tree = self.store.tree(root_id).await?; let tree = self.store.tree(root_id).await?;
// To ensure the cursor block will be rendered, all its parents must
// be unfolded.
if let TreeBlockId::Msg(id) | TreeBlockId::After(id) = cursor_id {
let mut id = id.clone();
while let Some(parent_id) = tree.parent(&id) {
self.folded.remove(&parent_id);
id = parent_id;
}
}
self.layout_tree(tree) self.layout_tree(tree)
} else { } else {
self.layout_bottom() self.layout_bottom()
@ -318,9 +336,11 @@ where
let cursor_id = TreeBlockId::from_cursor(self.cursor); let cursor_id = TreeBlockId::from_cursor(self.cursor);
let cursor_root_id = self.root_id(&cursor_id).await?; let cursor_root_id = self.root_id(&cursor_id).await?;
// Render cursor and blocks around it until screen is filled as long as // Render cursor and blocks around it so that the screen will always be
// the cursor is visible, regardless of how the screen is scrolled. // filled as long as the cursor is visible, regardless of how the screen
self.prepare_initial_tree(&cursor_root_id).await?; // is scrolled.
self.prepare_initial_tree(&cursor_id, &cursor_root_id)
.await?;
renderer::expand_to_fill_screen_around_block(self, &cursor_id).await?; renderer::expand_to_fill_screen_around_block(self, &cursor_id).await?;
// Scroll based on last cursor position // Scroll based on last cursor position

View file

@ -33,8 +33,14 @@ where
delta: i32, delta: i32,
) -> Result<(), S::Error> { ) -> Result<(), S::Error> {
let context = self.last_context(); let context = self.last_context();
let mut renderer = let mut renderer = TreeRenderer::new(
TreeRenderer::new(context, &self.store, &self.folded, cursor, editor, widthdb); context,
&self.store,
&mut self.folded,
cursor,
editor,
widthdb,
);
renderer.prepare_blocks_for_drawing().await?; renderer.prepare_blocks_for_drawing().await?;
renderer.scroll_by(delta).await?; renderer.scroll_by(delta).await?;
@ -54,8 +60,14 @@ where
widthdb: &mut WidthDb, widthdb: &mut WidthDb,
) -> Result<(), S::Error> { ) -> Result<(), S::Error> {
let context = self.last_context(); let context = self.last_context();
let mut renderer = let mut renderer = TreeRenderer::new(
TreeRenderer::new(context, &self.store, &self.folded, cursor, editor, widthdb); context,
&self.store,
&mut self.folded,
cursor,
editor,
widthdb,
);
renderer.prepare_blocks_for_drawing().await?; renderer.prepare_blocks_for_drawing().await?;
renderer.center_cursor(); renderer.center_cursor();