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