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}