use std::f64::INFINITY;
use crate::shell::kurbo::Size;
use crate::{
BoxConstraints, Data, Env, Event, EventCtx, LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx,
UpdateCtx, Widget, WidgetId,
};
pub struct SizedBox<T> {
inner: Option<Box<dyn Widget<T>>>,
width: Option<f64>,
height: Option<f64>,
}
impl<T> SizedBox<T> {
pub fn new(inner: impl Widget<T> + 'static) -> Self {
Self {
inner: Some(Box::new(inner)),
width: None,
height: None,
}
}
pub fn empty() -> Self {
Self {
inner: None,
width: None,
height: None,
}
}
pub fn width(mut self, width: f64) -> Self {
self.width = Some(width);
self
}
pub fn height(mut self, height: f64) -> Self {
self.height = Some(height);
self
}
pub fn expand(mut self) -> Self {
self.width = Some(INFINITY);
self.height = Some(INFINITY);
self
}
pub fn expand_width(mut self) -> Self {
self.width = Some(INFINITY);
self
}
pub fn expand_height(mut self) -> Self {
self.height = Some(INFINITY);
self
}
fn child_constraints(&self, bc: &BoxConstraints) -> BoxConstraints {
let (min_width, max_width) = match self.width {
Some(width) => {
let w = width.max(bc.min().width).min(bc.max().width);
(w, w)
}
None => (bc.min().width, bc.max().width),
};
let (min_height, max_height) = match self.height {
Some(height) => {
let h = height.max(bc.min().height).min(bc.max().height);
(h, h)
}
None => (bc.min().height, bc.max().height),
};
BoxConstraints::new(
Size::new(min_width, min_height),
Size::new(max_width, max_height),
)
}
#[cfg(test)]
pub(crate) fn width_and_height(&self) -> (Option<f64>, Option<f64>) {
(self.width, self.height)
}
}
impl<T: Data> Widget<T> for SizedBox<T> {
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
if let Some(ref mut inner) = self.inner {
inner.event(ctx, event, data, env);
}
}
fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
if let Some(ref mut inner) = self.inner {
inner.lifecycle(ctx, event, data, env)
}
}
fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env) {
if let Some(ref mut inner) = self.inner {
inner.update(ctx, old_data, data, env);
}
}
fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
bc.debug_check("SizedBox");
let child_bc = self.child_constraints(bc);
let size = match self.inner.as_mut() {
Some(inner) => inner.layout(ctx, &child_bc, data, env),
None => bc.constrain((self.width.unwrap_or(0.0), self.height.unwrap_or(0.0))),
};
if size.width.is_infinite() {
log::warn!("SizedBox is returning an infinite width.");
}
if size.height.is_infinite() {
log::warn!("SizedBox is returning an infinite height.");
}
size
}
fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
if let Some(ref mut inner) = self.inner {
inner.paint(ctx, data, env);
}
}
fn id(&self) -> Option<WidgetId> {
self.inner.as_ref().and_then(|inner| inner.id())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::widget::Label;
#[test]
fn expand() {
let expand = SizedBox::<()>::new(Label::new("hello!")).expand();
let bc = BoxConstraints::tight(Size::new(400., 400.)).loosen();
let child_bc = expand.child_constraints(&bc);
assert_eq!(child_bc.min(), Size::new(400., 400.,));
}
#[test]
fn no_width() {
let expand = SizedBox::<()>::new(Label::new("hello!")).height(200.);
let bc = BoxConstraints::tight(Size::new(400., 400.)).loosen();
let child_bc = expand.child_constraints(&bc);
assert_eq!(child_bc.min(), Size::new(0., 200.,));
assert_eq!(child_bc.max(), Size::new(400., 200.,));
}
}