use crate::shell::kurbo::{Point, Rect, Size};
use crate::{
BoxConstraints, Data, Env, Event, EventCtx, LayoutCtx, PaintBrush, PaintCtx, RenderContext,
UpdateCtx, Widget, WidgetPod,
};
struct BorderState {
width: f64,
brush: PaintBrush,
}
#[derive(Default)]
struct ContainerStyle {
background: Option<PaintBrush>,
border: Option<BorderState>,
}
pub struct Container<T: Data> {
style: ContainerStyle,
inner: WidgetPod<T, Box<dyn Widget<T>>>,
}
impl<T: Data> Container<T> {
pub fn new(inner: impl Widget<T> + 'static) -> Self {
Self {
style: ContainerStyle::default(),
inner: WidgetPod::new(inner).boxed(),
}
}
pub fn background(mut self, brush: impl Into<PaintBrush>) -> Self {
self.style.background = Some(brush.into());
self
}
pub fn border(mut self, brush: impl Into<PaintBrush>, width: f64) -> Self {
self.style.border = Some(BorderState {
width,
brush: brush.into(),
});
self
}
}
impl<T: Data + 'static> Widget<T> for Container<T> {
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
self.inner.event(ctx, event, data, env);
}
fn update(&mut self, ctx: &mut UpdateCtx, _old_data: Option<&T>, data: &T, env: &Env) {
self.inner.update(ctx, data, env);
}
fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
bc.debug_check("Container");
let border_width = match self.style.border {
Some(ref border) => border.width,
None => 0.0,
};
let child_bc = bc.shrink((2.0 * border_width, 2.0 * border_width));
let size = self.inner.layout(ctx, &child_bc, data, env);
let origin = Point::new(border_width, border_width);
self.inner
.set_layout_rect(Rect::from_origin_size(origin, size));
Size::new(
size.width + 2.0 * border_width,
size.height + 2.0 * border_width,
)
}
fn paint(&mut self, paint_ctx: &mut PaintCtx, data: &T, env: &Env) {
if let Some(ref brush) = self.style.background {
let rect = Rect::from_origin_size(Point::ZERO, paint_ctx.size());
paint_ctx.render_ctx.fill(rect, brush);
}
if let Some(ref border) = self.style.border {
let offset = border.width / 2.0;
let size = Size::new(
paint_ctx.size().width - border.width,
paint_ctx.size().height - border.width,
);
let rect = Rect::from_origin_size((offset, offset), size);
paint_ctx
.render_ctx
.stroke(rect, &border.brush, border.width);
}
self.inner.paint_with_offset(paint_ctx, data, env);
}
}