166 lines
4.2 KiB
Rust
166 lines
4.2 KiB
Rust
use async_trait::async_trait;
|
|
|
|
use crate::{AsyncWidget, Frame, Pos, Size, Widget, WidthDb};
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Float<I> {
|
|
pub 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(&self) -> Option<f32> {
|
|
self.horizontal
|
|
}
|
|
|
|
pub fn set_horizontal(&mut self, position: Option<f32>) {
|
|
if let Some(position) = position {
|
|
assert!((0.0..=1.0).contains(&position));
|
|
}
|
|
self.horizontal = position;
|
|
}
|
|
|
|
pub fn vertical(&self) -> Option<f32> {
|
|
self.vertical
|
|
}
|
|
|
|
pub fn set_vertical(&mut self, position: Option<f32>) {
|
|
if let Some(position) = position {
|
|
assert!((0.0..=1.0).contains(&position));
|
|
}
|
|
self.vertical = position;
|
|
}
|
|
|
|
pub fn with_horizontal(mut self, position: f32) -> Self {
|
|
self.set_horizontal(Some(position));
|
|
self
|
|
}
|
|
|
|
pub fn with_vertical(mut self, position: f32) -> Self {
|
|
self.set_vertical(Some(position));
|
|
self
|
|
}
|
|
|
|
pub fn with_all(self, position: f32) -> Self {
|
|
self.with_horizontal(position).with_vertical(position)
|
|
}
|
|
|
|
pub fn with_left(self) -> Self {
|
|
self.with_horizontal(0.0)
|
|
}
|
|
|
|
pub fn with_right(self) -> Self {
|
|
self.with_horizontal(1.0)
|
|
}
|
|
|
|
pub fn with_top(self) -> Self {
|
|
self.with_vertical(0.0)
|
|
}
|
|
|
|
pub fn with_bottom(self) -> Self {
|
|
self.with_vertical(1.0)
|
|
}
|
|
|
|
pub fn with_center_h(self) -> Self {
|
|
self.with_horizontal(0.5)
|
|
}
|
|
|
|
pub fn with_center_v(self) -> Self {
|
|
self.with_vertical(0.5)
|
|
}
|
|
|
|
pub fn with_center(self) -> Self {
|
|
self.with_all(0.5)
|
|
}
|
|
|
|
fn push_inner(&self, frame: &mut Frame, size: Size, mut inner_size: Size) {
|
|
let mut inner_pos = Pos::ZERO;
|
|
|
|
if let Some(horizontal) = self.horizontal {
|
|
let available = size.width.saturating_sub(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;
|
|
inner_size.width = inner_size.width.min(size.width);
|
|
} else {
|
|
inner_size.width = size.width;
|
|
}
|
|
|
|
if let Some(vertical) = self.vertical {
|
|
let available = size.height.saturating_sub(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;
|
|
inner_size.height = inner_size.height.min(size.height);
|
|
} else {
|
|
inner_size.height = size.height;
|
|
}
|
|
|
|
frame.push(inner_pos, inner_size);
|
|
}
|
|
}
|
|
|
|
impl<E, I> Widget<E> for Float<I>
|
|
where
|
|
I: Widget<E>,
|
|
{
|
|
fn size(
|
|
&self,
|
|
widthdb: &mut WidthDb,
|
|
max_width: Option<u16>,
|
|
max_height: Option<u16>,
|
|
) -> Result<Size, E> {
|
|
self.inner.size(widthdb, max_width, max_height)
|
|
}
|
|
|
|
fn draw(self, frame: &mut Frame) -> Result<(), E> {
|
|
let size = frame.size();
|
|
let inner_size = self
|
|
.inner
|
|
.size(frame.widthdb(), 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,
|
|
widthdb: &mut WidthDb,
|
|
max_width: Option<u16>,
|
|
max_height: Option<u16>,
|
|
) -> Result<Size, E> {
|
|
self.inner.size(widthdb, 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.widthdb(), Some(size.width), Some(size.height))
|
|
.await?;
|
|
|
|
self.push_inner(frame, size, inner_size);
|
|
self.inner.draw(frame).await?;
|
|
frame.pop();
|
|
|
|
Ok(())
|
|
}
|
|
}
|