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

View file

@ -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<M: Msg, S: MsgStore<M>> {
#[async_trait]
impl<M, S> Widget for Chat<M, S>
where
M: Msg + ChatMsg,
M: Msg + ChatMsg + Send + Sync,
M::Id: Send + Sync,
S: MsgStore<M> + Send + Sync,
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 {
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> {
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`
// 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,

View file

@ -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<M: Msg, S: MsgStore<M>> {
#[async_trait]
impl<M, S> Widget for TreeView<M, S>
where
M: Msg + ChatMsg,
M: Msg + ChatMsg + Send + Sync,
M::Id: Send + Sync,
S: MsgStore<M> + Send + Sync,
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
}

View file

@ -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<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> {
Ok(match cursor {
Cursor::Msg(id) => self.store.path(id).await?,
@ -69,7 +75,7 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
.is_some()
}
fn editor_block(
async fn editor_block(
&self,
context: &Context,
frame: &mut Frame,
@ -78,20 +84,23 @@ impl<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
let (widget, cursor_row) =
widgets::editor::<M>(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<BlockId<M::Id>> {
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,
context: &Context,
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
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<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
} 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<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
) -> TreeBlocks<M::Id> {
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<M::Id> {
async fn layout_bottom(&self, context: &Context, frame: &mut Frame) -> TreeBlocks<M::Id> {
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<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
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<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
};
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<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
) -> Result<TreeBlocks<M::Id>, 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<M: Msg + ChatMsg, S: MsgStore<M>> InnerTreeViewState<M, S> {
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<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(context, frame, tree);
let mut blocks = self.layout_tree(context, frame, tree).await;
blocks
.blocks_mut()
@ -330,7 +337,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(context, frame);
let mut blocks = self.layout_bottom(context, frame).await;
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 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 };

View file

@ -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<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)
}

View file

@ -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<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);
}
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 {
Box::new(widget)
}

View file

@ -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<u16>, max_height: Option<u16>) -> Size {
self.inner.size(frame, max_width, max_height)
async fn size(
&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) {

View file

@ -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<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_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)
}

View file

@ -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<u16>, max_height: Option<u16>) -> Size {
self.inner.size(frame, max_width, max_height)
async fn size(
&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) {

View file

@ -493,9 +493,14 @@ impl Editor {
#[async_trait]
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 {
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);

View file

@ -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<u16>, _max_height: Option<u16>) -> Size {
async fn size(
&self,
_widthdb: &mut WidthDb,
_max_width: Option<u16>,
_max_height: Option<u16>,
) -> Size {
self.size
}

View file

@ -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<u16>, max_height: Option<u16>) -> Size {
self.inner.size(frame, max_width, max_height)
async fn size(
&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) {
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);

View file

@ -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<u16>,
max_height: Option<u16>,
) -> Vec<SizedSegment> {
@ -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<u16>,
max_height: Option<u16>,
) -> Vec<SizedSegment> {
@ -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<u16>, max_height: Option<u16>) -> Size {
let sizes = sizes_horiz(&self.segments, frame, max_width, max_height);
async fn size(
&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 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<Self>, 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<u16>, max_height: Option<u16>) -> Size {
let sizes = sizes_vert(&self.segments, frame, max_width, max_height);
async fn size(
&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 height = sizes.iter().map(|s| s.size.height).sum::<u16>();
Size::new(width, height)
@ -225,7 +243,13 @@ impl Widget for VJoin {
async fn render(self: Box<Self>, 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);

View file

@ -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<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;
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);
}

View file

@ -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<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 {
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<Id> List<Id> {
}
#[async_trait]
impl<Id: Clone + Eq + Send> Widget for List<Id> {
fn size(&self, frame: &mut Frame, max_width: Option<u16>, _max_height: Option<u16>) -> Size {
let width = self
.rows
.iter()
.map(|r| r.size(frame, max_width, Some(1)).width)
.max()
.unwrap_or(0);
impl<Id: Clone + Eq + Send + Sync> Widget for List<Id> {
async fn size(
&self,
widthdb: &mut WidthDb,
max_width: Option<u16>,
_max_height: Option<u16>,
) -> 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)
}

View file

@ -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<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 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)
}

View file

@ -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<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) {
(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),

View file

@ -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<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)
}
@ -23,7 +28,12 @@ pub struct VRule;
#[async_trait]
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)
}

View file

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