From f581fa6c470b9788b02b75ea46e793c52dec1eb6 Mon Sep 17 00:00:00 2001 From: Joscha Date: Sat, 18 Feb 2023 18:54:51 +0100 Subject: [PATCH] Add JoinH and JoinV --- src/widget.rs | 6 +- src/widgets/join.rs | 319 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 324 insertions(+), 1 deletion(-) diff --git a/src/widget.rs b/src/widget.rs index 2fc3eb9..ef575cd 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -1,6 +1,6 @@ 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}; // TODO Feature-gate these traits @@ -61,6 +61,10 @@ pub trait WidgetExt: Sized { Float::new(self) } + fn segment(self) -> JoinSegment { + JoinSegment::new(self) + } + fn below(self, above: W) -> Layer { Layer::new(self, above) } diff --git a/src/widgets/join.rs b/src/widgets/join.rs index 4cc301b..90156a2 100644 --- a/src/widgets/join.rs +++ b/src/widgets/join.rs @@ -1,5 +1,9 @@ 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: // // 1. Use the available space @@ -50,6 +54,22 @@ struct Segment { weight: f32, } +impl Segment { + fn horizontal(size: Size, segment: &JoinSegment) -> Self { + Self { + size: size.width, + weight: segment.weight, + } + } + + fn vertical(size: Size, segment: &JoinSegment) -> Self { + Self { + size: size.height, + weight: segment.weight, + } + } +} + fn balance(segments: &mut [Segment], available: u16) { if segments.is_empty() { return; @@ -189,3 +209,302 @@ fn shrink(segments: &mut [Segment], mut available: u16) { segment.size += 1; } } + +pub struct JoinSegment { + inner: I, + weight: f32, +} + +impl JoinSegment { + 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 { + segments: Vec>, +} + +impl JoinH { + pub fn new(segments: Vec>) -> Self { + Self { segments } + } +} + +impl Widget for JoinH +where + I: Widget, +{ + fn size( + &self, + frame: &mut Frame, + max_width: Option, + max_height: Option, + ) -> Result { + 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 AsyncWidget for JoinH +where + I: AsyncWidget + Send + Sync, +{ + async fn size( + &self, + frame: &mut Frame, + max_width: Option, + max_height: Option, + ) -> Result { + 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 { + segments: Vec>, +} + +impl JoinV { + pub fn new(segments: Vec>) -> Self { + Self { segments } + } +} + +impl Widget for JoinV +where + I: Widget, +{ + fn size( + &self, + frame: &mut Frame, + max_width: Option, + max_height: Option, + ) -> Result { + 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 AsyncWidget for JoinV +where + I: AsyncWidget + Send + Sync, +{ + async fn size( + &self, + frame: &mut Frame, + max_width: Option, + max_height: Option, + ) -> Result { + 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(()) + } +}