Render messages with less async

This commit is contained in:
Joscha 2023-04-17 18:59:16 +02:00
parent 8182cc5d38
commit a638caadcb
5 changed files with 62 additions and 100 deletions

12
Cargo.lock generated
View file

@ -68,17 +68,6 @@ version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
[[package]]
name = "async-recursion"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.13",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.68" version = "0.1.68"
@ -251,7 +240,6 @@ name = "cove"
version = "0.6.1" version = "0.6.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-recursion",
"async-trait", "async-trait",
"clap", "clap",
"cookie", "cookie",

View file

@ -5,7 +5,6 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.70" anyhow = "1.0.70"
async-recursion = "1.0.4"
async-trait = "0.1.68" async-trait = "0.1.68"
clap = { version = "4.2.1", features = ["derive", "deprecated"] } clap = { version = "4.2.1", features = ["derive", "deprecated"] }
cookie = "0.17.0" cookie = "0.17.0"

View file

@ -2,10 +2,9 @@
use std::convert::Infallible; use std::convert::Infallible;
use async_recursion::async_recursion;
use async_trait::async_trait; use async_trait::async_trait;
use toss::widgets::{EditorState, Empty, Predrawn, Resize}; use toss::widgets::{EditorState, Empty, Predrawn, Resize};
use toss::{AsyncWidget, Size, WidthDb}; use toss::{Size, Widget, WidthDb};
use crate::store::{Msg, MsgStore, Tree}; use crate::store::{Msg, MsgStore, Tree};
use crate::ui::chat::blocks::{Block, Blocks, Range}; use crate::ui::chat::blocks::{Block, Blocks, Range};
@ -122,26 +121,24 @@ where
} }
} }
async fn predraw<W>(widget: W, size: Size, widthdb: &mut WidthDb) -> Predrawn fn predraw<W>(widget: W, size: Size, widthdb: &mut WidthDb) -> Predrawn
where where
W: AsyncWidget<Infallible> + Send + Sync, W: Widget<Infallible>,
{ {
Predrawn::new_async(Resize::new(widget).with_max_width(size.width), widthdb) Predrawn::new(Resize::new(widget).with_max_width(size.width), widthdb).infallible()
.await
.infallible()
} }
async fn zero_height_block(&mut self, parent: Option<&M::Id>) -> TreeBlock<M::Id> { fn zero_height_block(&mut self, parent: Option<&M::Id>) -> TreeBlock<M::Id> {
let id = match parent { let id = match parent {
Some(parent) => TreeBlockId::After(parent.clone()), Some(parent) => TreeBlockId::After(parent.clone()),
None => TreeBlockId::Bottom, None => TreeBlockId::Bottom,
}; };
let widget = Self::predraw(Empty::new(), self.context.size, self.widthdb).await; let widget = Self::predraw(Empty::new(), self.context.size, self.widthdb);
Block::new(id, widget, false) Block::new(id, widget, false)
} }
async fn editor_block(&mut self, indent: usize, parent: Option<&M::Id>) -> TreeBlock<M::Id> { fn editor_block(&mut self, indent: usize, parent: Option<&M::Id>) -> TreeBlock<M::Id> {
let id = match parent { let id = match parent {
Some(parent) => TreeBlockId::After(parent.clone()), Some(parent) => TreeBlockId::After(parent.clone()),
None => TreeBlockId::Bottom, None => TreeBlockId::Bottom,
@ -149,7 +146,7 @@ where
// TODO Unhighlighted version when focusing on nick list // TODO Unhighlighted version when focusing on nick list
let widget = widgets::editor::<M>(indent, &self.context.nick, self.editor); let widget = widgets::editor::<M>(indent, &self.context.nick, self.editor);
let widget = Self::predraw(widget, self.context.size, self.widthdb).await; let widget = Self::predraw(widget, self.context.size, self.widthdb);
let mut block = Block::new(id, widget, false); let mut block = Block::new(id, widget, false);
// Since the editor was rendered when the `Predrawn` was created, the // Since the editor was rendered when the `Predrawn` was created, the
@ -160,7 +157,7 @@ where
block block
} }
async fn pseudo_block(&mut self, indent: usize, parent: Option<&M::Id>) -> TreeBlock<M::Id> { fn pseudo_block(&mut self, indent: usize, parent: Option<&M::Id>) -> TreeBlock<M::Id> {
let id = match parent { let id = match parent {
Some(parent) => TreeBlockId::After(parent.clone()), Some(parent) => TreeBlockId::After(parent.clone()),
None => TreeBlockId::Bottom, None => TreeBlockId::Bottom,
@ -168,11 +165,11 @@ where
// TODO Unhighlighted version when focusing on nick list // TODO Unhighlighted version when focusing on nick list
let widget = widgets::pseudo::<M>(indent, &self.context.nick, self.editor); let widget = widgets::pseudo::<M>(indent, &self.context.nick, self.editor);
let widget = Self::predraw(widget, self.context.size, self.widthdb).await; let widget = Self::predraw(widget, self.context.size, self.widthdb);
Block::new(id, widget, false) Block::new(id, widget, false)
} }
async fn message_block(&mut self, indent: usize, msg: &M) -> TreeBlock<M::Id> { fn message_block(&mut self, indent: usize, msg: &M) -> TreeBlock<M::Id> {
let msg_id = msg.id(); let msg_id = msg.id();
let highlighted = match self.cursor { let highlighted = match self.cursor {
@ -182,15 +179,11 @@ where
// TODO Amount of folded messages // TODO Amount of folded messages
let widget = widgets::msg(self.context.focused && highlighted, indent, msg, None); let widget = widgets::msg(self.context.focused && highlighted, indent, msg, None);
let widget = Self::predraw(widget, self.context.size, self.widthdb).await; let widget = Self::predraw(widget, self.context.size, self.widthdb);
Block::new(TreeBlockId::Msg(msg_id), widget, true) Block::new(TreeBlockId::Msg(msg_id), widget, true)
} }
async fn message_placeholder_block( fn message_placeholder_block(&mut self, indent: usize, msg_id: &M::Id) -> TreeBlock<M::Id> {
&mut self,
indent: usize,
msg_id: &M::Id,
) -> TreeBlock<M::Id> {
let highlighted = match self.cursor { let highlighted = match self.cursor {
Cursor::Msg(id) => id == msg_id, Cursor::Msg(id) => id == msg_id,
_ => false, _ => false,
@ -198,28 +191,23 @@ where
// TODO Amount of folded messages // TODO Amount of folded messages
let widget = widgets::msg_placeholder(self.context.focused && highlighted, indent, None); let widget = widgets::msg_placeholder(self.context.focused && highlighted, indent, None);
let widget = Self::predraw(widget, self.context.size, self.widthdb).await; let widget = Self::predraw(widget, self.context.size, self.widthdb);
Block::new(TreeBlockId::Msg(msg_id.clone()), widget, true) Block::new(TreeBlockId::Msg(msg_id.clone()), widget, true)
} }
async fn layout_bottom(&mut self) -> TreeBlocks<M::Id> { fn layout_bottom(&mut self) -> TreeBlocks<M::Id> {
let mut blocks = Blocks::new(0); let mut blocks = Blocks::new(0);
match self.cursor { match self.cursor {
Cursor::Editor { parent: None, .. } => { Cursor::Editor { parent: None, .. } => blocks.push_bottom(self.editor_block(0, None)),
blocks.push_bottom(self.editor_block(0, None).await) Cursor::Pseudo { parent: None, .. } => blocks.push_bottom(self.pseudo_block(0, None)),
} _ => blocks.push_bottom(self.zero_height_block(None)),
Cursor::Pseudo { parent: None, .. } => {
blocks.push_bottom(self.pseudo_block(0, None).await)
}
_ => blocks.push_bottom(self.zero_height_block(None).await),
} }
blocks blocks
} }
#[async_recursion] fn layout_subtree(
async fn layout_subtree(
&mut self, &mut self,
tree: &Tree<M>, tree: &Tree<M>,
indent: usize, indent: usize,
@ -228,16 +216,16 @@ where
) { ) {
// Message itself // Message itself
let block = if let Some(msg) = tree.msg(msg_id) { let block = if let Some(msg) = tree.msg(msg_id) {
self.message_block(indent, msg).await self.message_block(indent, msg)
} else { } else {
self.message_placeholder_block(indent, msg_id).await self.message_placeholder_block(indent, msg_id)
}; };
blocks.push_bottom(block); blocks.push_bottom(block);
// Children, recursively // Children, recursively
if let Some(children) = tree.children(msg_id) { if let Some(children) = tree.children(msg_id) {
for child in children { for child in children {
self.layout_subtree(tree, indent + 1, child, blocks).await; self.layout_subtree(tree, indent + 1, child, blocks);
} }
} }
@ -245,21 +233,20 @@ where
let block = match self.cursor { let block = match self.cursor {
Cursor::Editor { Cursor::Editor {
parent: Some(id), .. parent: Some(id), ..
} if id == msg_id => self.editor_block(indent + 1, Some(msg_id)).await, } if id == msg_id => self.editor_block(indent + 1, Some(msg_id)),
Cursor::Pseudo { Cursor::Pseudo {
parent: Some(id), .. parent: Some(id), ..
} if id == msg_id => self.pseudo_block(indent + 1, Some(msg_id)).await, } if id == msg_id => self.pseudo_block(indent + 1, Some(msg_id)),
_ => self.zero_height_block(Some(msg_id)).await, _ => self.zero_height_block(Some(msg_id)),
}; };
blocks.push_bottom(block); blocks.push_bottom(block);
} }
async fn layout_tree(&mut self, tree: Tree<M>) -> TreeBlocks<M::Id> { fn layout_tree(&mut self, tree: Tree<M>) -> TreeBlocks<M::Id> {
let mut blocks = Blocks::new(0); let mut blocks = Blocks::new(0);
self.layout_subtree(&tree, 0, tree.root(), &mut blocks) self.layout_subtree(&tree, 0, tree.root(), &mut blocks);
.await;
blocks blocks
} }
@ -275,9 +262,9 @@ where
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?;
self.layout_tree(tree).await self.layout_tree(tree)
} else { } else {
self.layout_bottom().await self.layout_bottom()
}; };
self.blocks.append_bottom(blocks); self.blocks.append_bottom(blocks);
@ -429,7 +416,7 @@ where
if let Some(prev_root_id) = prev_root_id { if let Some(prev_root_id) = prev_root_id {
let tree = self.store.tree(&prev_root_id).await?; let tree = self.store.tree(&prev_root_id).await?;
let blocks = self.layout_tree(tree).await; let blocks = self.layout_tree(tree);
self.blocks.append_top(blocks); self.blocks.append_top(blocks);
self.top_root_id = Some(prev_root_id); self.top_root_id = Some(prev_root_id);
} else { } else {
@ -448,11 +435,11 @@ where
let next_root_id = self.store.next_root_id(bottom_root_id).await?; let next_root_id = self.store.next_root_id(bottom_root_id).await?;
if let Some(next_root_id) = next_root_id { if let Some(next_root_id) = next_root_id {
let tree = self.store.tree(&next_root_id).await?; let tree = self.store.tree(&next_root_id).await?;
let blocks = self.layout_tree(tree).await; let blocks = self.layout_tree(tree);
self.blocks.append_bottom(blocks); self.blocks.append_bottom(blocks);
self.bottom_root_id = Some(next_root_id); self.bottom_root_id = Some(next_root_id);
} else { } else {
let blocks = self.layout_bottom().await; let blocks = self.layout_bottom();
self.blocks.append_bottom(blocks); self.blocks.append_bottom(blocks);
self.blocks.end_bottom(); self.blocks.end_bottom();
self.bottom_root_id = None; self.bottom_root_id = None;

View file

@ -1,7 +1,7 @@
use std::convert::Infallible; use std::convert::Infallible;
use crossterm::style::Stylize; use crossterm::style::Stylize;
use toss::widgets::{BoxedAsync, EditorState, Join2, Join4, Join5, Text}; use toss::widgets::{Boxed, EditorState, Join2, Join4, Join5, Text};
use toss::{Style, Styled, WidgetExt}; use toss::{Style, Styled, WidgetExt};
use crate::store::Msg; use crate::store::Msg;
@ -47,7 +47,7 @@ pub fn msg<M: Msg + ChatMsg>(
indent: usize, indent: usize,
msg: &M, msg: &M,
folded_info: Option<usize>, folded_info: Option<usize>,
) -> BoxedAsync<'static, Infallible> { ) -> Boxed<'static, Infallible> {
let (nick, mut content) = msg.styled(); let (nick, mut content) = msg.styled();
if let Some(amount) = folded_info { if let Some(amount) = folded_info {
@ -81,14 +81,14 @@ pub fn msg<M: Msg + ChatMsg>(
// TODO Minimizing and maximizing messages // TODO Minimizing and maximizing messages
Text::new(content).segment(), Text::new(content).segment(),
) )
.boxed_async() .boxed()
} }
pub fn msg_placeholder( pub fn msg_placeholder(
highlighted: bool, highlighted: bool,
indent: usize, indent: usize,
folded_info: Option<usize>, folded_info: Option<usize>,
) -> BoxedAsync<'static, Infallible> { ) -> Boxed<'static, Infallible> {
let mut content = Styled::new(PLACEHOLDER, style_placeholder()); let mut content = Styled::new(PLACEHOLDER, style_placeholder());
if let Some(amount) = folded_info { if let Some(amount) = folded_info {
@ -110,14 +110,14 @@ pub fn msg_placeholder(
.with_fixed(true), .with_fixed(true),
Text::new(content).segment(), Text::new(content).segment(),
) )
.boxed_async() .boxed()
} }
pub fn editor<'a, M: ChatMsg>( pub fn editor<'a, M: ChatMsg>(
indent: usize, indent: usize,
nick: &str, nick: &str,
editor: &'a mut EditorState, editor: &'a mut EditorState,
) -> BoxedAsync<'a, Infallible> { ) -> Boxed<'a, Infallible> {
let (nick, content) = M::edit(nick, editor.text()); let (nick, content) = M::edit(nick, editor.text());
let editor = editor.widget().with_highlight(|_| content); let editor = editor.widget().with_highlight(|_| content);
@ -144,14 +144,14 @@ pub fn editor<'a, M: ChatMsg>(
.with_fixed(true), .with_fixed(true),
editor.segment(), editor.segment(),
) )
.boxed_async() .boxed()
} }
pub fn pseudo<'a, M: ChatMsg>( pub fn pseudo<'a, M: ChatMsg>(
indent: usize, indent: usize,
nick: &str, nick: &str,
editor: &'a mut EditorState, editor: &'a mut EditorState,
) -> BoxedAsync<'a, Infallible> { ) -> Boxed<'a, Infallible> {
let (nick, content) = M::edit(nick, editor.text()); let (nick, content) = M::edit(nick, editor.text());
Join5::horizontal( Join5::horizontal(
@ -177,5 +177,5 @@ pub fn pseudo<'a, M: ChatMsg>(
.with_fixed(true), .with_fixed(true),
Text::new(content).segment(), Text::new(content).segment(),
) )
.boxed_async() .boxed()
} }

View file

@ -1,12 +1,11 @@
use std::convert::Infallible; use std::convert::Infallible;
use async_trait::async_trait;
use crossterm::style::Stylize; use crossterm::style::Stylize;
use time::format_description::FormatItem; use time::format_description::FormatItem;
use time::macros::format_description; use time::macros::format_description;
use time::OffsetDateTime; use time::OffsetDateTime;
use toss::widgets::{BoxedAsync, Empty, Text}; use toss::widgets::{Boxed, Empty, Text};
use toss::{AsyncWidget, Frame, Pos, Size, Style, WidgetExt, WidthDb}; use toss::{Frame, Pos, Size, Style, Widget, WidgetExt, WidthDb};
use crate::util::InfallibleExt; use crate::util::InfallibleExt;
@ -24,9 +23,8 @@ impl Indent {
} }
} }
#[async_trait] impl<E> Widget<E> for Indent {
impl<E> AsyncWidget<E> for Indent { fn size(
async fn size(
&self, &self,
_widthdb: &mut WidthDb, _widthdb: &mut WidthDb,
_max_width: Option<u16>, _max_width: Option<u16>,
@ -36,7 +34,7 @@ impl<E> AsyncWidget<E> for Indent {
Ok(Size::new(width, 0)) Ok(Size::new(width, 0))
} }
async fn draw(self, frame: &mut Frame) -> Result<(), E> { fn draw(self, frame: &mut Frame) -> Result<(), E> {
let size = frame.size(); let size = frame.size();
let indent_string = INDENT_STR.repeat(self.level); let indent_string = INDENT_STR.repeat(self.level);
@ -51,7 +49,7 @@ impl<E> AsyncWidget<E> for Indent {
const TIME_FORMAT: &[FormatItem<'_>] = format_description!("[year]-[month]-[day] [hour]:[minute]"); const TIME_FORMAT: &[FormatItem<'_>] = format_description!("[year]-[month]-[day] [hour]:[minute]");
const TIME_WIDTH: u16 = 16; const TIME_WIDTH: u16 = 16;
pub struct Time(BoxedAsync<'static, Infallible>); pub struct Time(Boxed<'static, Infallible>);
impl Time { impl Time {
pub fn new(time: Option<OffsetDateTime>, style: Style) -> Self { pub fn new(time: Option<OffsetDateTime>, style: Style) -> Self {
@ -60,70 +58,60 @@ impl Time {
Text::new((text, style)) Text::new((text, style))
.background() .background()
.with_style(style) .with_style(style)
.boxed_async() .boxed()
} else { } else {
Empty::new() Empty::new()
.with_width(TIME_WIDTH) .with_width(TIME_WIDTH)
.background() .background()
.with_style(style) .with_style(style)
.boxed_async() .boxed()
}; };
Self(widget) Self(widget)
} }
} }
#[async_trait] impl<E> Widget<E> for Time {
impl<E> AsyncWidget<E> for Time { fn size(
async fn size(
&self, &self,
widthdb: &mut WidthDb, widthdb: &mut WidthDb,
max_width: Option<u16>, max_width: Option<u16>,
max_height: Option<u16>, max_height: Option<u16>,
) -> Result<Size, E> { ) -> Result<Size, E> {
Ok(self Ok(self.0.size(widthdb, max_width, max_height).infallible())
.0
.size(widthdb, max_width, max_height)
.await
.infallible())
} }
async fn draw(self, frame: &mut Frame) -> Result<(), E> { fn draw(self, frame: &mut Frame) -> Result<(), E> {
self.0.draw(frame).await.infallible(); self.0.draw(frame).infallible();
Ok(()) Ok(())
} }
} }
pub struct Seen(BoxedAsync<'static, Infallible>); pub struct Seen(Boxed<'static, Infallible>);
impl Seen { impl Seen {
pub fn new(seen: bool) -> Self { pub fn new(seen: bool) -> Self {
let widget = if seen { let widget = if seen {
Empty::new().with_width(1).boxed_async() Empty::new().with_width(1).boxed()
} else { } else {
let style = Style::new().black().on_green(); let style = Style::new().black().on_green();
Text::new("*").background().with_style(style).boxed_async() Text::new("*").background().with_style(style).boxed()
}; };
Self(widget) Self(widget)
} }
} }
#[async_trait] impl<E> Widget<E> for Seen {
impl<E> AsyncWidget<E> for Seen { fn size(
async fn size(
&self, &self,
widthdb: &mut WidthDb, widthdb: &mut WidthDb,
max_width: Option<u16>, max_width: Option<u16>,
max_height: Option<u16>, max_height: Option<u16>,
) -> Result<Size, E> { ) -> Result<Size, E> {
Ok(self Ok(self.0.size(widthdb, max_width, max_height).infallible())
.0
.size(widthdb, max_width, max_height)
.await
.infallible())
} }
async fn draw(self, frame: &mut Frame) -> Result<(), E> { fn draw(self, frame: &mut Frame) -> Result<(), E> {
self.0.draw(frame).await.infallible(); self.0.draw(frame).infallible();
Ok(()) Ok(())
} }
} }