diff --git a/pulse-ui/src/layout.rs b/pulse-ui/src/layout.rs index 4e94d14..2d992cb 100644 --- a/pulse-ui/src/layout.rs +++ b/pulse-ui/src/layout.rs @@ -7,6 +7,7 @@ pub enum LayoutItem { Frame { padding: u16, item: Box }, Widget(Size), + Spacing(Size), } #[derive(Debug, Clone)] @@ -28,7 +29,7 @@ impl LayoutItem { let remaining = allocation - items .iter() - .map(|item| item.get_size().get_fixed(alloc.height)) + .map(|item| item.get_size().get_fixed(allocation)) .sum::(); let total_weight = items @@ -82,6 +83,10 @@ impl LayoutItem { Self::Widget(_) => { full_alloc.widgets.push(*alloc); } + + Self::Spacing(_) => { + full_alloc.frame.push(*alloc); + } } } @@ -89,7 +94,7 @@ impl LayoutItem { match self { Self::Rows { unit, .. } => unit, Self::Columns { unit, .. } => unit, - Self::Widget(unit) => unit, + Self::Widget(unit) | Self::Spacing(unit) => unit, Self::Frame { item, .. } => item.get_size(), } } diff --git a/pulse-ui/src/lib.rs b/pulse-ui/src/lib.rs index 5336b83..fa05603 100644 --- a/pulse-ui/src/lib.rs +++ b/pulse-ui/src/lib.rs @@ -1,4 +1,4 @@ -use std::any::Any; +use std::{any::Any, time::Duration}; use tokio::sync::mpsc; @@ -38,10 +38,12 @@ pub async fn run(create_app: impl FnOnce(&state::Context) -> A) { crossterm::terminal::enable_raw_mode().unwrap(); tokio::spawn(async move { - loop { - tx.send(Box::new(crossterm::event::read().unwrap())) - .await - .unwrap(); + while !tx.is_closed() { + if crossterm::event::poll(Duration::from_millis(100)).unwrap() { + tx.send(Box::new(crossterm::event::read().unwrap())) + .await + .unwrap(); + } } }); diff --git a/pulse-ui/src/widget/center.rs b/pulse-ui/src/widget/center.rs new file mode 100644 index 0000000..33ac06e --- /dev/null +++ b/pulse-ui/src/widget/center.rs @@ -0,0 +1,20 @@ +use crate::widget::Widget; + +pub struct Center(pub String); + +impl Widget for Center { + fn render(&self, scope: &mut crate::render::RenderScope) { + let mut width = 0; + let mut height = 0; + + for line in self.0.lines() { + height += 1; + width = width.max(line.len() as u16); + } + + let x = (scope.rect.width - width) / 2; + let y = (scope.rect.height - height) / 2; + + scope.draw_text((x, y), &self.0); + } +} diff --git a/pulse-ui/src/widget/mod.rs b/pulse-ui/src/widget/mod.rs index 819165e..d75c0e6 100644 --- a/pulse-ui/src/widget/mod.rs +++ b/pulse-ui/src/widget/mod.rs @@ -1,3 +1,4 @@ +pub mod center; pub mod outline; pub mod spaced; diff --git a/pulse-ui/src/widget/spaced.rs b/pulse-ui/src/widget/spaced.rs index 23cec09..8e28ce3 100644 --- a/pulse-ui/src/widget/spaced.rs +++ b/pulse-ui/src/widget/spaced.rs @@ -4,16 +4,26 @@ pub struct SpacedRows(pub Vec<(LayoutItem, Box)>); impl Widget for SpacedRows { fn render(&self, scope: &mut crate::render::RenderScope) { - let alloc = scope.rect.allocate(&crate::layout::LayoutItem::Rows { + let mut items = Vec::new(); + + for (i, (item, _)) in self.0.iter().enumerate() { + if i > 0 { + items.push(LayoutItem::Spacing(crate::unit::Size::Fixed(1))) + } + + items.push(item.clone()); + } + + let mut alloc = scope.rect.allocate(&crate::layout::LayoutItem::Rows { unit: Percent(100), - items: self.0.iter().map(|v| v.0.clone()).collect(), + items, }); for i in 0..alloc.widgets.len() { let item = &self.0[i]; if i > 0 { - let rect = &alloc.widgets[i]; + let rect = &mut alloc.widgets[i]; scope.draw_text((0, rect.y - 2), "─".repeat(rect.width as usize)); } @@ -27,9 +37,19 @@ pub struct SpacedColumns(pub Vec<(LayoutItem, Box)>); impl Widget for SpacedColumns { fn render(&self, scope: &mut crate::render::RenderScope) { + let mut items = Vec::new(); + + for (i, (item, _)) in self.0.iter().enumerate() { + if i > 0 { + items.push(LayoutItem::Spacing(crate::unit::Size::Fixed(1))) + } + + items.push(item.clone()); + } + let mut alloc = scope.rect.allocate(&crate::layout::LayoutItem::Columns { unit: Percent(100), - items: self.0.iter().map(|v| v.0.clone()).collect(), + items, }); for i in 0..alloc.widgets.len() { @@ -39,9 +59,8 @@ impl Widget for SpacedColumns { let rect = &mut alloc.widgets[i]; for i in 0..rect.height { - scope.draw_text((rect.x - 1, i), "│"); + scope.draw_text((rect.x - 2, i), "│"); } - rect.x += 1; } alloc.draw(i, &*item.1); diff --git a/src/main.rs b/src/main.rs index 2af3330..414d870 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,7 @@ use std::any::Any; use pulse_ui::{ - App, - layout::{LayoutItem, layout}, - state::{Refresh, State}, - unit::Size, - widget::{outline::Outline, spaced::SpacedRows}, + App, layout::{LayoutItem, layout}, state::{Refresh, State}, unit::Size, widget::{outline::Outline, spaced::{SpacedColumns, SpacedRows}}, }; pub struct PulseTradeApp { @@ -21,6 +17,10 @@ impl App for PulseTradeApp { } *self.count.lock().await += 1; + + if *self.count.lock().await > 10 { + ctx.close().await; + } } async fn layout(&self) -> pulse_ui::layout::LayoutItem { @@ -35,11 +35,14 @@ impl App for PulseTradeApp { layout.draw( 0, - SpacedRows(vec![ + SpacedColumns(vec![ (LayoutItem::Widget(Size::Flex(1)), Box::new("one")), (LayoutItem::Widget(Size::Flex(1)), Box::new("two")), (LayoutItem::Widget(Size::Flex(1)), Box::new("three")), - (LayoutItem::Widget(Size::Flex(1)), Box::new(self.count.display().await)), + ( + LayoutItem::Widget(Size::Flex(1)), + Box::new(self.count.display().await), + ), ]), ); }