//! Moving the cursor around. use crate::store::{Msg, MsgStore, Tree}; use super::{Correction, InnerTreeViewState}; #[derive(Debug, Clone, Copy)] pub enum Cursor { Bottom, Msg(I), Editor { coming_from: Option, parent: Option, }, Pseudo { coming_from: Option, parent: Option, }, } impl Cursor { pub fn refers_to(&self, id: &I) -> bool { if let Self::Msg(own_id) = self { own_id == id } else { false } } pub fn refers_to_last_child_of(&self, id: &I) -> bool { if let Self::Editor { parent: Some(parent), .. } | Self::Pseudo { parent: Some(parent), .. } = self { parent == id } else { false } } } impl> InnerTreeViewState { fn find_parent(tree: &Tree, id: &mut M::Id) -> bool { if let Some(parent) = tree.parent(id) { *id = parent; true } else { false } } fn find_first_child(tree: &Tree, id: &mut M::Id) -> bool { if let Some(child) = tree.children(id).and_then(|c| c.first()) { *id = child.clone(); true } else { false } } fn find_last_child(tree: &Tree, id: &mut M::Id) -> bool { if let Some(child) = tree.children(id).and_then(|c| c.last()) { *id = child.clone(); true } else { false } } /// Move to the previous sibling, or don't move if this is not possible. /// /// Always stays at the same level of indentation. async fn find_prev_sibling(store: &S, tree: &mut Tree, id: &mut M::Id) -> bool { if let Some(prev_sibling) = tree.prev_sibling(id) { *id = prev_sibling; true } else if tree.parent(id).is_none() { // We're at the root of our tree, so we need to move to the root of // the previous tree. if let Some(prev_tree_id) = store.prev_tree_id(tree.root()).await { *tree = store.tree(&prev_tree_id).await; *id = prev_tree_id; true } else { false } } else { false } } /// Move to the next sibling, or don't move if this is not possible. /// /// Always stays at the same level of indentation. async fn find_next_sibling(store: &S, tree: &mut Tree, id: &mut M::Id) -> bool { if let Some(next_sibling) = tree.next_sibling(id) { *id = next_sibling; true } else if tree.parent(id).is_none() { // We're at the root of our tree, so we need to move to the root of // the next tree. if let Some(next_tree_id) = store.next_tree_id(tree.root()).await { *tree = store.tree(&next_tree_id).await; *id = next_tree_id; true } else { false } } else { false } } /// Move to the previous message, or don't move if this is not possible. async fn find_prev_msg(store: &S, tree: &mut Tree, id: &mut M::Id) -> bool { // Move to previous sibling, then to its last child // If not possible, move to parent if Self::find_prev_sibling(store, tree, id).await { while Self::find_last_child(tree, id) {} true } else { Self::find_parent(tree, id) } } /// Move to the next message, or don't move if this is not possible. async fn find_next_msg(store: &S, tree: &mut Tree, id: &mut M::Id) -> bool { if Self::find_first_child(tree, id) { return true; } if Self::find_next_sibling(store, tree, id).await { return true; } // Temporary id to avoid modifying the original one if no parent-sibling // can be found. let mut tmp_id = id.clone(); while Self::find_parent(tree, &mut tmp_id) { if Self::find_next_sibling(store, tree, &mut tmp_id).await { *id = tmp_id; return true; } } false } pub async fn move_cursor_up(&mut self) { match &mut self.cursor { Cursor::Bottom | Cursor::Pseudo { parent: None, .. } => { if let Some(last_tree_id) = self.store.last_tree_id().await { let tree = self.store.tree(&last_tree_id).await; let mut id = last_tree_id; while Self::find_last_child(&tree, &mut id) {} self.cursor = Cursor::Msg(id); } } Cursor::Msg(ref mut msg) => { let path = self.store.path(msg).await; let mut tree = self.store.tree(path.first()).await; Self::find_prev_msg(&self.store, &mut tree, msg).await; } Cursor::Editor { .. } => {} Cursor::Pseudo { parent: Some(parent), .. } => { let tree = self.store.tree(parent).await; let mut id = parent.clone(); while Self::find_last_child(&tree, &mut id) {} self.cursor = Cursor::Msg(id); } } self.correction = Some(Correction::MakeCursorVisible); } pub async fn move_cursor_down(&mut self) { match &mut self.cursor { Cursor::Msg(ref mut msg) => { let path = self.store.path(msg).await; let mut tree = self.store.tree(path.first()).await; if !Self::find_next_msg(&self.store, &mut tree, msg).await { self.cursor = Cursor::Bottom; } } Cursor::Pseudo { parent: None, .. } => { self.cursor = Cursor::Bottom; } Cursor::Pseudo { parent: Some(parent), .. } => { let mut tree = self.store.tree(parent).await; let mut id = parent.clone(); while Self::find_last_child(&tree, &mut id) {} // Now we're at the previous message if Self::find_next_msg(&self.store, &mut tree, &mut id).await { self.cursor = Cursor::Msg(id); } else { self.cursor = Cursor::Bottom; } } _ => {} } 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.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.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); } pub async fn parent_for_normal_reply(&self) -> Option> { match &self.cursor { Cursor::Bottom => Some(None), Cursor::Msg(id) => { let path = self.store.path(id).await; let tree = self.store.tree(path.first()).await; Some(Some(if tree.next_sibling(id).is_some() { // A reply to a message that has further siblings should be a // direct reply. An indirect reply might end up a lot further // down in the current conversation. id.clone() } else if let Some(parent) = tree.parent(id) { // A reply to a message without younger siblings should be // an indirect reply so as not to create unnecessarily deep // threads. In the case that our message has children, this // might get a bit confusing. I'm not sure yet how well this // "smart" reply actually works in practice. parent } else { // When replying to a top-level message, it makes sense to avoid // creating unnecessary new threads. id.clone() })) } _ => None, } } pub async fn parent_for_alternate_reply(&self) -> Option> { match &self.cursor { Cursor::Bottom => Some(None), Cursor::Msg(id) => { let path = self.store.path(id).await; let tree = self.store.tree(path.first()).await; Some(Some(if tree.next_sibling(id).is_none() { // The opposite of replying normally id.clone() } else if let Some(parent) = tree.parent(id) { // The opposite of replying normally parent } else { // The same as replying normally, still to avoid creating // unnecessary new threads id.clone() })) } _ => None, } } } /* pub async fn move_up_sibling>( &mut self, store: &S, cursor: &mut Option>, frame: &mut Frame, size: Size, ) { let old_blocks = self .layout_blocks(store, cursor.as_ref(), frame, size) .await; let old_cursor_id = cursor.as_ref().map(|c| c.id.clone()); if let Some(cursor) = cursor { let path = store.path(&cursor.id).await; let mut tree = store.tree(path.first()).await; self.find_prev_sibling(store, &mut tree, &mut cursor.id) .await; } else if let Some(last_tree) = store.last_tree().await { // I think moving to the root of the last tree makes the most sense // here. Alternatively, we could just not move the cursor, but that // wouldn't be very useful. *cursor = Some(Cursor::new(last_tree)); } // If neither condition holds, we can't set a cursor because there's no // message to move to. if let Some(cursor) = cursor { self.correct_cursor_offset(store, frame, size, &old_blocks, &old_cursor_id, cursor) .await; } } pub async fn move_down_sibling>( &mut self, store: &S, cursor: &mut Option>, frame: &mut Frame, size: Size, ) { let old_blocks = self .layout_blocks(store, cursor.as_ref(), frame, size) .await; let old_cursor_id = cursor.as_ref().map(|c| c.id.clone()); if let Some(cursor) = cursor { let path = store.path(&cursor.id).await; let mut tree = store.tree(path.first()).await; self.find_next_sibling(store, &mut tree, &mut cursor.id) .await; } // If that condition doesn't hold, we're already at the bottom in // cursor-less mode and can't move further down anyways. if let Some(cursor) = cursor { self.correct_cursor_offset(store, frame, size, &old_blocks, &old_cursor_id, cursor) .await; } } // TODO move_older[_unseen] // TODO move_newer[_unseen] pub async fn center_cursor>( &mut self, store: &S, cursor: &mut Option>, frame: &mut Frame, size: Size, ) { if let Some(cursor) = cursor { cursor.proportion = 0.5; // Correcting the offset just to make sure that this function // behaves nicely if the cursor has too many lines. let old_blocks = self.layout_blocks(store, Some(cursor), frame, size).await; let old_cursor_id = Some(cursor.id.clone()); self.correct_cursor_offset(store, frame, size, &old_blocks, &old_cursor_id, cursor) .await; } } */