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