Merge pull request #4 from selimaj-dev/patches-and-centering

Patches and centering
This commit is contained in:
2026-07-15 08:58:38 +02:00
committed by GitHub
6 changed files with 70 additions and 20 deletions
+7 -2
View File
@@ -7,6 +7,7 @@ pub enum LayoutItem {
Frame { padding: u16, item: Box<LayoutItem> }, Frame { padding: u16, item: Box<LayoutItem> },
Widget(Size), Widget(Size),
Spacing(Size),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -28,7 +29,7 @@ impl LayoutItem {
let remaining = allocation let remaining = allocation
- items - items
.iter() .iter()
.map(|item| item.get_size().get_fixed(alloc.height)) .map(|item| item.get_size().get_fixed(allocation))
.sum::<u16>(); .sum::<u16>();
let total_weight = items let total_weight = items
@@ -82,6 +83,10 @@ impl LayoutItem {
Self::Widget(_) => { Self::Widget(_) => {
full_alloc.widgets.push(*alloc); full_alloc.widgets.push(*alloc);
} }
Self::Spacing(_) => {
full_alloc.frame.push(*alloc);
}
} }
} }
@@ -89,7 +94,7 @@ impl LayoutItem {
match self { match self {
Self::Rows { unit, .. } => unit, Self::Rows { unit, .. } => unit,
Self::Columns { unit, .. } => unit, Self::Columns { unit, .. } => unit,
Self::Widget(unit) => unit, Self::Widget(unit) | Self::Spacing(unit) => unit,
Self::Frame { item, .. } => item.get_size(), Self::Frame { item, .. } => item.get_size(),
} }
} }
+7 -5
View File
@@ -1,4 +1,4 @@
use std::any::Any; use std::{any::Any, time::Duration};
use tokio::sync::mpsc; use tokio::sync::mpsc;
@@ -38,10 +38,12 @@ pub async fn run<A: App>(create_app: impl FnOnce(&state::Context) -> A) {
crossterm::terminal::enable_raw_mode().unwrap(); crossterm::terminal::enable_raw_mode().unwrap();
tokio::spawn(async move { tokio::spawn(async move {
loop { while !tx.is_closed() {
tx.send(Box::new(crossterm::event::read().unwrap())) if crossterm::event::poll(Duration::from_millis(100)).unwrap() {
.await tx.send(Box::new(crossterm::event::read().unwrap()))
.unwrap(); .await
.unwrap();
}
} }
}); });
+20
View File
@@ -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);
}
}
+1
View File
@@ -1,3 +1,4 @@
pub mod center;
pub mod outline; pub mod outline;
pub mod spaced; pub mod spaced;
+25 -6
View File
@@ -4,16 +4,26 @@ pub struct SpacedRows(pub Vec<(LayoutItem, Box<dyn Widget>)>);
impl Widget for SpacedRows { impl Widget for SpacedRows {
fn render(&self, scope: &mut crate::render::RenderScope) { 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), unit: Percent(100),
items: self.0.iter().map(|v| v.0.clone()).collect(), items,
}); });
for i in 0..alloc.widgets.len() { for i in 0..alloc.widgets.len() {
let item = &self.0[i]; let item = &self.0[i];
if i > 0 { 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)); scope.draw_text((0, rect.y - 2), "─".repeat(rect.width as usize));
} }
@@ -27,9 +37,19 @@ pub struct SpacedColumns(pub Vec<(LayoutItem, Box<dyn Widget>)>);
impl Widget for SpacedColumns { impl Widget for SpacedColumns {
fn render(&self, scope: &mut crate::render::RenderScope) { 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 { let mut alloc = scope.rect.allocate(&crate::layout::LayoutItem::Columns {
unit: Percent(100), unit: Percent(100),
items: self.0.iter().map(|v| v.0.clone()).collect(), items,
}); });
for i in 0..alloc.widgets.len() { for i in 0..alloc.widgets.len() {
@@ -39,9 +59,8 @@ impl Widget for SpacedColumns {
let rect = &mut alloc.widgets[i]; let rect = &mut alloc.widgets[i];
for i in 0..rect.height { 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); alloc.draw(i, &*item.1);
+10 -7
View File
@@ -1,11 +1,7 @@
use std::any::Any; use std::any::Any;
use pulse_ui::{ use pulse_ui::{
App, App, layout::{LayoutItem, layout}, state::{Refresh, State}, unit::Size, widget::{outline::Outline, spaced::{SpacedColumns, SpacedRows}},
layout::{LayoutItem, layout},
state::{Refresh, State},
unit::Size,
widget::{outline::Outline, spaced::SpacedRows},
}; };
pub struct PulseTradeApp { pub struct PulseTradeApp {
@@ -21,6 +17,10 @@ impl App for PulseTradeApp {
} }
*self.count.lock().await += 1; *self.count.lock().await += 1;
if *self.count.lock().await > 10 {
ctx.close().await;
}
} }
async fn layout(&self) -> pulse_ui::layout::LayoutItem { async fn layout(&self) -> pulse_ui::layout::LayoutItem {
@@ -35,11 +35,14 @@ impl App for PulseTradeApp {
layout.draw( layout.draw(
0, 0,
SpacedRows(vec![ SpacedColumns(vec![
(LayoutItem::Widget(Size::Flex(1)), Box::new("one")), (LayoutItem::Widget(Size::Flex(1)), Box::new("one")),
(LayoutItem::Widget(Size::Flex(1)), Box::new("two")), (LayoutItem::Widget(Size::Flex(1)), Box::new("two")),
(LayoutItem::Widget(Size::Flex(1)), Box::new("three")), (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),
),
]), ]),
); );
} }