use std::ops::Range; use json::ToJson; use serde_json::json; use crate::{ geometry::{rect::RectF, vector::Vector2F}, json, Drawable, Element, SceneBuilder, SizeConstraint, View, ViewContext, }; pub struct ConstrainedBox { child: Element, constraint: Constraint, } pub enum Constraint { Static(SizeConstraint), Dynamic(Box) -> SizeConstraint>), } impl ToJson for Constraint { fn to_json(&self) -> serde_json::Value { match self { Constraint::Static(constraint) => constraint.to_json(), Constraint::Dynamic(_) => "dynamic".into(), } } } impl ConstrainedBox { pub fn new(child: impl Drawable) -> Self { Self { child: child.into_element(), constraint: Constraint::Static(Default::default()), } } pub fn dynamically( mut self, constraint: impl 'static + FnMut(SizeConstraint, &mut V, &mut ViewContext) -> SizeConstraint, ) -> Self { self.constraint = Constraint::Dynamic(Box::new(constraint)); self } pub fn with_min_width(mut self, min_width: f32) -> Self { if let Constraint::Dynamic(_) = self.constraint { self.constraint = Constraint::Static(Default::default()); } if let Constraint::Static(constraint) = &mut self.constraint { constraint.min.set_x(min_width); } else { unreachable!() } self } pub fn with_max_width(mut self, max_width: f32) -> Self { if let Constraint::Dynamic(_) = self.constraint { self.constraint = Constraint::Static(Default::default()); } if let Constraint::Static(constraint) = &mut self.constraint { constraint.max.set_x(max_width); } else { unreachable!() } self } pub fn with_max_height(mut self, max_height: f32) -> Self { if let Constraint::Dynamic(_) = self.constraint { self.constraint = Constraint::Static(Default::default()); } if let Constraint::Static(constraint) = &mut self.constraint { constraint.max.set_y(max_height); } else { unreachable!() } self } pub fn with_width(mut self, width: f32) -> Self { if let Constraint::Dynamic(_) = self.constraint { self.constraint = Constraint::Static(Default::default()); } if let Constraint::Static(constraint) = &mut self.constraint { constraint.min.set_x(width); constraint.max.set_x(width); } else { unreachable!() } self } pub fn with_height(mut self, height: f32) -> Self { if let Constraint::Dynamic(_) = self.constraint { self.constraint = Constraint::Static(Default::default()); } if let Constraint::Static(constraint) = &mut self.constraint { constraint.min.set_y(height); constraint.max.set_y(height); } else { unreachable!() } self } fn constraint( &mut self, input_constraint: SizeConstraint, view: &mut V, cx: &mut ViewContext, ) -> SizeConstraint { match &mut self.constraint { Constraint::Static(constraint) => *constraint, Constraint::Dynamic(compute_constraint) => { compute_constraint(input_constraint, view, cx) } } } } impl Drawable for ConstrainedBox { type LayoutState = (); type PaintState = (); fn layout( &mut self, mut parent_constraint: SizeConstraint, view: &mut V, cx: &mut ViewContext, ) -> (Vector2F, Self::LayoutState) { let constraint = self.constraint(parent_constraint, view, cx); parent_constraint.min = parent_constraint.min.max(constraint.min); parent_constraint.max = parent_constraint.max.min(constraint.max); parent_constraint.max = parent_constraint.max.max(parent_constraint.min); let size = self.child.layout(parent_constraint, view, cx); (size, ()) } fn paint( &mut self, scene: &mut SceneBuilder, bounds: RectF, visible_bounds: RectF, _: &mut Self::LayoutState, view: &mut V, cx: &mut ViewContext, ) -> Self::PaintState { scene.paint_layer(Some(visible_bounds), |scene| { self.child .paint(scene, bounds.origin(), visible_bounds, view, cx); }) } fn rect_for_text_range( &self, range_utf16: Range, _: RectF, _: RectF, _: &Self::LayoutState, _: &Self::PaintState, view: &V, cx: &ViewContext, ) -> Option { self.child.rect_for_text_range(range_utf16, view, cx) } fn debug( &self, _: RectF, _: &Self::LayoutState, _: &Self::PaintState, view: &V, cx: &ViewContext, ) -> json::Value { json!({"type": "ConstrainedBox", "assigned_constraint": self.constraint.to_json(), "child": self.child.debug(view, cx)}) } }