Make Widget::size like toss::AsyncWidget::size

This commit is contained in:
Joscha 2023-04-05 23:08:53 +02:00
parent 059ff94aef
commit ff9a16d8a3
21 changed files with 244 additions and 110 deletions

12
Cargo.lock generated
View file

@ -68,6 +68,17 @@ 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"
@ -240,6 +251,7 @@ 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,6 +5,7 @@ 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

@ -11,7 +11,7 @@ use std::{fmt, io};
use async_trait::async_trait; use async_trait::async_trait;
use parking_lot::FairMutex; use parking_lot::FairMutex;
use time::OffsetDateTime; use time::OffsetDateTime;
use toss::{Frame, Size, Styled, Terminal}; use toss::{Frame, Size, Styled, Terminal, WidthDb};
use crate::store::{Msg, MsgStore}; use crate::store::{Msg, MsgStore};
@ -139,14 +139,19 @@ pub enum Chat<M: Msg, S: MsgStore<M>> {
#[async_trait] #[async_trait]
impl<M, S> Widget for Chat<M, S> impl<M, S> Widget for Chat<M, S>
where where
M: Msg + ChatMsg, M: Msg + ChatMsg + Send + Sync,
M::Id: Send + Sync, M::Id: Send + Sync,
S: MsgStore<M> + Send + Sync, S: MsgStore<M> + Send + Sync,
S::Error: fmt::Display, S::Error: fmt::Display,
{ {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size { async fn size(
&self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Size {
match self { match self {
Self::Tree(tree) => tree.size(frame, max_width, max_height), Self::Tree(tree) => tree.size(widthdb, max_width, max_height).await,
} }
} }

View file

@ -20,11 +20,12 @@ pub struct Block<I> {
} }
impl<I> Block<I> { impl<I> Block<I> {
pub fn new<W: Into<BoxedWidget>>(frame: &mut Frame, id: I, widget: W) -> Self { pub async fn new<W: Into<BoxedWidget>>(frame: &mut Frame, id: I, widget: W) -> Self {
// Interestingly, rust-analyzer fails to deduce the type of `widget` // Interestingly, rust-analyzer fails to deduce the type of `widget`
// here but rustc knows it's a `BoxedWidget`. // here but rustc knows it's a `BoxedWidget`.
let widget = widget.into(); 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(); let height = size.height.into();
Self { Self {
id, id,

View file

@ -12,7 +12,7 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use parking_lot::FairMutex; use parking_lot::FairMutex;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use toss::{Frame, Pos, Size, Terminal}; use toss::{Frame, Pos, Size, Terminal, WidthDb};
use crate::macros::logging_unwrap; use crate::macros::logging_unwrap;
use crate::store::{Msg, MsgStore}; use crate::store::{Msg, MsgStore};
@ -427,12 +427,17 @@ pub struct TreeView<M: Msg, S: MsgStore<M>> {
#[async_trait] #[async_trait]
impl<M, S> Widget for TreeView<M, S> impl<M, S> Widget for TreeView<M, S>
where where
M: Msg + ChatMsg, M: Msg + ChatMsg + Send + Sync,
M::Id: Send + Sync, M::Id: Send + Sync,
S: MsgStore<M> + Send + Sync, S: MsgStore<M> + Send + Sync,
S::Error: fmt::Display, S::Error: fmt::Display,
{ {
fn size(&self, _frame: &mut Frame, _max_width: Option<u16>, _max_height: Option<u16>) -> Size { async fn size(
&self,
_widthdb: &mut WidthDb,
_max_width: Option<u16>,
_max_height: Option<u16>,
) -> Size {
Size::ZERO Size::ZERO
} }

View file

@ -1,3 +1,4 @@
use async_recursion::async_recursion;
use toss::Frame; use toss::Frame;
use crate::store::{Msg, MsgStore, Path, Tree}; use crate::store::{Msg, MsgStore, Path, Tree};
@ -21,7 +22,12 @@ struct Context {
focused: bool, focused: bool,
} }
impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> { impl<M, S> InnerTreeViewState<M, S>
where
M: Msg + ChatMsg + Send + Sync,
M::Id: Send + Sync,
S: MsgStore<M> + Send + Sync,
{
async fn cursor_path(&self, cursor: &Cursor<M::Id>) -> Result<Path<M::Id>, S::Error> { async fn cursor_path(&self, cursor: &Cursor<M::Id>) -> Result<Path<M::Id>, S::Error> {
Ok(match cursor { Ok(match cursor {
Cursor::Msg(id) => self.store.path(id).await?, Cursor::Msg(id) => self.store.path(id).await?,
@ -69,7 +75,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
.is_some() .is_some()
} }
fn editor_block( async fn editor_block(
&self, &self,
context: &Context, context: &Context,
frame: &mut Frame, frame: &mut Frame,
@ -78,20 +84,23 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
let (widget, cursor_row) = let (widget, cursor_row) =
widgets::editor::<M>(frame.widthdb(), indent, &context.nick, &self.editor); widgets::editor::<M>(frame.widthdb(), indent, &context.nick, &self.editor);
let cursor_row = cursor_row as i32; 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, &self,
context: &Context, context: &Context,
frame: &mut Frame, frame: &mut Frame,
indent: usize, indent: usize,
) -> Block<BlockId<M::Id>> { ) -> Block<BlockId<M::Id>> {
let widget = widgets::pseudo::<M>(indent, &context.nick, &self.editor); let widget = widgets::pseudo::<M>(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, &self,
context: &Context, context: &Context,
frame: &mut Frame, frame: &mut Frame,
@ -102,7 +111,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
) { ) {
// Ghost cursor in front, for positioning according to last cursor line // Ghost cursor in front, for positioning according to last cursor line
if self.last_cursor.refers_to(id) { 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); blocks.blocks_mut().push_back(block);
} }
@ -121,43 +130,40 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
} else { } else {
widgets::msg_placeholder(highlighted, indent, folded_info) 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); blocks.blocks_mut().push_back(block);
// Children, recursively // Children, recursively
if !folded { if !folded {
if let Some(children) = tree.children(id) { if let Some(children) = tree.children(id) {
for child in children { 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 // Trailing ghost cursor, for positioning according to last cursor line
if self.last_cursor.refers_to_last_child_of(id) { 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); blocks.blocks_mut().push_back(block);
} }
// Trailing editor or pseudomessage // Trailing editor or pseudomessage
if self.cursor.refers_to_last_child_of(id) { if self.cursor.refers_to_last_child_of(id) {
match self.cursor { match self.cursor {
Cursor::Editor { .. } => { Cursor::Editor { .. } => blocks
blocks .blocks_mut()
.blocks_mut() .push_back(self.editor_block(context, frame, indent + 1).await),
.push_back(self.editor_block(context, frame, indent + 1)) Cursor::Pseudo { .. } => blocks
} .blocks_mut()
Cursor::Pseudo { .. } => { .push_back(self.pseudo_block(context, frame, indent + 1).await),
blocks
.blocks_mut()
.push_back(self.pseudo_block(context, frame, indent + 1))
}
_ => {} _ => {}
} }
} }
} }
fn layout_tree( async fn layout_tree(
&self, &self,
context: &Context, context: &Context,
frame: &mut Frame, frame: &mut Frame,
@ -165,32 +171,33 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
) -> TreeBlocks<M::Id> { ) -> TreeBlocks<M::Id> {
let root = Root::Tree(tree.root().clone()); let root = Root::Tree(tree.root().clone());
let mut blocks = TreeBlocks::new(root.clone(), root); 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 blocks
} }
fn layout_bottom(&self, context: &Context, frame: &mut Frame) -> TreeBlocks<M::Id> { async fn layout_bottom(&self, context: &Context, frame: &mut Frame) -> TreeBlocks<M::Id> {
let mut blocks = TreeBlocks::new(Root::Bottom, Root::Bottom); let mut blocks = TreeBlocks::new(Root::Bottom, Root::Bottom);
// Ghost cursor, for positioning according to last cursor line // Ghost cursor, for positioning according to last cursor line
if let Cursor::Editor { parent: None, .. } | Cursor::Pseudo { parent: None, .. } = if let Cursor::Editor { parent: None, .. } | Cursor::Pseudo { parent: None, .. } =
self.last_cursor 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); blocks.blocks_mut().push_back(block);
} }
match self.cursor { match self.cursor {
Cursor::Bottom => { 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); blocks.blocks_mut().push_back(block);
} }
Cursor::Editor { parent: None, .. } => blocks Cursor::Editor { parent: None, .. } => blocks
.blocks_mut() .blocks_mut()
.push_back(self.editor_block(context, frame, 0)), .push_back(self.editor_block(context, frame, 0).await),
Cursor::Pseudo { parent: None, .. } => blocks Cursor::Pseudo { parent: None, .. } => blocks
.blocks_mut() .blocks_mut()
.push_back(self.pseudo_block(context, frame, 0)), .push_back(self.pseudo_block(context, frame, 0).await),
_ => {} _ => {}
} }
@ -216,7 +223,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
None => break, None => break,
}; };
let prev_tree = self.store.tree(&prev_root_id).await?; 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(()) Ok(())
@ -238,9 +245,9 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
}; };
if let Some(next_root_id) = next_root_id { if let Some(next_root_id) = next_root_id {
let next_tree = self.store.tree(&next_root_id).await?; 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 { } else {
blocks.append(self.layout_bottom(context, frame)); blocks.append(self.layout_bottom(context, frame).await);
} }
} }
@ -281,7 +288,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
) -> Result<TreeBlocks<M::Id>, S::Error> { ) -> Result<TreeBlocks<M::Id>, S::Error> {
Ok(match &self.last_cursor { Ok(match &self.last_cursor {
Cursor::Bottom => { 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; let bottom_line = frame.size().height as i32 - 1;
blocks.blocks_mut().set_bottom_line(bottom_line); blocks.blocks_mut().set_bottom_line(bottom_line);
@ -289,7 +296,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
blocks blocks
} }
Cursor::Editor { parent: None, .. } | Cursor::Pseudo { parent: None, .. } => { 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
.blocks_mut() .blocks_mut()
@ -306,7 +313,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
} => { } => {
let root = last_cursor_path.first(); let root = last_cursor_path.first();
let tree = self.store.tree(root).await?; 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
.blocks_mut() .blocks_mut()
@ -330,7 +337,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
Cursor::Bottom Cursor::Bottom
| Cursor::Editor { parent: None, .. } | Cursor::Editor { parent: None, .. }
| Cursor::Pseudo { 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); blocks.blocks_mut().set_bottom_line(bottom_line);
@ -345,7 +352,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
} => { } => {
let root = cursor_path.first(); let root = cursor_path.first();
let tree = self.store.tree(root).await?; 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_above_last = cursor_path < last_cursor_path;
let cursor_line = if cursor_above_last { 0 } else { bottom_line }; let cursor_line = if cursor_above_last { 0 } else { bottom_line };

View file

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use toss::{Frame, Pos, Size, Style}; use toss::{Frame, Pos, Size, Style, WidthDb};
use crate::ui::widgets::Widget; use crate::ui::widgets::Widget;
@ -19,7 +19,12 @@ impl Indent {
#[async_trait] #[async_trait]
impl Widget for Indent { impl Widget for Indent {
fn size(&self, _frame: &mut Frame, _max_width: Option<u16>, _max_height: Option<u16>) -> Size { async fn size(
&self,
_widthdb: &mut WidthDb,
_max_width: Option<u16>,
_max_height: Option<u16>,
) -> Size {
Size::new((INDENT_WIDTH * self.level) as u16, 0) Size::new((INDENT_WIDTH * self.level) as u16, 0)
} }

View file

@ -19,20 +19,25 @@ pub mod rules;
pub mod text; pub mod text;
use async_trait::async_trait; 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) // TODO Add Error type and return Result-s (at least in Widget::render)
#[async_trait] #[async_trait]
pub trait Widget { pub trait Widget {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size; async fn size(
&self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Size;
async fn render(self: Box<Self>, frame: &mut Frame); async fn render(self: Box<Self>, frame: &mut Frame);
} }
pub type BoxedWidget = Box<dyn Widget + Send>; pub type BoxedWidget = Box<dyn Widget + Send + Sync>;
impl<W: 'static + Widget + Send> From<W> for BoxedWidget { impl<W: 'static + Widget + Send + Sync> From<W> for BoxedWidget {
fn from(widget: W) -> Self { fn from(widget: W) -> Self {
Box::new(widget) Box::new(widget)
} }

View file

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use toss::{Frame, Pos, Size, Style}; use toss::{Frame, Pos, Size, Style, WidthDb};
use super::{BoxedWidget, Widget}; use super::{BoxedWidget, Widget};
@ -24,8 +24,13 @@ impl Background {
#[async_trait] #[async_trait]
impl Widget for Background { impl Widget for Background {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size { async fn size(
self.inner.size(frame, max_width, max_height) &self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Size {
self.inner.size(widthdb, max_width, max_height).await
} }
async fn render(self: Box<Self>, frame: &mut Frame) { async fn render(self: Box<Self>, frame: &mut Frame) {

View file

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use toss::{Frame, Pos, Size, Style}; use toss::{Frame, Pos, Size, Style, WidthDb};
use super::{BoxedWidget, Widget}; use super::{BoxedWidget, Widget};
@ -24,10 +24,15 @@ impl Border {
#[async_trait] #[async_trait]
impl Widget for Border { impl Widget for Border {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size { async fn size(
&self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Size {
let max_width = max_width.map(|w| w.saturating_sub(2)); let max_width = max_width.map(|w| w.saturating_sub(2));
let max_height = max_height.map(|h| h.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) size + Size::new(2, 2)
} }

View file

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use toss::{Frame, Pos, Size}; use toss::{Frame, Pos, Size, WidthDb};
use super::{BoxedWidget, Widget}; use super::{BoxedWidget, Widget};
@ -28,8 +28,13 @@ impl Cursor {
#[async_trait] #[async_trait]
impl Widget for Cursor { impl Widget for Cursor {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size { async fn size(
self.inner.size(frame, max_width, max_height) &self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Size {
self.inner.size(widthdb, max_width, max_height).await
} }
async fn render(self: Box<Self>, frame: &mut Frame) { async fn render(self: Box<Self>, frame: &mut Frame) {

View file

@ -493,9 +493,14 @@ impl Editor {
#[async_trait] #[async_trait]
impl Widget for Editor { impl Widget for Editor {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size { async fn size(
&self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Size {
if let Some(placeholder) = &self.hidden { 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 // Cursor needs to fit regardless of focus
size.width = size.width.max(1); size.width = size.width.max(1);
@ -504,8 +509,6 @@ impl Widget for Editor {
return size; return size;
} }
let widthdb = frame.widthdb();
let max_width = max_width.map(|w| w as usize).unwrap_or(usize::MAX).max(1); let max_width = max_width.map(|w| w as usize).unwrap_or(usize::MAX).max(1);
let max_text_width = max_width - 1; let max_text_width = max_width - 1;
let indices = wrap(widthdb, self.text.text(), max_text_width); let indices = wrap(widthdb, self.text.text(), max_text_width);

View file

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use toss::{Frame, Size}; use toss::{Frame, Size, WidthDb};
use super::Widget; use super::Widget;
@ -31,7 +31,12 @@ impl Empty {
#[async_trait] #[async_trait]
impl Widget for Empty { impl Widget for Empty {
fn size(&self, _frame: &mut Frame, _max_width: Option<u16>, _max_height: Option<u16>) -> Size { async fn size(
&self,
_widthdb: &mut WidthDb,
_max_width: Option<u16>,
_max_height: Option<u16>,
) -> Size {
self.size self.size
} }

View file

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use toss::{Frame, Pos, Size}; use toss::{Frame, Pos, Size, WidthDb};
use super::{BoxedWidget, Widget}; use super::{BoxedWidget, Widget};
@ -31,14 +31,22 @@ impl Float {
#[async_trait] #[async_trait]
impl Widget for Float { impl Widget for Float {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size { async fn size(
self.inner.size(frame, max_width, max_height) &self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Size {
self.inner.size(widthdb, max_width, max_height).await
} }
async fn render(self: Box<Self>, frame: &mut Frame) { async fn render(self: Box<Self>, frame: &mut Frame) {
let size = frame.size(); 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.width = inner_size.width.min(size.width);
inner_size.height = inner_size.height.min(size.height); inner_size.height = inner_size.height.min(size.height);

View file

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use toss::{Frame, Pos, Size}; use toss::{Frame, Pos, Size, WidthDb};
use super::{BoxedWidget, Widget}; use super::{BoxedWidget, Widget};
@ -54,9 +54,9 @@ impl SizedSegment {
} }
} }
fn sizes_horiz( async fn sizes_horiz(
segments: &[Segment], segments: &[Segment],
frame: &mut Frame, widthdb: &mut WidthDb,
max_width: Option<u16>, max_width: Option<u16>,
max_height: Option<u16>, max_height: Option<u16>,
) -> Vec<SizedSegment> { ) -> Vec<SizedSegment> {
@ -74,7 +74,8 @@ fn sizes_horiz(
.map(|w| w.saturating_sub(total_width)); .map(|w| w.saturating_sub(total_width));
s.size = segments[s.idx] s.size = segments[s.idx]
.widget .widget
.size(frame, available_width, max_height); .size(widthdb, available_width, max_height)
.await;
if let Some(available_width) = available_width { if let Some(available_width) = available_width {
s.size.width = s.size.width.min(available_width); s.size.width = s.size.width.min(available_width);
} }
@ -84,9 +85,9 @@ fn sizes_horiz(
sized sized
} }
fn sizes_vert( async fn sizes_vert(
segments: &[Segment], segments: &[Segment],
frame: &mut Frame, widthdb: &mut WidthDb,
max_width: Option<u16>, max_width: Option<u16>,
max_height: Option<u16>, max_height: Option<u16>,
) -> Vec<SizedSegment> { ) -> Vec<SizedSegment> {
@ -104,7 +105,8 @@ fn sizes_vert(
.map(|w| w.saturating_sub(total_height)); .map(|w| w.saturating_sub(total_height));
s.size = segments[s.idx] s.size = segments[s.idx]
.widget .widget
.size(frame, max_width, available_height); .size(widthdb, max_width, available_height)
.await;
if let Some(available_height) = available_height { if let Some(available_height) = available_height {
s.size.height = s.size.height.min(available_height); s.size.height = s.size.height.min(available_height);
} }
@ -177,8 +179,13 @@ impl HJoin {
#[async_trait] #[async_trait]
impl Widget for HJoin { impl Widget for HJoin {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size { async fn size(
let sizes = sizes_horiz(&self.segments, frame, max_width, max_height); &self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Size {
let sizes = sizes_horiz(&self.segments, widthdb, max_width, max_height).await;
let width = sizes.iter().map(|s| s.size.width).sum::<u16>(); let width = sizes.iter().map(|s| s.size.width).sum::<u16>();
let height = sizes.iter().map(|s| s.size.height).max().unwrap_or(0); let height = sizes.iter().map(|s| s.size.height).max().unwrap_or(0);
Size::new(width, height) Size::new(width, height)
@ -187,7 +194,13 @@ impl Widget for HJoin {
async fn render(self: Box<Self>, frame: &mut Frame) { async fn render(self: Box<Self>, frame: &mut Frame) {
let size = frame.size(); 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); expand_horiz(&mut sizes, size.width);
sizes.sort_by_key(|s| s.idx); sizes.sort_by_key(|s| s.idx);
@ -215,8 +228,13 @@ impl VJoin {
#[async_trait] #[async_trait]
impl Widget for VJoin { impl Widget for VJoin {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size { async fn size(
let sizes = sizes_vert(&self.segments, frame, max_width, max_height); &self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> 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 width = sizes.iter().map(|s| s.size.width).max().unwrap_or(0);
let height = sizes.iter().map(|s| s.size.height).sum::<u16>(); let height = sizes.iter().map(|s| s.size.height).sum::<u16>();
Size::new(width, height) Size::new(width, height)
@ -225,7 +243,13 @@ impl Widget for VJoin {
async fn render(self: Box<Self>, frame: &mut Frame) { async fn render(self: Box<Self>, frame: &mut Frame) {
let size = frame.size(); 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); expand_vert(&mut sizes, size.height);
sizes.sort_by_key(|s| s.idx); sizes.sort_by_key(|s| s.idx);

View file

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use toss::{Frame, Size}; use toss::{Frame, Size, WidthDb};
use super::{BoxedWidget, Widget}; use super::{BoxedWidget, Widget};
@ -15,10 +15,15 @@ impl Layer {
#[async_trait] #[async_trait]
impl Widget for Layer { impl Widget for Layer {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size { async fn size(
&self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Size {
let mut max_size = Size::ZERO; let mut max_size = Size::ZERO;
for layer in &self.layers { 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.width = max_size.width.max(size.width);
max_size.height = max_size.height.max(size.height); max_size.height = max_size.height.max(size.height);
} }

View file

@ -2,7 +2,7 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use parking_lot::Mutex; use parking_lot::Mutex;
use toss::{Frame, Pos, Size}; use toss::{Frame, Pos, Size, WidthDb};
use super::{BoxedWidget, Widget}; use super::{BoxedWidget, Widget};
@ -266,14 +266,19 @@ impl<Id> Row<Id> {
} }
} }
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size { async fn size(
&self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Size {
match self { 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 { Self::Selectable {
normal, selected, .. normal, selected, ..
} => { } => {
let normal_size = normal.size(frame, max_width, max_height); let normal_size = normal.size(widthdb, max_width, max_height).await;
let selected_size = selected.size(frame, max_width, max_height); let selected_size = selected.size(widthdb, max_width, max_height).await;
Size::new( Size::new(
normal_size.width.max(selected_size.width), normal_size.width.max(selected_size.width),
normal_size.height.max(selected_size.height), normal_size.height.max(selected_size.height),
@ -327,14 +332,18 @@ impl<Id> List<Id> {
} }
#[async_trait] #[async_trait]
impl<Id: Clone + Eq + Send> Widget for List<Id> { impl<Id: Clone + Eq + Send + Sync> Widget for List<Id> {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, _max_height: Option<u16>) -> Size { async fn size(
let width = self &self,
.rows widthdb: &mut WidthDb,
.iter() max_width: Option<u16>,
.map(|r| r.size(frame, max_width, Some(1)).width) _max_height: Option<u16>,
.max() ) -> Size {
.unwrap_or(0); 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(); let height = self.rows.len();
Size::new(width, height as u16) Size::new(width, height as u16)
} }

View file

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use toss::{Frame, Pos, Size}; use toss::{Frame, Pos, Size, WidthDb};
use super::{BoxedWidget, Widget}; use super::{BoxedWidget, Widget};
@ -66,14 +66,19 @@ impl Padding {
#[async_trait] #[async_trait]
impl Widget for Padding { impl Widget for Padding {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size { async fn size(
&self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Size {
let horizontal = self.left + self.right; let horizontal = self.left + self.right;
let vertical = self.top + self.bottom; let vertical = self.top + self.bottom;
let max_width = max_width.map(|w| w.saturating_sub(horizontal)); let max_width = max_width.map(|w| w.saturating_sub(horizontal));
let max_height = max_height.map(|h| h.saturating_sub(vertical)); 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) size + Size::new(horizontal, vertical)
} }

View file

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use toss::{Frame, Size}; use toss::{Frame, Size, WidthDb};
use super::{BoxedWidget, Widget}; use super::{BoxedWidget, Widget};
@ -45,7 +45,12 @@ impl Resize {
#[async_trait] #[async_trait]
impl Widget for Resize { impl Widget for Resize {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, max_height: Option<u16>) -> Size { async fn size(
&self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Size {
let max_width = match (max_width, self.max_width) { let max_width = match (max_width, self.max_width) {
(None, None) => None, (None, None) => None,
(Some(w), None) => Some(w), (Some(w), None) => Some(w),
@ -60,7 +65,7 @@ impl Widget for Resize {
(Some(h), Some(sh)) => Some(h.min(sh)), (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 { let width = match self.min_width {
Some(min_width) => size.width.max(min_width), Some(min_width) => size.width.max(min_width),

View file

@ -1,5 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use toss::{Frame, Pos, Size}; use toss::{Frame, Pos, Size, WidthDb};
use super::Widget; use super::Widget;
@ -7,7 +7,12 @@ pub struct HRule;
#[async_trait] #[async_trait]
impl Widget for HRule { impl Widget for HRule {
fn size(&self, _frame: &mut Frame, _max_width: Option<u16>, _max_height: Option<u16>) -> Size { async fn size(
&self,
_widthdb: &mut WidthDb,
_max_width: Option<u16>,
_max_height: Option<u16>,
) -> Size {
Size::new(0, 1) Size::new(0, 1)
} }
@ -23,7 +28,12 @@ pub struct VRule;
#[async_trait] #[async_trait]
impl Widget for VRule { impl Widget for VRule {
fn size(&self, _frame: &mut Frame, _max_width: Option<u16>, _max_height: Option<u16>) -> Size { async fn size(
&self,
_widthdb: &mut WidthDb,
_max_width: Option<u16>,
_max_height: Option<u16>,
) -> Size {
Size::new(1, 0) Size::new(1, 0)
} }

View file

@ -36,9 +36,13 @@ impl Text {
#[async_trait] #[async_trait]
impl Widget for Text { impl Widget for Text {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, _max_height: Option<u16>) -> Size { async fn size(
let lines = self.wrapped(frame.widthdb(), max_width); &self,
let widthdb = frame.widthdb(); widthdb: &mut WidthDb,
max_width: Option<u16>,
_max_height: Option<u16>,
) -> Size {
let lines = self.wrapped(widthdb, max_width);
let min_width = lines let min_width = lines
.iter() .iter()
.map(|l| widthdb.width(l.text().trim_end())) .map(|l| widthdb.width(l.text().trim_end()))