diff --git a/src/ui/chat/tree.rs b/src/ui/chat/tree.rs index d8ad638..aed3381 100644 --- a/src/ui/chat/tree.rs +++ b/src/ui/chat/tree.rs @@ -1,6 +1,5 @@ mod cursor; mod layout; -mod time; mod tree_blocks; mod widgets; diff --git a/src/ui/chat/tree/widgets.rs b/src/ui/chat/tree/widgets.rs index cd54a0e..be999c5 100644 --- a/src/ui/chat/tree/widgets.rs +++ b/src/ui/chat/tree/widgets.rs @@ -1,15 +1,25 @@ +mod indent; +mod time; + use crate::store::Msg; use crate::ui::widgets::join::{HJoin, Segment}; use crate::ui::widgets::padding::Padding; use crate::ui::widgets::text::Text; use crate::ui::widgets::BoxedWidget; -use super::time; +use self::indent::Indent; pub fn msg(highlighted: bool, indent: usize, msg: &M) -> BoxedWidget { HJoin::new(vec![ - Segment::new(Padding::new(time::widget(Some(msg.time()), highlighted)).right(1)), + Segment::new( + Padding::new(time::widget(Some(msg.time()), highlighted)) + .stretch(true) + .right(1), + ), + Segment::new(Indent::new(indent, highlighted)), Segment::new(Padding::new(Text::new(msg.nick())).right(1)), + // TODO Minimum content width + // TODO Minimizing and maximizing messages Segment::new(Text::new(msg.content()).wrap(true)), ]) .into() @@ -17,7 +27,12 @@ pub fn msg(highlighted: bool, indent: usize, msg: &M) -> BoxedWidget { pub fn msg_placeholder(highlighted: bool, indent: usize) -> BoxedWidget { HJoin::new(vec![ - Segment::new(Padding::new(time::widget(None, highlighted)).right(1)), + Segment::new( + Padding::new(time::widget(None, highlighted)) + .stretch(true) + .right(1), + ), + Segment::new(Indent::new(indent, highlighted)), Segment::new(Text::new("[...]")), ]) .into() diff --git a/src/ui/chat/tree/widgets/indent.rs b/src/ui/chat/tree/widgets/indent.rs new file mode 100644 index 0000000..97bfb8a --- /dev/null +++ b/src/ui/chat/tree/widgets/indent.rs @@ -0,0 +1,48 @@ +use async_trait::async_trait; +use crossterm::style::{ContentStyle, Stylize}; +use toss::frame::{Frame, Pos, Size}; + +use crate::ui::widgets::Widget; + +pub const INDENT: &str = "│ "; +pub const INDENT_WIDTH: usize = 2; + +pub fn style() -> ContentStyle { + ContentStyle::default().dark_grey() +} + +pub fn style_inverted() -> ContentStyle { + ContentStyle::default().black().on_white() +} + +pub struct Indent { + level: usize, + highlighted: bool, +} + +impl Indent { + pub fn new(level: usize, highlighted: bool) -> Self { + Self { level, highlighted } + } +} + +#[async_trait] +impl Widget for Indent { + fn size(&self, frame: &mut Frame, max_width: Option, max_height: Option) -> Size { + Size::new((INDENT_WIDTH * self.level) as u16, 0) + } + + async fn render(self: Box, frame: &mut Frame) { + let size = frame.size(); + + let style = if self.highlighted { + style_inverted() + } else { + style() + }; + + for y in 0..size.height { + frame.write(Pos::new(0, y.into()), (INDENT.repeat(self.level), style)) + } + } +} diff --git a/src/ui/chat/tree/time.rs b/src/ui/chat/tree/widgets/time.rs similarity index 100% rename from src/ui/chat/tree/time.rs rename to src/ui/chat/tree/widgets/time.rs diff --git a/src/ui/widgets/padding.rs b/src/ui/widgets/padding.rs index ba6a92a..74a7e29 100644 --- a/src/ui/widgets/padding.rs +++ b/src/ui/widgets/padding.rs @@ -5,6 +5,7 @@ use super::{BoxedWidget, Widget}; pub struct Padding { inner: BoxedWidget, + stretch: bool, left: u16, right: u16, top: u16, @@ -15,6 +16,7 @@ impl Padding { pub fn new>(inner: W) -> Self { Self { inner: inner.into(), + stretch: false, left: 0, right: 0, top: 0, @@ -22,6 +24,13 @@ impl Padding { } } + /// Whether the inner widget should be stretched to fill the additional + /// space. + pub fn stretch(mut self, active: bool) -> Self { + self.stretch = active; + self + } + pub fn left(mut self, amount: u16) -> Self { self.left = amount; self @@ -73,10 +82,14 @@ impl Widget for Padding { let size = frame.size(); let inner_pos = Pos::new(self.left.into(), self.top.into()); - let inner_size = Size::new( - size.width.saturating_sub(self.left + self.right), - size.height.saturating_sub(self.top + self.bottom), - ); + let inner_size = if self.stretch { + size + } else { + Size::new( + size.width.saturating_sub(self.left + self.right), + size.height.saturating_sub(self.top + self.bottom), + ) + }; frame.push(inner_pos, inner_size); self.inner.render(frame).await;