use std::ops::{Bound, RangeBounds};
use le::{Layout, MinimumNatural};
use crate::{
le::layout::{Rect, Vec2},
terminal::Buffer,
Widget,
};
pub struct SizeConstraint<
W: RangeBounds<usize>,
H: RangeBounds<usize>,
T: Widget,
> {
pub widget: T,
pub width: W,
pub height: H,
}
impl<W, H, T> SizeConstraint<W, H, T>
where
W: RangeBounds<usize>,
H: RangeBounds<usize>,
T: Widget,
{
pub fn new(widget: T, width: W, height: H) -> Self {
Self {
widget,
width,
height,
}
}
fn clamp_width(&self, width: usize) -> usize {
let start = start_bound_n(&self.width);
let end = end_bound_n(&self.width);
width.clamp(start, end.max(start))
}
fn clamp_height(&self, height: usize) -> usize {
let start = start_bound_n(&self.height);
let end = end_bound_n(&self.height);
height.clamp(start, end.max(start))
}
}
fn start_bound_n<R: RangeBounds<usize>>(range: &R) -> usize {
bound_n(&range.start_bound()).unwrap_or(0)
}
fn end_bound_n<R: RangeBounds<usize>>(range: &R) -> usize {
bound_n(&range.end_bound()).unwrap_or(usize::MAX)
}
fn bound_n(bound: &Bound<&usize>) -> Option<usize> {
match bound {
Bound::Excluded(x) => Some(x.saturating_sub(1)),
Bound::Included(x) => Some(**x),
Bound::Unbounded => None,
}
}
impl<W, H, T> Layout for SizeConstraint<W, H, T>
where
W: RangeBounds<usize>,
H: RangeBounds<usize>,
T: Widget,
{
fn width_for_height(&self, height: usize) -> MinimumNatural<usize> {
let height = self.clamp_height(height);
MinimumNatural {
minimum: self
.clamp_width(self.widget.width_for_height(height).minimum),
natural: self
.clamp_width(self.widget.width_for_height(height).natural),
}
}
fn height_for_width(&self, width: usize) -> MinimumNatural<usize> {
let width = self.clamp_width(width);
MinimumNatural {
minimum: self
.clamp_height(self.widget.height_for_width(width).minimum),
natural: self
.clamp_height(self.widget.height_for_width(width).natural),
}
}
fn prefered_size(&self) -> MinimumNatural<Vec2> {
let size_bounds = (
bound_n(&self.width.end_bound()),
bound_n(&self.height.end_bound()),
);
let widget_size_og = if let (Some(size_x), Some(size_y)) = size_bounds {
self.widget
.prefered_size_of_container(Vec2::new(size_x, size_y))
} else {
self.widget.prefered_size()
};
let mut widget_size = widget_size_og;
widget_size.minimum.x = self.clamp_width(widget_size_og.minimum.x);
widget_size.minimum.y = self.clamp_height(widget_size_og.minimum.y);
widget_size.natural.x = self.clamp_width(widget_size_og.natural.x);
widget_size.natural.y = self.clamp_height(widget_size_og.natural.y);
if widget_size.minimum.x != widget_size_og.minimum.x {
widget_size.minimum.y = self.clamp_height(
self.height_for_width(widget_size.minimum.x).minimum,
);
}
if widget_size.minimum.y != widget_size_og.minimum.y {
widget_size.minimum.x = self.clamp_width(
self.width_for_height(widget_size.minimum.y).minimum,
);
}
if widget_size.natural.x != widget_size_og.natural.x {
widget_size.natural.y = self.clamp_height(
self.height_for_width(widget_size.natural.x).natural,
);
}
if widget_size.natural.y != widget_size_og.natural.y {
widget_size.natural.x = self.clamp_width(
self.width_for_height(widget_size.natural.y).natural,
);
}
widget_size
}
fn prefered_size_of_container(
&self,
container: Vec2,
) -> MinimumNatural<Vec2> {
let container = Vec2::new(
self.clamp_width(container.x),
self.clamp_height(container.y),
);
let widget_size_og = self.widget.prefered_size_of_container(container);
let mut widget_size = self.widget.prefered_size_of_container(container);
widget_size.minimum.x = self.clamp_width(widget_size_og.minimum.x);
widget_size.minimum.y = self.clamp_height(widget_size_og.minimum.y);
widget_size.natural.x = self.clamp_width(widget_size_og.natural.x);
widget_size.natural.y = self.clamp_height(widget_size_og.natural.y);
if widget_size.minimum.x != widget_size_og.minimum.x {
widget_size.minimum.y = self.clamp_height(
self.height_for_width(widget_size.minimum.x).minimum,
);
}
if widget_size.minimum.y != widget_size_og.minimum.y {
widget_size.minimum.x = self.clamp_width(
self.width_for_height(widget_size.minimum.y).minimum,
);
}
if widget_size.natural.x != widget_size_og.natural.x {
widget_size.natural.y = self.clamp_height(
self.height_for_width(widget_size.natural.x).natural,
);
}
if widget_size.natural.y != widget_size_og.natural.y {
widget_size.natural.x = self.clamp_width(
self.width_for_height(widget_size.natural.y).natural,
);
}
widget_size
}
}
impl<W, H, T> Widget for SizeConstraint<W, H, T>
where
W: RangeBounds<usize>,
H: RangeBounds<usize>,
T: Widget,
{
fn render(&self, rect: Rect, buffer: &mut Buffer) {
let mut rect = rect;
if self.clamp_width(rect.size.x) != rect.size.x {
rect.size.x =
self.clamp_width(self.width_for_height(rect.size.y).natural);
}
if self.clamp_height(rect.size.y) != rect.size.y {
rect.size.y =
self.clamp_height(self.height_for_width(rect.size.x).natural);
}
self.widget.render(rect, buffer);
}
}