Render editor and pseudo message
This commit is contained in:
parent
4ead592e59
commit
415da3afd8
6 changed files with 132 additions and 42 deletions
|
|
@ -61,9 +61,9 @@ impl<M: Msg, S: MsgStore<M>> ChatState<M, S> {
|
|||
&self.store
|
||||
}
|
||||
|
||||
pub fn widget(&self) -> Chat<M, S> {
|
||||
pub fn widget(&self, nick: String) -> Chat<M, S> {
|
||||
match self.mode {
|
||||
Mode::Tree => Chat::Tree(self.tree.widget()),
|
||||
Mode::Tree => Chat::Tree(self.tree.widget(nick)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,8 +88,11 @@ impl<M: Msg, S: MsgStore<M>> TreeViewState<M, S> {
|
|||
Self(Arc::new(Mutex::new(InnerTreeViewState::new(store))))
|
||||
}
|
||||
|
||||
pub fn widget(&self) -> TreeView<M, S> {
|
||||
TreeView(self.0.clone())
|
||||
pub fn widget(&self, nick: String) -> TreeView<M, S> {
|
||||
TreeView {
|
||||
inner: self.0.clone(),
|
||||
nick,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_navigation(&mut self, event: KeyEvent) -> bool {
|
||||
|
|
@ -114,7 +117,10 @@ impl<M: Msg, S: MsgStore<M>> TreeViewState<M, S> {
|
|||
// Widget //
|
||||
////////////
|
||||
|
||||
pub struct TreeView<M: Msg, S: MsgStore<M>>(Arc<Mutex<InnerTreeViewState<M, S>>>);
|
||||
pub struct TreeView<M: Msg, S: MsgStore<M>> {
|
||||
inner: Arc<Mutex<InnerTreeViewState<M, S>>>,
|
||||
nick: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<M, S> Widget for TreeView<M, S>
|
||||
|
|
@ -128,8 +134,8 @@ where
|
|||
}
|
||||
|
||||
async fn render(self: Box<Self>, frame: &mut Frame) {
|
||||
let mut guard = self.0.lock().await;
|
||||
let blocks = guard.relayout(frame).await;
|
||||
let mut guard = self.inner.lock().await;
|
||||
let blocks = guard.relayout(&self.nick, frame).await;
|
||||
|
||||
let size = frame.size();
|
||||
for block in blocks.into_blocks().blocks {
|
||||
|
|
|
|||
|
|
@ -51,8 +51,20 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
.is_some()
|
||||
}
|
||||
|
||||
fn editor_block(&self, nick: &str, frame: &mut Frame, indent: usize) -> Block<BlockId<M::Id>> {
|
||||
let (widget, cursor_line) = widgets::editor::<M>(frame, indent, nick, &self.editor);
|
||||
let cursor_line = cursor_line as i32;
|
||||
Block::new(frame, BlockId::Cursor, widget).focus(cursor_line..cursor_line + 1)
|
||||
}
|
||||
|
||||
fn pseudo_block(&self, nick: &str, frame: &mut Frame, indent: usize) -> Block<BlockId<M::Id>> {
|
||||
let widget = widgets::pseudo::<M>(indent, nick, &self.editor);
|
||||
Block::new(frame, BlockId::Cursor, widget)
|
||||
}
|
||||
|
||||
fn layout_subtree(
|
||||
&self,
|
||||
nick: &str,
|
||||
frame: &mut Frame,
|
||||
tree: &Tree<M>,
|
||||
indent: usize,
|
||||
|
|
@ -78,7 +90,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
// Children, recursively
|
||||
if let Some(children) = tree.children(id) {
|
||||
for child in children {
|
||||
self.layout_subtree(frame, tree, indent + 1, child, blocks);
|
||||
self.layout_subtree(nick, frame, tree, indent + 1, child, blocks);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,20 +102,29 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
|
||||
// Trailing editor or pseudomessage
|
||||
if self.cursor.refers_to_last_child_of(id) {
|
||||
match self.cursor {
|
||||
Cursor::Editor { .. } => blocks
|
||||
.blocks_mut()
|
||||
.push_back(self.editor_block(nick, frame, indent)),
|
||||
Cursor::Pseudo { .. } => blocks
|
||||
.blocks_mut()
|
||||
.push_back(self.pseudo_block(nick, frame, indent)),
|
||||
_ => {}
|
||||
}
|
||||
// TODO Render proper editor or pseudocursor
|
||||
let block = Block::new(frame, BlockId::Cursor, Text::new("TODO"));
|
||||
blocks.blocks_mut().push_back(block);
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_tree(&self, frame: &mut Frame, tree: Tree<M>) -> TreeBlocks<M::Id> {
|
||||
fn layout_tree(&self, nick: &str, frame: &mut Frame, tree: Tree<M>) -> TreeBlocks<M::Id> {
|
||||
let root = Root::Tree(tree.root().clone());
|
||||
let mut blocks = TreeBlocks::new(root.clone(), root);
|
||||
self.layout_subtree(frame, &tree, 0, tree.root(), &mut blocks);
|
||||
self.layout_subtree(nick, frame, &tree, 0, tree.root(), &mut blocks);
|
||||
blocks
|
||||
}
|
||||
|
||||
fn layout_bottom(&self, frame: &mut Frame) -> TreeBlocks<M::Id> {
|
||||
fn layout_bottom(&self, nick: &str, frame: &mut Frame) -> TreeBlocks<M::Id> {
|
||||
let mut blocks = TreeBlocks::new(Root::Bottom, Root::Bottom);
|
||||
|
||||
// Ghost cursor, for positioning according to last cursor line
|
||||
|
|
@ -119,18 +140,19 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
let block = Block::new(frame, BlockId::Cursor, Empty);
|
||||
blocks.blocks_mut().push_back(block);
|
||||
}
|
||||
Cursor::Editor { parent: None, .. } | Cursor::Pseudo { parent: None, .. } => {
|
||||
// TODO Render proper editor or pseudocursor
|
||||
let block = Block::new(frame, BlockId::Cursor, Text::new("TODO"));
|
||||
blocks.blocks_mut().push_back(block);
|
||||
}
|
||||
Cursor::Editor { parent: None, .. } => blocks
|
||||
.blocks_mut()
|
||||
.push_back(self.editor_block(nick, frame, 0)),
|
||||
Cursor::Pseudo { parent: None, .. } => blocks
|
||||
.blocks_mut()
|
||||
.push_back(self.pseudo_block(nick, frame, 0)),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
blocks
|
||||
}
|
||||
|
||||
async fn expand_to_top(&self, frame: &mut Frame, blocks: &mut TreeBlocks<M::Id>) {
|
||||
async fn expand_to_top(&self, nick: &str, frame: &mut Frame, blocks: &mut TreeBlocks<M::Id>) {
|
||||
let top_line = 0;
|
||||
|
||||
while blocks.blocks().top_line > top_line {
|
||||
|
|
@ -144,11 +166,16 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
None => break,
|
||||
};
|
||||
let prev_tree = self.store.tree(&prev_tree_id).await;
|
||||
blocks.prepend(self.layout_tree(frame, prev_tree));
|
||||
blocks.prepend(self.layout_tree(nick, frame, prev_tree));
|
||||
}
|
||||
}
|
||||
|
||||
async fn expand_to_bottom(&self, frame: &mut Frame, blocks: &mut TreeBlocks<M::Id>) {
|
||||
async fn expand_to_bottom(
|
||||
&self,
|
||||
nick: &str,
|
||||
frame: &mut Frame,
|
||||
blocks: &mut TreeBlocks<M::Id>,
|
||||
) {
|
||||
let bottom_line = frame.size().height as i32 - 1;
|
||||
|
||||
while blocks.blocks().bottom_line < bottom_line {
|
||||
|
|
@ -159,44 +186,46 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
};
|
||||
if let Some(next_tree_id) = next_tree_id {
|
||||
let next_tree = self.store.tree(&next_tree_id).await;
|
||||
blocks.append(self.layout_tree(frame, next_tree));
|
||||
blocks.append(self.layout_tree(nick, frame, next_tree));
|
||||
} else {
|
||||
blocks.append(self.layout_bottom(frame));
|
||||
blocks.append(self.layout_bottom(nick, frame));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn fill_screen_and_clamp_scrolling(
|
||||
&self,
|
||||
nick: &str,
|
||||
frame: &mut Frame,
|
||||
blocks: &mut TreeBlocks<M::Id>,
|
||||
) {
|
||||
let top_line = 0;
|
||||
let bottom_line = frame.size().height as i32 - 1;
|
||||
|
||||
self.expand_to_top(frame, blocks).await;
|
||||
self.expand_to_top(nick, frame, blocks).await;
|
||||
|
||||
if blocks.blocks().top_line > top_line {
|
||||
blocks.blocks_mut().set_top_line(0);
|
||||
}
|
||||
|
||||
self.expand_to_bottom(frame, blocks).await;
|
||||
self.expand_to_bottom(nick, frame, blocks).await;
|
||||
|
||||
if blocks.blocks().bottom_line < bottom_line {
|
||||
blocks.blocks_mut().set_bottom_line(bottom_line);
|
||||
}
|
||||
|
||||
self.expand_to_top(frame, blocks).await;
|
||||
self.expand_to_top(nick, frame, blocks).await;
|
||||
}
|
||||
|
||||
async fn layout_last_cursor_seed(
|
||||
&self,
|
||||
nick: &str,
|
||||
frame: &mut Frame,
|
||||
last_cursor_path: &Path<M::Id>,
|
||||
) -> TreeBlocks<M::Id> {
|
||||
match &self.last_cursor {
|
||||
Cursor::Bottom => {
|
||||
let mut blocks = self.layout_bottom(frame);
|
||||
let mut blocks = self.layout_bottom(nick, frame);
|
||||
|
||||
let bottom_line = frame.size().height as i32 - 1;
|
||||
blocks.blocks_mut().set_bottom_line(bottom_line);
|
||||
|
|
@ -204,7 +233,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
blocks
|
||||
}
|
||||
Cursor::Editor { parent: None, .. } | Cursor::Pseudo { parent: None, .. } => {
|
||||
let mut blocks = self.layout_bottom(frame);
|
||||
let mut blocks = self.layout_bottom(nick, frame);
|
||||
|
||||
blocks
|
||||
.blocks_mut()
|
||||
|
|
@ -221,7 +250,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
} => {
|
||||
let root = last_cursor_path.first();
|
||||
let tree = self.store.tree(root).await;
|
||||
let mut blocks = self.layout_tree(frame, tree);
|
||||
let mut blocks = self.layout_tree(nick, frame, tree);
|
||||
|
||||
blocks
|
||||
.blocks_mut()
|
||||
|
|
@ -234,6 +263,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
|
||||
async fn layout_cursor_seed(
|
||||
&self,
|
||||
nick: &str,
|
||||
frame: &mut Frame,
|
||||
last_cursor_path: &Path<M::Id>,
|
||||
cursor_path: &Path<M::Id>,
|
||||
|
|
@ -244,7 +274,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
Cursor::Bottom
|
||||
| Cursor::Editor { parent: None, .. }
|
||||
| Cursor::Pseudo { parent: None, .. } => {
|
||||
let mut blocks = self.layout_bottom(frame);
|
||||
let mut blocks = self.layout_bottom(nick, frame);
|
||||
|
||||
blocks.blocks_mut().set_bottom_line(bottom_line);
|
||||
|
||||
|
|
@ -259,7 +289,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
} => {
|
||||
let root = cursor_path.first();
|
||||
let tree = self.store.tree(root).await;
|
||||
let mut blocks = self.layout_tree(frame, tree);
|
||||
let mut blocks = self.layout_tree(nick, frame, tree);
|
||||
|
||||
let cursor_above_last = cursor_path < last_cursor_path;
|
||||
let cursor_line = if cursor_above_last { 0 } else { bottom_line };
|
||||
|
|
@ -274,15 +304,17 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
|
||||
async fn layout_initial_seed(
|
||||
&self,
|
||||
nick: &str,
|
||||
frame: &mut Frame,
|
||||
last_cursor_path: &Path<M::Id>,
|
||||
cursor_path: &Path<M::Id>,
|
||||
) -> TreeBlocks<M::Id> {
|
||||
if let Cursor::Bottom = self.cursor {
|
||||
self.layout_cursor_seed(frame, last_cursor_path, cursor_path)
|
||||
self.layout_cursor_seed(nick, frame, last_cursor_path, cursor_path)
|
||||
.await
|
||||
} else {
|
||||
self.layout_last_cursor_seed(frame, last_cursor_path).await
|
||||
self.layout_last_cursor_seed(nick, frame, last_cursor_path)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +409,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn relayout(&mut self, frame: &mut Frame) -> TreeBlocks<M::Id> {
|
||||
pub async fn relayout(&mut self, nick: &str, frame: &mut Frame) -> TreeBlocks<M::Id> {
|
||||
// The basic idea is this:
|
||||
//
|
||||
// First, layout a full screen of blocks around self.last_cursor, using
|
||||
|
|
@ -400,24 +432,24 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
let cursor_path = self.cursor_path(&self.cursor).await;
|
||||
|
||||
let mut blocks = self
|
||||
.layout_initial_seed(frame, &last_cursor_path, &cursor_path)
|
||||
.layout_initial_seed(nick, frame, &last_cursor_path, &cursor_path)
|
||||
.await;
|
||||
blocks.blocks_mut().offset(self.scroll);
|
||||
self.fill_screen_and_clamp_scrolling(frame, &mut blocks)
|
||||
self.fill_screen_and_clamp_scrolling(nick, frame, &mut blocks)
|
||||
.await;
|
||||
|
||||
if !self.contains_cursor(&blocks) {
|
||||
blocks = self
|
||||
.layout_cursor_seed(frame, &last_cursor_path, &cursor_path)
|
||||
.layout_cursor_seed(nick, frame, &last_cursor_path, &cursor_path)
|
||||
.await;
|
||||
self.fill_screen_and_clamp_scrolling(frame, &mut blocks)
|
||||
self.fill_screen_and_clamp_scrolling(nick, frame, &mut blocks)
|
||||
.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)
|
||||
self.fill_screen_and_clamp_scrolling(nick, frame, &mut blocks)
|
||||
.await;
|
||||
}
|
||||
Some(Correction::MoveCursorToVisibleArea) => {
|
||||
|
|
@ -433,8 +465,10 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
|||
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)
|
||||
blocks = self
|
||||
.layout_last_cursor_seed(nick, frame, &last_cursor_path)
|
||||
.await;
|
||||
self.fill_screen_and_clamp_scrolling(nick, frame, &mut blocks)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ mod indent;
|
|||
mod time;
|
||||
|
||||
use crossterm::style::{ContentStyle, Stylize};
|
||||
use toss::frame::Frame;
|
||||
|
||||
use super::super::ChatMsg;
|
||||
use crate::store::Msg;
|
||||
use crate::ui::widgets::editor::EditorState;
|
||||
use crate::ui::widgets::join::{HJoin, Segment};
|
||||
use crate::ui::widgets::layer::Layer;
|
||||
use crate::ui::widgets::padding::Padding;
|
||||
|
|
@ -53,3 +55,50 @@ pub fn msg_placeholder(highlighted: bool, indent: usize) -> BoxedWidget {
|
|||
])
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn editor<M: ChatMsg>(
|
||||
frame: &mut Frame,
|
||||
indent: usize,
|
||||
nick: &str,
|
||||
editor: &EditorState,
|
||||
) -> (BoxedWidget, usize) {
|
||||
let (nick, content) = M::edit(nick, &editor.text());
|
||||
let editor = editor.widget().highlight(|_| content);
|
||||
let cursor_row = editor.cursor_row(frame);
|
||||
|
||||
let widget = HJoin::new(vec![
|
||||
Segment::new(
|
||||
Padding::new(time::widget(None, true))
|
||||
.stretch(true)
|
||||
.right(1),
|
||||
),
|
||||
Segment::new(Indent::new(indent, true)),
|
||||
Segment::new(Layer::new(vec![
|
||||
Indent::new(1, false).into(),
|
||||
Padding::new(Text::new(nick)).right(1).into(),
|
||||
])),
|
||||
Segment::new(editor).priority(1),
|
||||
])
|
||||
.into();
|
||||
|
||||
(widget, cursor_row)
|
||||
}
|
||||
|
||||
pub fn pseudo<M: ChatMsg>(indent: usize, nick: &str, editor: &EditorState) -> BoxedWidget {
|
||||
let (nick, content) = M::edit(nick, &editor.text());
|
||||
|
||||
HJoin::new(vec![
|
||||
Segment::new(
|
||||
Padding::new(time::widget(None, true))
|
||||
.stretch(true)
|
||||
.right(1),
|
||||
),
|
||||
Segment::new(Indent::new(indent, true)),
|
||||
Segment::new(Layer::new(vec![
|
||||
Indent::new(1, false).into(),
|
||||
Padding::new(Text::new(nick)).right(1).into(),
|
||||
])),
|
||||
Segment::new(Text::new(content).wrap(true)).priority(1),
|
||||
])
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,8 @@ impl EuphRoom {
|
|||
Segment::new(Border::new(
|
||||
Padding::new(self.status_widget(status)).horizontal(1),
|
||||
)),
|
||||
Segment::new(self.chat.widget()).expanding(true),
|
||||
// TODO Use last known nick?
|
||||
Segment::new(self.chat.widget(String::new())).expanding(true),
|
||||
])
|
||||
.into()
|
||||
}
|
||||
|
|
@ -133,7 +134,7 @@ impl EuphRoom {
|
|||
Segment::new(Border::new(
|
||||
Padding::new(self.status_widget(status)).horizontal(1),
|
||||
)),
|
||||
Segment::new(self.chat.widget()).expanding(true),
|
||||
Segment::new(self.chat.widget(joined.session.name.clone())).expanding(true),
|
||||
]))
|
||||
.expanding(true),
|
||||
Segment::new(Border::new(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue