Add JoinH and JoinV
This commit is contained in:
parent
828bba464a
commit
f581fa6c47
2 changed files with 324 additions and 1 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use crate::widgets::{Background, Border, Either, Either3, Float, Layer, Padding};
|
use crate::widgets::{Background, Border, Either, Either3, Float, JoinSegment, Layer, Padding};
|
||||||
use crate::{Frame, Size};
|
use crate::{Frame, Size};
|
||||||
|
|
||||||
// TODO Feature-gate these traits
|
// TODO Feature-gate these traits
|
||||||
|
|
@ -61,6 +61,10 @@ pub trait WidgetExt: Sized {
|
||||||
Float::new(self)
|
Float::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn segment(self) -> JoinSegment<Self> {
|
||||||
|
JoinSegment::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
fn below<W>(self, above: W) -> Layer<Self, W> {
|
fn below<W>(self, above: W) -> Layer<Self, W> {
|
||||||
Layer::new(self, above)
|
Layer::new(self, above)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use crate::{AsyncWidget, Frame, Pos, Size, Widget};
|
||||||
|
|
||||||
// The following algorithm has three goals, listed in order of importance:
|
// The following algorithm has three goals, listed in order of importance:
|
||||||
//
|
//
|
||||||
// 1. Use the available space
|
// 1. Use the available space
|
||||||
|
|
@ -50,6 +54,22 @@ struct Segment {
|
||||||
weight: f32,
|
weight: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Segment {
|
||||||
|
fn horizontal<I>(size: Size, segment: &JoinSegment<I>) -> Self {
|
||||||
|
Self {
|
||||||
|
size: size.width,
|
||||||
|
weight: segment.weight,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vertical<I>(size: Size, segment: &JoinSegment<I>) -> Self {
|
||||||
|
Self {
|
||||||
|
size: size.height,
|
||||||
|
weight: segment.weight,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn balance(segments: &mut [Segment], available: u16) {
|
fn balance(segments: &mut [Segment], available: u16) {
|
||||||
if segments.is_empty() {
|
if segments.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -189,3 +209,302 @@ fn shrink(segments: &mut [Segment], mut available: u16) {
|
||||||
segment.size += 1;
|
segment.size += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct JoinSegment<I> {
|
||||||
|
inner: I,
|
||||||
|
weight: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> JoinSegment<I> {
|
||||||
|
pub fn new(inner: I) -> Self {
|
||||||
|
Self { inner, weight: 1.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn weight(mut self, weight: f32) -> Self {
|
||||||
|
assert!(weight >= 0.0);
|
||||||
|
self.weight = weight;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct JoinH<I> {
|
||||||
|
segments: Vec<JoinSegment<I>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> JoinH<I> {
|
||||||
|
pub fn new(segments: Vec<JoinSegment<I>>) -> Self {
|
||||||
|
Self { segments }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, I> Widget<E> for JoinH<I>
|
||||||
|
where
|
||||||
|
I: Widget<E>,
|
||||||
|
{
|
||||||
|
fn size(
|
||||||
|
&self,
|
||||||
|
frame: &mut Frame,
|
||||||
|
max_width: Option<u16>,
|
||||||
|
max_height: Option<u16>,
|
||||||
|
) -> Result<Size, E> {
|
||||||
|
if let Some(max_width) = max_width {
|
||||||
|
let mut balanced_segments = vec![];
|
||||||
|
for segment in &self.segments {
|
||||||
|
let size = segment.inner.size(frame, Some(max_width), max_height)?;
|
||||||
|
balanced_segments.push(Segment::horizontal(size, segment));
|
||||||
|
}
|
||||||
|
balance(&mut balanced_segments, max_width);
|
||||||
|
|
||||||
|
let mut width = 0;
|
||||||
|
let mut height = 0;
|
||||||
|
for (segment, balanced) in self.segments.iter().zip(balanced_segments.into_iter()) {
|
||||||
|
let size = segment.inner.size(frame, Some(balanced.size), max_height)?;
|
||||||
|
width += size.width;
|
||||||
|
height = height.max(size.height);
|
||||||
|
}
|
||||||
|
Ok(Size::new(width, height))
|
||||||
|
} else {
|
||||||
|
let mut width = 0;
|
||||||
|
let mut height = 0;
|
||||||
|
for segment in &self.segments {
|
||||||
|
let size = segment.inner.size(frame, max_width, max_height)?;
|
||||||
|
width += size.width;
|
||||||
|
height = height.max(size.height);
|
||||||
|
}
|
||||||
|
Ok(Size::new(width, height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self, frame: &mut Frame) -> Result<(), E> {
|
||||||
|
let size = frame.size();
|
||||||
|
let max_width = Some(size.width);
|
||||||
|
let max_height = Some(size.height);
|
||||||
|
|
||||||
|
let mut balanced_segments = vec![];
|
||||||
|
for segment in &self.segments {
|
||||||
|
let size = segment.inner.size(frame, max_width, max_height)?;
|
||||||
|
balanced_segments.push(Segment::horizontal(size, segment));
|
||||||
|
}
|
||||||
|
balance(&mut balanced_segments, size.width);
|
||||||
|
|
||||||
|
let mut x = 0;
|
||||||
|
for (segment, balanced) in self.segments.into_iter().zip(balanced_segments.into_iter()) {
|
||||||
|
frame.push(Pos::new(x, 0), Size::new(balanced.size, size.height));
|
||||||
|
segment.inner.draw(frame)?;
|
||||||
|
frame.pop();
|
||||||
|
x += balanced.size as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<E, I> AsyncWidget<E> for JoinH<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> {
|
||||||
|
if let Some(max_width) = max_width {
|
||||||
|
let mut balanced_segments = vec![];
|
||||||
|
for segment in &self.segments {
|
||||||
|
let size = segment
|
||||||
|
.inner
|
||||||
|
.size(frame, Some(max_width), max_height)
|
||||||
|
.await?;
|
||||||
|
balanced_segments.push(Segment::horizontal(size, segment));
|
||||||
|
}
|
||||||
|
balance(&mut balanced_segments, max_width);
|
||||||
|
|
||||||
|
let mut width = 0;
|
||||||
|
let mut height = 0;
|
||||||
|
for (segment, balanced) in self.segments.iter().zip(balanced_segments.into_iter()) {
|
||||||
|
let size = segment
|
||||||
|
.inner
|
||||||
|
.size(frame, Some(balanced.size), max_height)
|
||||||
|
.await?;
|
||||||
|
width += size.width;
|
||||||
|
height = height.max(size.height);
|
||||||
|
}
|
||||||
|
Ok(Size::new(width, height))
|
||||||
|
} else {
|
||||||
|
let mut width = 0;
|
||||||
|
let mut height = 0;
|
||||||
|
for segment in &self.segments {
|
||||||
|
let size = segment.inner.size(frame, max_width, max_height).await?;
|
||||||
|
width += size.width;
|
||||||
|
height = height.max(size.height);
|
||||||
|
}
|
||||||
|
Ok(Size::new(width, height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn draw(self, frame: &mut Frame) -> Result<(), E> {
|
||||||
|
let size = frame.size();
|
||||||
|
let max_width = Some(size.width);
|
||||||
|
let max_height = Some(size.height);
|
||||||
|
|
||||||
|
let mut balanced_segments = vec![];
|
||||||
|
for segment in &self.segments {
|
||||||
|
let size = segment.inner.size(frame, max_width, max_height).await?;
|
||||||
|
balanced_segments.push(Segment::horizontal(size, segment));
|
||||||
|
}
|
||||||
|
balance(&mut balanced_segments, size.width);
|
||||||
|
|
||||||
|
let mut x = 0;
|
||||||
|
for (segment, balanced) in self.segments.into_iter().zip(balanced_segments.into_iter()) {
|
||||||
|
frame.push(Pos::new(x, 0), Size::new(balanced.size, size.height));
|
||||||
|
segment.inner.draw(frame).await?;
|
||||||
|
frame.pop();
|
||||||
|
x += balanced.size as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct JoinV<I> {
|
||||||
|
segments: Vec<JoinSegment<I>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> JoinV<I> {
|
||||||
|
pub fn new(segments: Vec<JoinSegment<I>>) -> Self {
|
||||||
|
Self { segments }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, I> Widget<E> for JoinV<I>
|
||||||
|
where
|
||||||
|
I: Widget<E>,
|
||||||
|
{
|
||||||
|
fn size(
|
||||||
|
&self,
|
||||||
|
frame: &mut Frame,
|
||||||
|
max_width: Option<u16>,
|
||||||
|
max_height: Option<u16>,
|
||||||
|
) -> Result<Size, E> {
|
||||||
|
if let Some(max_height) = max_height {
|
||||||
|
let mut balanced_segments = vec![];
|
||||||
|
for segment in &self.segments {
|
||||||
|
let size = segment.inner.size(frame, max_width, Some(max_height))?;
|
||||||
|
balanced_segments.push(Segment::vertical(size, segment));
|
||||||
|
}
|
||||||
|
balance(&mut balanced_segments, max_height);
|
||||||
|
|
||||||
|
let mut width = 0;
|
||||||
|
let mut height = 0;
|
||||||
|
for (segment, balanced) in self.segments.iter().zip(balanced_segments.into_iter()) {
|
||||||
|
let size = segment.inner.size(frame, max_width, Some(balanced.size))?;
|
||||||
|
width = width.max(size.width);
|
||||||
|
height += size.height;
|
||||||
|
}
|
||||||
|
Ok(Size::new(width, height))
|
||||||
|
} else {
|
||||||
|
let mut width = 0;
|
||||||
|
let mut height = 0;
|
||||||
|
for segment in &self.segments {
|
||||||
|
let size = segment.inner.size(frame, max_width, max_height)?;
|
||||||
|
width = width.max(size.width);
|
||||||
|
height += size.height;
|
||||||
|
}
|
||||||
|
Ok(Size::new(width, height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(self, frame: &mut Frame) -> Result<(), E> {
|
||||||
|
let size = frame.size();
|
||||||
|
let max_width = Some(size.width);
|
||||||
|
let max_height = Some(size.height);
|
||||||
|
|
||||||
|
let mut balanced_segments = vec![];
|
||||||
|
for segment in &self.segments {
|
||||||
|
let size = segment.inner.size(frame, max_width, max_height)?;
|
||||||
|
balanced_segments.push(Segment::vertical(size, segment));
|
||||||
|
}
|
||||||
|
balance(&mut balanced_segments, size.height);
|
||||||
|
|
||||||
|
let mut y = 0;
|
||||||
|
for (segment, balanced) in self.segments.into_iter().zip(balanced_segments.into_iter()) {
|
||||||
|
frame.push(Pos::new(0, y), Size::new(size.width, balanced.size));
|
||||||
|
segment.inner.draw(frame)?;
|
||||||
|
frame.pop();
|
||||||
|
y += balanced.size as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<E, I> AsyncWidget<E> for JoinV<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> {
|
||||||
|
if let Some(max_height) = max_height {
|
||||||
|
let mut balanced_segments = vec![];
|
||||||
|
for segment in &self.segments {
|
||||||
|
let size = segment
|
||||||
|
.inner
|
||||||
|
.size(frame, max_width, Some(max_height))
|
||||||
|
.await?;
|
||||||
|
balanced_segments.push(Segment::vertical(size, segment));
|
||||||
|
}
|
||||||
|
balance(&mut balanced_segments, max_height);
|
||||||
|
|
||||||
|
let mut width = 0;
|
||||||
|
let mut height = 0;
|
||||||
|
for (segment, balanced) in self.segments.iter().zip(balanced_segments.into_iter()) {
|
||||||
|
let size = segment
|
||||||
|
.inner
|
||||||
|
.size(frame, max_width, Some(balanced.size))
|
||||||
|
.await?;
|
||||||
|
width = width.max(size.width);
|
||||||
|
height += size.height;
|
||||||
|
}
|
||||||
|
Ok(Size::new(width, height))
|
||||||
|
} else {
|
||||||
|
let mut width = 0;
|
||||||
|
let mut height = 0;
|
||||||
|
for segment in &self.segments {
|
||||||
|
let size = segment.inner.size(frame, max_width, max_height).await?;
|
||||||
|
width = width.max(size.width);
|
||||||
|
height += size.height;
|
||||||
|
}
|
||||||
|
Ok(Size::new(width, height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn draw(self, frame: &mut Frame) -> Result<(), E> {
|
||||||
|
let size = frame.size();
|
||||||
|
let max_width = Some(size.width);
|
||||||
|
let max_height = Some(size.height);
|
||||||
|
|
||||||
|
let mut balanced_segments = vec![];
|
||||||
|
for segment in &self.segments {
|
||||||
|
let size = segment.inner.size(frame, max_width, max_height).await?;
|
||||||
|
balanced_segments.push(Segment::vertical(size, segment));
|
||||||
|
}
|
||||||
|
balance(&mut balanced_segments, size.height);
|
||||||
|
|
||||||
|
let mut y = 0;
|
||||||
|
for (segment, balanced) in self.segments.into_iter().zip(balanced_segments.into_iter()) {
|
||||||
|
frame.push(Pos::new(0, y), Size::new(size.width, balanced.size));
|
||||||
|
segment.inner.draw(frame).await?;
|
||||||
|
frame.pop();
|
||||||
|
y += balanced.size as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue