diff --git a/Cargo.lock b/Cargo.lock index 8839b7c..d9a92db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,6 +68,17 @@ version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "async-trait" version = "0.1.68" @@ -240,6 +251,7 @@ name = "cove" version = "0.6.1" dependencies = [ "anyhow", + "async-recursion", "async-trait", "clap", "cookie", diff --git a/Cargo.toml b/Cargo.toml index 63742b3..ef096ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] anyhow = "1.0.70" +async-recursion = "1.0.4" async-trait = "0.1.68" clap = { version = "4.2.1", features = ["derive", "deprecated"] } cookie = "0.17.0" diff --git a/src/ui/chat.rs b/src/ui/chat.rs index 475f7f0..f9f0367 100644 --- a/src/ui/chat.rs +++ b/src/ui/chat.rs @@ -11,7 +11,7 @@ use std::{fmt, io}; use async_trait::async_trait; use parking_lot::FairMutex; use time::OffsetDateTime; -use toss::{Frame, Size, Styled, Terminal}; +use toss::{Frame, Size, Styled, Terminal, WidthDb}; use crate::store::{Msg, MsgStore}; @@ -139,14 +139,19 @@ pub enum Chat> { #[async_trait] impl Widget for Chat where - M: Msg + ChatMsg, + M: Msg + ChatMsg + Send + Sync, M::Id: Send + Sync, S: MsgStore + Send + Sync, S::Error: fmt::Display, { - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size { match self { - Self::Tree(tree) => tree.size(frame, max_width, max_height), + Self::Tree(tree) => tree.size(widthdb, max_width, max_height).await, } } diff --git a/src/ui/chat/blocks.rs b/src/ui/chat/blocks.rs index 240981c..bb596cb 100644 --- a/src/ui/chat/blocks.rs +++ b/src/ui/chat/blocks.rs @@ -20,11 +20,12 @@ pub struct Block { } impl Block { - pub fn new>(frame: &mut Frame, id: I, widget: W) -> Self { + pub async fn new>(frame: &mut Frame, id: I, widget: W) -> Self { // Interestingly, rust-analyzer fails to deduce the type of `widget` // here but rustc knows it's a `BoxedWidget`. let widget = widget.into(); - let size = widget.size(frame, Some(frame.size().width), None); + let max_width = frame.size().width; + let size = widget.size(frame.widthdb(), Some(max_width), None).await; let height = size.height.into(); Self { id, diff --git a/src/ui/chat/tree.rs b/src/ui/chat/tree.rs index 0749ac4..49e1392 100644 --- a/src/ui/chat/tree.rs +++ b/src/ui/chat/tree.rs @@ -12,7 +12,7 @@ use std::sync::Arc; use async_trait::async_trait; use parking_lot::FairMutex; use tokio::sync::Mutex; -use toss::{Frame, Pos, Size, Terminal}; +use toss::{Frame, Pos, Size, Terminal, WidthDb}; use crate::macros::logging_unwrap; use crate::store::{Msg, MsgStore}; @@ -427,12 +427,17 @@ pub struct TreeView> { #[async_trait] impl Widget for TreeView where - M: Msg + ChatMsg, + M: Msg + ChatMsg + Send + Sync, M::Id: Send + Sync, S: MsgStore + Send + Sync, S::Error: fmt::Display, { - fn size(&self, _frame: &mut Frame, _max_width: Option, _max_height: Option) -> Size { + async fn size( + &self, + _widthdb: &mut WidthDb, + _max_width: Option, + _max_height: Option, + ) -> Size { Size::ZERO } diff --git a/src/ui/chat/tree/layout.rs b/src/ui/chat/tree/layout.rs index f474bf9..df5fa4a 100644 --- a/src/ui/chat/tree/layout.rs +++ b/src/ui/chat/tree/layout.rs @@ -1,3 +1,4 @@ +use async_recursion::async_recursion; use toss::Frame; use crate::store::{Msg, MsgStore, Path, Tree}; @@ -21,7 +22,12 @@ struct Context { focused: bool, } -impl> InnerTreeViewState { +impl InnerTreeViewState +where + M: Msg + ChatMsg + Send + Sync, + M::Id: Send + Sync, + S: MsgStore + Send + Sync, +{ async fn cursor_path(&self, cursor: &Cursor) -> Result, S::Error> { Ok(match cursor { Cursor::Msg(id) => self.store.path(id).await?, @@ -69,7 +75,7 @@ impl> InnerTreeViewState { .is_some() } - fn editor_block( + async fn editor_block( &self, context: &Context, frame: &mut Frame, @@ -78,20 +84,23 @@ impl> InnerTreeViewState { let (widget, cursor_row) = widgets::editor::(frame.widthdb(), indent, &context.nick, &self.editor); let cursor_row = cursor_row as i32; - Block::new(frame, BlockId::Cursor, widget).focus(cursor_row..cursor_row + 1) + Block::new(frame, BlockId::Cursor, widget) + .await + .focus(cursor_row..cursor_row + 1) } - fn pseudo_block( + async fn pseudo_block( &self, context: &Context, frame: &mut Frame, indent: usize, ) -> Block> { let widget = widgets::pseudo::(indent, &context.nick, &self.editor); - Block::new(frame, BlockId::Cursor, widget) + Block::new(frame, BlockId::Cursor, widget).await } - fn layout_subtree( + #[async_recursion] + async fn layout_subtree( &self, context: &Context, frame: &mut Frame, @@ -102,7 +111,7 @@ impl> InnerTreeViewState { ) { // Ghost cursor in front, for positioning according to last cursor line if self.last_cursor.refers_to(id) { - let block = Block::new(frame, BlockId::LastCursor, Empty::new()); + let block = Block::new(frame, BlockId::LastCursor, Empty::new()).await; blocks.blocks_mut().push_back(block); } @@ -121,43 +130,40 @@ impl> InnerTreeViewState { } else { widgets::msg_placeholder(highlighted, indent, folded_info) }; - let block = Block::new(frame, BlockId::Msg(id.clone()), widget); + let block = Block::new(frame, BlockId::Msg(id.clone()), widget).await; blocks.blocks_mut().push_back(block); // Children, recursively if !folded { if let Some(children) = tree.children(id) { for child in children { - self.layout_subtree(context, frame, tree, indent + 1, child, blocks); + self.layout_subtree(context, frame, tree, indent + 1, child, blocks) + .await; } } } // Trailing ghost cursor, for positioning according to last cursor line if self.last_cursor.refers_to_last_child_of(id) { - let block = Block::new(frame, BlockId::LastCursor, Empty::new()); + let block = Block::new(frame, BlockId::LastCursor, Empty::new()).await; blocks.blocks_mut().push_back(block); } // 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(context, frame, indent + 1)) - } - Cursor::Pseudo { .. } => { - blocks - .blocks_mut() - .push_back(self.pseudo_block(context, frame, indent + 1)) - } + Cursor::Editor { .. } => blocks + .blocks_mut() + .push_back(self.editor_block(context, frame, indent + 1).await), + Cursor::Pseudo { .. } => blocks + .blocks_mut() + .push_back(self.pseudo_block(context, frame, indent + 1).await), _ => {} } } } - fn layout_tree( + async fn layout_tree( &self, context: &Context, frame: &mut Frame, @@ -165,32 +171,33 @@ impl> InnerTreeViewState { ) -> TreeBlocks { let root = Root::Tree(tree.root().clone()); let mut blocks = TreeBlocks::new(root.clone(), root); - self.layout_subtree(context, frame, &tree, 0, tree.root(), &mut blocks); + self.layout_subtree(context, frame, &tree, 0, tree.root(), &mut blocks) + .await; blocks } - fn layout_bottom(&self, context: &Context, frame: &mut Frame) -> TreeBlocks { + async fn layout_bottom(&self, context: &Context, frame: &mut Frame) -> TreeBlocks { let mut blocks = TreeBlocks::new(Root::Bottom, Root::Bottom); // Ghost cursor, for positioning according to last cursor line if let Cursor::Editor { parent: None, .. } | Cursor::Pseudo { parent: None, .. } = self.last_cursor { - let block = Block::new(frame, BlockId::LastCursor, Empty::new()); + let block = Block::new(frame, BlockId::LastCursor, Empty::new()).await; blocks.blocks_mut().push_back(block); } match self.cursor { Cursor::Bottom => { - let block = Block::new(frame, BlockId::Cursor, Empty::new()); + let block = Block::new(frame, BlockId::Cursor, Empty::new()).await; blocks.blocks_mut().push_back(block); } Cursor::Editor { parent: None, .. } => blocks .blocks_mut() - .push_back(self.editor_block(context, frame, 0)), + .push_back(self.editor_block(context, frame, 0).await), Cursor::Pseudo { parent: None, .. } => blocks .blocks_mut() - .push_back(self.pseudo_block(context, frame, 0)), + .push_back(self.pseudo_block(context, frame, 0).await), _ => {} } @@ -216,7 +223,7 @@ impl> InnerTreeViewState { None => break, }; let prev_tree = self.store.tree(&prev_root_id).await?; - blocks.prepend(self.layout_tree(context, frame, prev_tree)); + blocks.prepend(self.layout_tree(context, frame, prev_tree).await); } Ok(()) @@ -238,9 +245,9 @@ impl> InnerTreeViewState { }; if let Some(next_root_id) = next_root_id { let next_tree = self.store.tree(&next_root_id).await?; - blocks.append(self.layout_tree(context, frame, next_tree)); + blocks.append(self.layout_tree(context, frame, next_tree).await); } else { - blocks.append(self.layout_bottom(context, frame)); + blocks.append(self.layout_bottom(context, frame).await); } } @@ -281,7 +288,7 @@ impl> InnerTreeViewState { ) -> Result, S::Error> { Ok(match &self.last_cursor { Cursor::Bottom => { - let mut blocks = self.layout_bottom(context, frame); + let mut blocks = self.layout_bottom(context, frame).await; let bottom_line = frame.size().height as i32 - 1; blocks.blocks_mut().set_bottom_line(bottom_line); @@ -289,7 +296,7 @@ impl> InnerTreeViewState { blocks } Cursor::Editor { parent: None, .. } | Cursor::Pseudo { parent: None, .. } => { - let mut blocks = self.layout_bottom(context, frame); + let mut blocks = self.layout_bottom(context, frame).await; blocks .blocks_mut() @@ -306,7 +313,7 @@ impl> InnerTreeViewState { } => { let root = last_cursor_path.first(); let tree = self.store.tree(root).await?; - let mut blocks = self.layout_tree(context, frame, tree); + let mut blocks = self.layout_tree(context, frame, tree).await; blocks .blocks_mut() @@ -330,7 +337,7 @@ impl> InnerTreeViewState { Cursor::Bottom | Cursor::Editor { parent: None, .. } | Cursor::Pseudo { parent: None, .. } => { - let mut blocks = self.layout_bottom(context, frame); + let mut blocks = self.layout_bottom(context, frame).await; blocks.blocks_mut().set_bottom_line(bottom_line); @@ -345,7 +352,7 @@ impl> InnerTreeViewState { } => { let root = cursor_path.first(); let tree = self.store.tree(root).await?; - let mut blocks = self.layout_tree(context, frame, tree); + let mut blocks = self.layout_tree(context, frame, tree).await; let cursor_above_last = cursor_path < last_cursor_path; let cursor_line = if cursor_above_last { 0 } else { bottom_line }; diff --git a/src/ui/chat/tree/widgets/indent.rs b/src/ui/chat/tree/widgets/indent.rs index 4e120c6..a226f93 100644 --- a/src/ui/chat/tree/widgets/indent.rs +++ b/src/ui/chat/tree/widgets/indent.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use toss::{Frame, Pos, Size, Style}; +use toss::{Frame, Pos, Size, Style, WidthDb}; use crate::ui::widgets::Widget; @@ -19,7 +19,12 @@ impl Indent { #[async_trait] impl Widget for Indent { - fn size(&self, _frame: &mut Frame, _max_width: Option, _max_height: Option) -> Size { + async fn size( + &self, + _widthdb: &mut WidthDb, + _max_width: Option, + _max_height: Option, + ) -> Size { Size::new((INDENT_WIDTH * self.level) as u16, 0) } diff --git a/src/ui/widgets.rs b/src/ui/widgets.rs index f5cb59c..20fb6d3 100644 --- a/src/ui/widgets.rs +++ b/src/ui/widgets.rs @@ -19,20 +19,25 @@ pub mod rules; pub mod text; use async_trait::async_trait; -use toss::{Frame, Size}; +use toss::{Frame, Size, WidthDb}; // TODO Add Error type and return Result-s (at least in Widget::render) #[async_trait] pub trait Widget { - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size; + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size; async fn render(self: Box, frame: &mut Frame); } -pub type BoxedWidget = Box; +pub type BoxedWidget = Box; -impl From for BoxedWidget { +impl From for BoxedWidget { fn from(widget: W) -> Self { Box::new(widget) } diff --git a/src/ui/widgets/background.rs b/src/ui/widgets/background.rs index f075a91..432e54d 100644 --- a/src/ui/widgets/background.rs +++ b/src/ui/widgets/background.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use toss::{Frame, Pos, Size, Style}; +use toss::{Frame, Pos, Size, Style, WidthDb}; use super::{BoxedWidget, Widget}; @@ -24,8 +24,13 @@ impl Background { #[async_trait] impl Widget for Background { - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { - self.inner.size(frame, max_width, max_height) + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size { + self.inner.size(widthdb, max_width, max_height).await } async fn render(self: Box, frame: &mut Frame) { diff --git a/src/ui/widgets/border.rs b/src/ui/widgets/border.rs index 134312b..bfd76ea 100644 --- a/src/ui/widgets/border.rs +++ b/src/ui/widgets/border.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use toss::{Frame, Pos, Size, Style}; +use toss::{Frame, Pos, Size, Style, WidthDb}; use super::{BoxedWidget, Widget}; @@ -24,10 +24,15 @@ impl Border { #[async_trait] impl Widget for Border { - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size { let max_width = max_width.map(|w| w.saturating_sub(2)); let max_height = max_height.map(|h| h.saturating_sub(2)); - let size = self.inner.size(frame, max_width, max_height); + let size = self.inner.size(widthdb, max_width, max_height).await; size + Size::new(2, 2) } diff --git a/src/ui/widgets/cursor.rs b/src/ui/widgets/cursor.rs index 09856f4..22ac5cc 100644 --- a/src/ui/widgets/cursor.rs +++ b/src/ui/widgets/cursor.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use toss::{Frame, Pos, Size}; +use toss::{Frame, Pos, Size, WidthDb}; use super::{BoxedWidget, Widget}; @@ -28,8 +28,13 @@ impl Cursor { #[async_trait] impl Widget for Cursor { - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { - self.inner.size(frame, max_width, max_height) + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size { + self.inner.size(widthdb, max_width, max_height).await } async fn render(self: Box, frame: &mut Frame) { diff --git a/src/ui/widgets/editor.rs b/src/ui/widgets/editor.rs index 088373e..e2b8955 100644 --- a/src/ui/widgets/editor.rs +++ b/src/ui/widgets/editor.rs @@ -493,9 +493,14 @@ impl Editor { #[async_trait] impl Widget for Editor { - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size { if let Some(placeholder) = &self.hidden { - let mut size = placeholder.size(frame, max_width, max_height); + let mut size = placeholder.size(widthdb, max_width, max_height).await; // Cursor needs to fit regardless of focus size.width = size.width.max(1); @@ -504,8 +509,6 @@ impl Widget for Editor { return size; } - let widthdb = frame.widthdb(); - let max_width = max_width.map(|w| w as usize).unwrap_or(usize::MAX).max(1); let max_text_width = max_width - 1; let indices = wrap(widthdb, self.text.text(), max_text_width); diff --git a/src/ui/widgets/empty.rs b/src/ui/widgets/empty.rs index 790c8b1..a5d98ea 100644 --- a/src/ui/widgets/empty.rs +++ b/src/ui/widgets/empty.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use toss::{Frame, Size}; +use toss::{Frame, Size, WidthDb}; use super::Widget; @@ -31,7 +31,12 @@ impl Empty { #[async_trait] impl Widget for Empty { - fn size(&self, _frame: &mut Frame, _max_width: Option, _max_height: Option) -> Size { + async fn size( + &self, + _widthdb: &mut WidthDb, + _max_width: Option, + _max_height: Option, + ) -> Size { self.size } diff --git a/src/ui/widgets/float.rs b/src/ui/widgets/float.rs index 6a82284..a262cd6 100644 --- a/src/ui/widgets/float.rs +++ b/src/ui/widgets/float.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use toss::{Frame, Pos, Size}; +use toss::{Frame, Pos, Size, WidthDb}; use super::{BoxedWidget, Widget}; @@ -31,14 +31,22 @@ impl Float { #[async_trait] impl Widget for Float { - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { - self.inner.size(frame, max_width, max_height) + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size { + self.inner.size(widthdb, max_width, max_height).await } async fn render(self: Box, frame: &mut Frame) { let size = frame.size(); - let mut inner_size = self.inner.size(frame, Some(size.width), Some(size.height)); + let mut inner_size = self + .inner + .size(frame.widthdb(), Some(size.width), Some(size.height)) + .await; inner_size.width = inner_size.width.min(size.width); inner_size.height = inner_size.height.min(size.height); diff --git a/src/ui/widgets/join.rs b/src/ui/widgets/join.rs index 00d18fa..2aa4551 100644 --- a/src/ui/widgets/join.rs +++ b/src/ui/widgets/join.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use toss::{Frame, Pos, Size}; +use toss::{Frame, Pos, Size, WidthDb}; use super::{BoxedWidget, Widget}; @@ -54,9 +54,9 @@ impl SizedSegment { } } -fn sizes_horiz( +async fn sizes_horiz( segments: &[Segment], - frame: &mut Frame, + widthdb: &mut WidthDb, max_width: Option, max_height: Option, ) -> Vec { @@ -74,7 +74,8 @@ fn sizes_horiz( .map(|w| w.saturating_sub(total_width)); s.size = segments[s.idx] .widget - .size(frame, available_width, max_height); + .size(widthdb, available_width, max_height) + .await; if let Some(available_width) = available_width { s.size.width = s.size.width.min(available_width); } @@ -84,9 +85,9 @@ fn sizes_horiz( sized } -fn sizes_vert( +async fn sizes_vert( segments: &[Segment], - frame: &mut Frame, + widthdb: &mut WidthDb, max_width: Option, max_height: Option, ) -> Vec { @@ -104,7 +105,8 @@ fn sizes_vert( .map(|w| w.saturating_sub(total_height)); s.size = segments[s.idx] .widget - .size(frame, max_width, available_height); + .size(widthdb, max_width, available_height) + .await; if let Some(available_height) = available_height { s.size.height = s.size.height.min(available_height); } @@ -177,8 +179,13 @@ impl HJoin { #[async_trait] impl Widget for HJoin { - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { - let sizes = sizes_horiz(&self.segments, frame, max_width, max_height); + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size { + let sizes = sizes_horiz(&self.segments, widthdb, max_width, max_height).await; let width = sizes.iter().map(|s| s.size.width).sum::(); let height = sizes.iter().map(|s| s.size.height).max().unwrap_or(0); Size::new(width, height) @@ -187,7 +194,13 @@ impl Widget for HJoin { async fn render(self: Box, frame: &mut Frame) { let size = frame.size(); - let mut sizes = sizes_horiz(&self.segments, frame, Some(size.width), Some(size.height)); + let mut sizes = sizes_horiz( + &self.segments, + frame.widthdb(), + Some(size.width), + Some(size.height), + ) + .await; expand_horiz(&mut sizes, size.width); sizes.sort_by_key(|s| s.idx); @@ -215,8 +228,13 @@ impl VJoin { #[async_trait] impl Widget for VJoin { - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { - let sizes = sizes_vert(&self.segments, frame, max_width, max_height); + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size { + let sizes = sizes_vert(&self.segments, widthdb, max_width, max_height).await; let width = sizes.iter().map(|s| s.size.width).max().unwrap_or(0); let height = sizes.iter().map(|s| s.size.height).sum::(); Size::new(width, height) @@ -225,7 +243,13 @@ impl Widget for VJoin { async fn render(self: Box, frame: &mut Frame) { let size = frame.size(); - let mut sizes = sizes_vert(&self.segments, frame, Some(size.width), Some(size.height)); + let mut sizes = sizes_vert( + &self.segments, + frame.widthdb(), + Some(size.width), + Some(size.height), + ) + .await; expand_vert(&mut sizes, size.height); sizes.sort_by_key(|s| s.idx); diff --git a/src/ui/widgets/layer.rs b/src/ui/widgets/layer.rs index 30807e6..fe0d983 100644 --- a/src/ui/widgets/layer.rs +++ b/src/ui/widgets/layer.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use toss::{Frame, Size}; +use toss::{Frame, Size, WidthDb}; use super::{BoxedWidget, Widget}; @@ -15,10 +15,15 @@ impl Layer { #[async_trait] impl Widget for Layer { - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size { let mut max_size = Size::ZERO; for layer in &self.layers { - let size = layer.size(frame, max_width, max_height); + let size = layer.size(widthdb, max_width, max_height).await; max_size.width = max_size.width.max(size.width); max_size.height = max_size.height.max(size.height); } diff --git a/src/ui/widgets/list.rs b/src/ui/widgets/list.rs index 0dcd0aa..fc148a0 100644 --- a/src/ui/widgets/list.rs +++ b/src/ui/widgets/list.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use async_trait::async_trait; use parking_lot::Mutex; -use toss::{Frame, Pos, Size}; +use toss::{Frame, Pos, Size, WidthDb}; use super::{BoxedWidget, Widget}; @@ -266,14 +266,19 @@ impl Row { } } - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size { match self { - Self::Unselectable { normal } => normal.size(frame, max_width, max_height), + Self::Unselectable { normal } => normal.size(widthdb, max_width, max_height).await, Self::Selectable { normal, selected, .. } => { - let normal_size = normal.size(frame, max_width, max_height); - let selected_size = selected.size(frame, max_width, max_height); + let normal_size = normal.size(widthdb, max_width, max_height).await; + let selected_size = selected.size(widthdb, max_width, max_height).await; Size::new( normal_size.width.max(selected_size.width), normal_size.height.max(selected_size.height), @@ -327,14 +332,18 @@ impl List { } #[async_trait] -impl Widget for List { - fn size(&self, frame: &mut Frame, max_width: Option, _max_height: Option) -> Size { - let width = self - .rows - .iter() - .map(|r| r.size(frame, max_width, Some(1)).width) - .max() - .unwrap_or(0); +impl Widget for List { + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + _max_height: Option, + ) -> Size { + let mut width = 0; + for row in &self.rows { + let size = row.size(widthdb, max_width, Some(1)).await; + width = width.max(size.width); + } let height = self.rows.len(); Size::new(width, height as u16) } diff --git a/src/ui/widgets/padding.rs b/src/ui/widgets/padding.rs index e2be11d..e5b45cd 100644 --- a/src/ui/widgets/padding.rs +++ b/src/ui/widgets/padding.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use toss::{Frame, Pos, Size}; +use toss::{Frame, Pos, Size, WidthDb}; use super::{BoxedWidget, Widget}; @@ -66,14 +66,19 @@ impl Padding { #[async_trait] impl Widget for Padding { - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size { let horizontal = self.left + self.right; let vertical = self.top + self.bottom; let max_width = max_width.map(|w| w.saturating_sub(horizontal)); let max_height = max_height.map(|h| h.saturating_sub(vertical)); - let size = self.inner.size(frame, max_width, max_height); + let size = self.inner.size(widthdb, max_width, max_height).await; size + Size::new(horizontal, vertical) } diff --git a/src/ui/widgets/resize.rs b/src/ui/widgets/resize.rs index 67c07ed..b2edf22 100644 --- a/src/ui/widgets/resize.rs +++ b/src/ui/widgets/resize.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use toss::{Frame, Size}; +use toss::{Frame, Size, WidthDb}; use super::{BoxedWidget, Widget}; @@ -45,7 +45,12 @@ impl Resize { #[async_trait] impl Widget for Resize { - fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + max_height: Option, + ) -> Size { let max_width = match (max_width, self.max_width) { (None, None) => None, (Some(w), None) => Some(w), @@ -60,7 +65,7 @@ impl Widget for Resize { (Some(h), Some(sh)) => Some(h.min(sh)), }; - let size = self.inner.size(frame, max_width, max_height); + let size = self.inner.size(widthdb, max_width, max_height).await; let width = match self.min_width { Some(min_width) => size.width.max(min_width), diff --git a/src/ui/widgets/rules.rs b/src/ui/widgets/rules.rs index f6b01c1..eaff35f 100644 --- a/src/ui/widgets/rules.rs +++ b/src/ui/widgets/rules.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use toss::{Frame, Pos, Size}; +use toss::{Frame, Pos, Size, WidthDb}; use super::Widget; @@ -7,7 +7,12 @@ pub struct HRule; #[async_trait] impl Widget for HRule { - fn size(&self, _frame: &mut Frame, _max_width: Option, _max_height: Option) -> Size { + async fn size( + &self, + _widthdb: &mut WidthDb, + _max_width: Option, + _max_height: Option, + ) -> Size { Size::new(0, 1) } @@ -23,7 +28,12 @@ pub struct VRule; #[async_trait] impl Widget for VRule { - fn size(&self, _frame: &mut Frame, _max_width: Option, _max_height: Option) -> Size { + async fn size( + &self, + _widthdb: &mut WidthDb, + _max_width: Option, + _max_height: Option, + ) -> Size { Size::new(1, 0) } diff --git a/src/ui/widgets/text.rs b/src/ui/widgets/text.rs index 1a383f5..3ce1dbe 100644 --- a/src/ui/widgets/text.rs +++ b/src/ui/widgets/text.rs @@ -36,9 +36,13 @@ impl Text { #[async_trait] impl Widget for Text { - fn size(&self, frame: &mut Frame, max_width: Option, _max_height: Option) -> Size { - let lines = self.wrapped(frame.widthdb(), max_width); - let widthdb = frame.widthdb(); + async fn size( + &self, + widthdb: &mut WidthDb, + max_width: Option, + _max_height: Option, + ) -> Size { + let lines = self.wrapped(widthdb, max_width); let min_width = lines .iter() .map(|l| widthdb.width(l.text().trim_end()))