use figures::{Fraction, ScreenScale, Size};
use crate::context::{AsEventContext, EventContext, LayoutContext};
use crate::styles::DimensionRange;
use crate::widget::{MakeWidget, RootBehavior, WidgetRef, WrappedLayout, WrapperWidget};
use crate::ConstraintLimit;
#[derive(Debug)]
pub struct Resize {
pub width: DimensionRange,
pub height: DimensionRange,
child: WidgetRef,
}
impl Resize {
#[must_use]
pub fn child(&self) -> &WidgetRef {
&self.child
}
#[must_use]
pub fn to<T>(size: Size<T>, child: impl MakeWidget) -> Self
where
T: Into<DimensionRange>,
{
Self {
child: WidgetRef::new(child),
width: size.width.into(),
height: size.height.into(),
}
}
#[must_use]
pub fn from_width(width: impl Into<DimensionRange>, child: impl MakeWidget) -> Self {
Self {
child: WidgetRef::new(child),
width: width.into(),
height: DimensionRange::from(..),
}
}
#[must_use]
pub fn width(mut self, width: impl Into<DimensionRange>) -> Self {
self.width = width.into();
self
}
#[must_use]
pub fn height(mut self, height: impl Into<DimensionRange>) -> Self {
self.height = height.into();
self
}
#[must_use]
pub fn from_height(height: impl Into<DimensionRange>, child: impl MakeWidget) -> Self {
Self {
child: WidgetRef::new(child),
width: DimensionRange::from(..),
height: height.into(),
}
}
}
impl WrapperWidget for Resize {
fn child_mut(&mut self) -> &mut WidgetRef {
&mut self.child
}
fn root_behavior(&mut self, _context: &mut EventContext<'_>) -> Option<RootBehavior> {
Some(RootBehavior::Resize(Size::new(self.width, self.height)))
}
fn layout_child(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout {
let child = self.child.mounted(&mut context.as_event_context());
let (size, fill_layout) = if let (Some(width), Some(height)) =
(self.width.exact_dimension(), self.height.exact_dimension())
{
(
Size::new(width, height).map(|i| i.into_upx(context.gfx.scale())),
true,
)
} else {
let available_space = Size::new(
override_constraint(available_space.width, self.width, context.gfx.scale()),
override_constraint(available_space.height, self.height, context.gfx.scale()),
);
(
context.for_other(&child).layout(available_space),
matches!(available_space.width, ConstraintLimit::SizeToFit(_))
|| matches!(available_space.height, ConstraintLimit::SizeToFit(_)),
)
};
let mut size = Size::new(
self.width.clamp(size.width, context.gfx.scale()),
self.height.clamp(size.height, context.gfx.scale()),
);
if fill_layout {
size = context
.for_other(&child)
.layout(size.map(ConstraintLimit::Fill))
.min(size);
}
WrappedLayout::aligned(size, available_space, context)
}
}
fn override_constraint(
constraint: ConstraintLimit,
range: DimensionRange,
scale: Fraction,
) -> ConstraintLimit {
match constraint {
ConstraintLimit::Fill(size) => ConstraintLimit::Fill(range.clamp(size, scale)),
ConstraintLimit::SizeToFit(clipped_after) => match (range.minimum(), range.maximum()) {
(Some(min), Some(max)) if min == max => ConstraintLimit::Fill(min.into_upx(scale)),
_ => ConstraintLimit::SizeToFit(range.minimum().map_or_else(
|| range.clamp(clipped_after, scale),
|min| min.into_upx(scale),
)),
},
}
}