Add Float widget

This commit is contained in:
Joscha 2023-02-16 15:12:27 +01:00
parent bcc07dc9ba
commit 575faf9bbf
4 changed files with 137 additions and 12 deletions

View file

@ -6,17 +6,20 @@ use toss::widgets::{BorderLook, Text};
use toss::{Styled, Terminal, Widget, WidgetExt}; use toss::{Styled, Terminal, Widget, WidgetExt};
fn widget() -> impl Widget<Infallible> { fn widget() -> impl Widget<Infallible> {
Text::new( let styled = Styled::new("Hello world!", ContentStyle::default().green())
Styled::new("Hello world!", ContentStyle::default().green()) .then_plain("\n")
.then_plain("\n") .then(
.then( "Press any key to exit",
"Press any key to exit", ContentStyle::default().on_dark_blue(),
ContentStyle::default().on_dark_blue(), );
), Text::new(styled)
) .padding()
.border() .horizontal(1)
.look(BorderLook::LINE_DOUBLE) .border()
.style(ContentStyle::default().dark_red()) .look(BorderLook::LINE_DOUBLE)
.style(ContentStyle::default().dark_red())
.float()
.all(0.5)
} }
fn render_frame(term: &mut Terminal) { fn render_frame(term: &mut Terminal) {

View file

@ -1,6 +1,6 @@
use async_trait::async_trait; use async_trait::async_trait;
use crate::widgets::{Border, Padding}; use crate::widgets::{Border, Float, Padding};
use crate::{Frame, Size}; use crate::{Frame, Size};
// TODO Feature-gate these traits // TODO Feature-gate these traits
@ -33,6 +33,10 @@ pub trait WidgetExt: Sized {
Border::new(self) Border::new(self)
} }
fn float(self) -> Float<Self> {
Float::new(self)
}
fn padding(self) -> Padding<Self> { fn padding(self) -> Padding<Self> {
Padding::new(self) Padding::new(self)
} }
@ -45,6 +49,10 @@ pub trait AsyncWidgetExt: Sized {
Border::new(self) Border::new(self)
} }
fn float(self) -> Float<Self> {
Float::new(self)
}
fn padding(self) -> Padding<Self> { fn padding(self) -> Padding<Self> {
Padding::new(self) Padding::new(self)
} }

View file

@ -1,7 +1,9 @@
mod border; mod border;
mod float;
mod padding; mod padding;
mod text; mod text;
pub use border::*; pub use border::*;
pub use float::*;
pub use padding::*; pub use padding::*;
pub use text::*; pub use text::*;

112
src/widgets/float.rs Normal file
View file

@ -0,0 +1,112 @@
use async_trait::async_trait;
use crate::{AsyncWidget, Frame, Pos, Size, Widget};
pub struct Float<I> {
inner: I,
horizontal: Option<f32>,
vertical: Option<f32>,
}
impl<I> Float<I> {
pub fn new(inner: I) -> Self {
Self {
inner,
horizontal: None,
vertical: None,
}
}
pub fn horizontal(mut self, position: f32) -> Self {
self.horizontal = Some(position);
self
}
pub fn vertical(mut self, position: f32) -> Self {
self.vertical = Some(position);
self
}
pub fn all(self, position: f32) -> Self {
self.horizontal(position).vertical(position)
}
fn push_inner(&self, frame: &mut Frame, size: Size, mut inner_size: Size) {
inner_size.width = inner_size.width.min(size.width);
inner_size.height = inner_size.height.min(size.height);
let mut inner_pos = Pos::ZERO;
if let Some(horizontal) = self.horizontal {
let available = (size.width - inner_size.width) as f32;
// Biased towards the left if horizontal lands exactly on the
// boundary between two cells
inner_pos.x = (horizontal * available).floor().min(available) as i32;
}
if let Some(vertical) = self.vertical {
let available = (size.height - inner_size.height) as f32;
// Biased towards the top if vertical lands exactly on the boundary
// between two cells
inner_pos.y = (vertical * available).floor().min(available) as i32;
}
frame.push(inner_pos, inner_size);
}
}
impl<E, I> Widget<E> for Float<I>
where
I: Widget<E>,
{
fn size(
&self,
frame: &mut Frame,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Result<Size, E> {
self.inner.size(frame, max_width, max_height)
}
fn draw(self, frame: &mut Frame) -> Result<(), E> {
let size = frame.size();
let inner_size = self
.inner
.size(frame, Some(size.width), Some(size.height))?;
self.push_inner(frame, size, inner_size);
self.inner.draw(frame)?;
frame.pop();
Ok(())
}
}
#[async_trait]
impl<E, I> AsyncWidget<E> for Float<I>
where
I: AsyncWidget<E> + Send + Sync,
{
async fn size(
&self,
frame: &mut Frame,
max_width: Option<u16>,
max_height: Option<u16>,
) -> Result<Size, E> {
self.inner.size(frame, max_width, max_height).await
}
async fn draw(self, frame: &mut Frame) -> Result<(), E> {
let size = frame.size();
let inner_size = self
.inner
.size(frame, Some(size.width), Some(size.height))
.await?;
self.push_inner(frame, size, inner_size);
self.inner.draw(frame).await?;
frame.pop();
Ok(())
}
}