constrained_box.rs

  1use std::ops::Range;
  2
  3use json::ToJson;
  4use serde_json::json;
  5
  6use crate::{
  7    geometry::{rect::RectF, vector::Vector2F},
  8    json,
  9    presenter::MeasurementContext,
 10    DebugContext, Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
 11};
 12
 13pub struct ConstrainedBox {
 14    child: ElementBox,
 15    constraint: Constraint,
 16}
 17
 18pub enum Constraint {
 19    Static(SizeConstraint),
 20    Dynamic(Box<dyn FnMut(SizeConstraint, &mut LayoutContext) -> SizeConstraint>),
 21}
 22
 23impl ToJson for Constraint {
 24    fn to_json(&self) -> serde_json::Value {
 25        match self {
 26            Constraint::Static(constraint) => constraint.to_json(),
 27            Constraint::Dynamic(_) => "dynamic".into(),
 28        }
 29    }
 30}
 31
 32impl ConstrainedBox {
 33    pub fn new(child: ElementBox) -> Self {
 34        Self {
 35            child,
 36            constraint: Constraint::Static(Default::default()),
 37        }
 38    }
 39
 40    pub fn dynamically(
 41        mut self,
 42        constraint: impl 'static + FnMut(SizeConstraint, &mut LayoutContext) -> SizeConstraint,
 43    ) -> Self {
 44        self.constraint = Constraint::Dynamic(Box::new(constraint));
 45        self
 46    }
 47
 48    pub fn with_min_width(mut self, min_width: f32) -> Self {
 49        if let Constraint::Dynamic(_) = self.constraint {
 50            self.constraint = Constraint::Static(Default::default());
 51        }
 52
 53        if let Constraint::Static(constraint) = &mut self.constraint {
 54            constraint.min.set_x(min_width);
 55        } else {
 56            unreachable!()
 57        }
 58
 59        self
 60    }
 61
 62    pub fn with_max_width(mut self, max_width: f32) -> Self {
 63        if let Constraint::Dynamic(_) = self.constraint {
 64            self.constraint = Constraint::Static(Default::default());
 65        }
 66
 67        if let Constraint::Static(constraint) = &mut self.constraint {
 68            constraint.max.set_x(max_width);
 69        } else {
 70            unreachable!()
 71        }
 72
 73        self
 74    }
 75
 76    pub fn with_max_height(mut self, max_height: f32) -> Self {
 77        if let Constraint::Dynamic(_) = self.constraint {
 78            self.constraint = Constraint::Static(Default::default());
 79        }
 80
 81        if let Constraint::Static(constraint) = &mut self.constraint {
 82            constraint.max.set_y(max_height);
 83        } else {
 84            unreachable!()
 85        }
 86
 87        self
 88    }
 89
 90    pub fn with_width(mut self, width: f32) -> Self {
 91        if let Constraint::Dynamic(_) = self.constraint {
 92            self.constraint = Constraint::Static(Default::default());
 93        }
 94
 95        if let Constraint::Static(constraint) = &mut self.constraint {
 96            constraint.min.set_x(width);
 97            constraint.max.set_x(width);
 98        } else {
 99            unreachable!()
100        }
101
102        self
103    }
104
105    pub fn with_height(mut self, height: f32) -> Self {
106        if let Constraint::Dynamic(_) = self.constraint {
107            self.constraint = Constraint::Static(Default::default());
108        }
109
110        if let Constraint::Static(constraint) = &mut self.constraint {
111            constraint.min.set_y(height);
112            constraint.max.set_y(height);
113        } else {
114            unreachable!()
115        }
116
117        self
118    }
119
120    fn constraint(
121        &mut self,
122        input_constraint: SizeConstraint,
123        cx: &mut LayoutContext,
124    ) -> SizeConstraint {
125        match &mut self.constraint {
126            Constraint::Static(constraint) => *constraint,
127            Constraint::Dynamic(compute_constraint) => compute_constraint(input_constraint, cx),
128        }
129    }
130}
131
132impl Element for ConstrainedBox {
133    type LayoutState = ();
134    type PaintState = ();
135
136    fn layout(
137        &mut self,
138        mut parent_constraint: SizeConstraint,
139        cx: &mut LayoutContext,
140    ) -> (Vector2F, Self::LayoutState) {
141        let constraint = self.constraint(parent_constraint, cx);
142        parent_constraint.min = parent_constraint.min.max(constraint.min);
143        parent_constraint.max = parent_constraint.max.min(constraint.max);
144        parent_constraint.max = parent_constraint.max.max(parent_constraint.min);
145        let size = self.child.layout(parent_constraint, cx);
146        (size, ())
147    }
148
149    fn paint(
150        &mut self,
151        bounds: RectF,
152        visible_bounds: RectF,
153        _: &mut Self::LayoutState,
154        cx: &mut PaintContext,
155    ) -> Self::PaintState {
156        cx.paint_layer(Some(visible_bounds), |cx| {
157            self.child.paint(bounds.origin(), visible_bounds, cx);
158        })
159    }
160
161    fn rect_for_text_range(
162        &self,
163        range_utf16: Range<usize>,
164        _: RectF,
165        _: RectF,
166        _: &Self::LayoutState,
167        _: &Self::PaintState,
168        cx: &MeasurementContext,
169    ) -> Option<RectF> {
170        self.child.rect_for_text_range(range_utf16, cx)
171    }
172
173    fn debug(
174        &self,
175        _: RectF,
176        _: &Self::LayoutState,
177        _: &Self::PaintState,
178        cx: &DebugContext,
179    ) -> json::Value {
180        json!({"type": "ConstrainedBox", "assigned_constraint": self.constraint.to_json(), "child": self.child.debug(cx)})
181    }
182}