1mod align;
2mod canvas;
3mod clipped;
4mod constrained_box;
5mod container;
6mod empty;
7mod expanded;
8mod flex;
9mod hook;
10mod image;
11mod keystroke_label;
12mod label;
13mod list;
14mod mouse_event_handler;
15mod overlay;
16mod resizable;
17mod stack;
18mod svg;
19mod text;
20mod tooltip;
21mod uniform_list;
22
23pub use self::{
24 align::*, canvas::*, constrained_box::*, container::*, empty::*, flex::*, hook::*, image::*,
25 keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*, resizable::*,
26 stack::*, svg::*, text::*, tooltip::*, uniform_list::*,
27};
28use self::{clipped::Clipped, expanded::Expanded};
29use crate::{
30 geometry::{
31 rect::RectF,
32 vector::{vec2f, Vector2F},
33 },
34 json, Action, SceneBuilder, SizeConstraint, View, ViewContext,
35};
36use core::panic;
37use json::ToJson;
38use std::{any::Any, borrow::Cow, marker::PhantomData, mem, ops::Range};
39
40trait AnyElement<V: View> {
41 fn layout(
42 &mut self,
43 constraint: SizeConstraint,
44 view: &mut V,
45 cx: &mut ViewContext<V>,
46 ) -> Vector2F;
47
48 fn paint(
49 &mut self,
50 scene: &mut SceneBuilder,
51 origin: Vector2F,
52 visible_bounds: RectF,
53 view: &mut V,
54 cx: &mut ViewContext<V>,
55 );
56
57 fn rect_for_text_range(
58 &self,
59 range_utf16: Range<usize>,
60 view: &V,
61 cx: &ViewContext<V>,
62 ) -> Option<RectF>;
63
64 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value;
65
66 fn size(&self) -> Vector2F;
67
68 fn metadata(&self) -> Option<&dyn Any>;
69}
70
71pub trait Element<V: View> {
72 type LayoutState;
73 type PaintState;
74
75 fn layout(
76 &mut self,
77 constraint: SizeConstraint,
78 view: &mut V,
79 cx: &mut ViewContext<V>,
80 ) -> (Vector2F, Self::LayoutState);
81
82 fn paint(
83 &mut self,
84 scene: &mut SceneBuilder,
85 bounds: RectF,
86 visible_bounds: RectF,
87 layout: &mut Self::LayoutState,
88 view: &mut V,
89 cx: &mut ViewContext<V>,
90 ) -> Self::PaintState;
91
92 fn rect_for_text_range(
93 &self,
94 range_utf16: Range<usize>,
95 bounds: RectF,
96 visible_bounds: RectF,
97 layout: &Self::LayoutState,
98 paint: &Self::PaintState,
99 view: &V,
100 cx: &ViewContext<V>,
101 ) -> Option<RectF>;
102
103 fn metadata(&self) -> Option<&dyn Any> {
104 None
105 }
106
107 fn debug(
108 &self,
109 bounds: RectF,
110 layout: &Self::LayoutState,
111 paint: &Self::PaintState,
112 view: &V,
113 cx: &ViewContext<V>,
114 ) -> serde_json::Value;
115
116 fn boxed(self) -> ElementBox<V>
117 where
118 Self: 'static + Sized,
119 {
120 ElementBox {
121 element: Box::new(Lifecycle::Init { element: self }),
122 view_type: PhantomData,
123 name: None,
124 }
125 }
126
127 fn named(self, name: impl Into<Cow<'static, str>>) -> ElementBox<V>
128 where
129 Self: 'static + Sized,
130 {
131 ElementBox {
132 element: Box::new(Lifecycle::Init { element: self }),
133 view_type: PhantomData,
134 name: Some(name.into()),
135 }
136 }
137
138 fn constrained(self) -> ConstrainedBox<V>
139 where
140 Self: 'static + Sized,
141 {
142 ConstrainedBox::new(self.boxed())
143 }
144
145 fn aligned(self) -> Align<V>
146 where
147 Self: 'static + Sized,
148 {
149 Align::new(self.boxed())
150 }
151
152 fn clipped(self) -> Clipped<V>
153 where
154 Self: 'static + Sized,
155 {
156 Clipped::new(self.boxed())
157 }
158
159 fn contained(self) -> Container<V>
160 where
161 Self: 'static + Sized,
162 {
163 Container::new(self.boxed())
164 }
165
166 fn expanded(self) -> Expanded<V>
167 where
168 Self: 'static + Sized,
169 {
170 Expanded::new(self.boxed())
171 }
172
173 fn flex(self, flex: f32, expanded: bool) -> FlexItem<V>
174 where
175 Self: 'static + Sized,
176 {
177 FlexItem::new(self.boxed()).flex(flex, expanded)
178 }
179
180 fn flex_float(self) -> FlexItem<V>
181 where
182 Self: 'static + Sized,
183 {
184 FlexItem::new(self.boxed()).float()
185 }
186
187 fn with_tooltip<Tag: 'static>(
188 self,
189 id: usize,
190 text: String,
191 action: Option<Box<dyn Action>>,
192 style: TooltipStyle,
193 cx: &mut ViewContext<V>,
194 ) -> Tooltip<V>
195 where
196 Self: 'static + Sized,
197 {
198 Tooltip::new::<Tag, V>(id, text, action, style, self.boxed(), cx)
199 }
200
201 fn with_resize_handle<Tag: 'static>(
202 self,
203 element_id: usize,
204 side: Side,
205 handle_size: f32,
206 initial_size: f32,
207 cx: &mut ViewContext<V>,
208 ) -> Resizable<V>
209 where
210 Self: 'static + Sized,
211 {
212 Resizable::new::<Tag, V>(
213 self.boxed(),
214 element_id,
215 side,
216 handle_size,
217 initial_size,
218 cx,
219 )
220 }
221}
222
223pub enum Lifecycle<V: View, E: Element<V>> {
224 Empty,
225 Init {
226 element: E,
227 },
228 PostLayout {
229 element: E,
230 constraint: SizeConstraint,
231 size: Vector2F,
232 layout: E::LayoutState,
233 },
234 PostPaint {
235 element: E,
236 constraint: SizeConstraint,
237 bounds: RectF,
238 visible_bounds: RectF,
239 layout: E::LayoutState,
240 paint: E::PaintState,
241 },
242}
243
244impl<V: View, E: Element<V>> AnyElement<V> for Lifecycle<V, E> {
245 fn layout(
246 &mut self,
247 constraint: SizeConstraint,
248 view: &mut V,
249 cx: &mut ViewContext<V>,
250 ) -> Vector2F {
251 let result;
252 *self = match mem::take(self) {
253 Lifecycle::Empty => unreachable!(),
254 Lifecycle::Init { mut element }
255 | Lifecycle::PostLayout { mut element, .. }
256 | Lifecycle::PostPaint { mut element, .. } => {
257 let (size, layout) = element.layout(constraint, view, cx);
258 debug_assert!(size.x().is_finite());
259 debug_assert!(size.y().is_finite());
260
261 result = size;
262 Lifecycle::PostLayout {
263 element,
264 constraint,
265 size,
266 layout,
267 }
268 }
269 };
270 result
271 }
272
273 fn paint(
274 &mut self,
275 scene: &mut SceneBuilder,
276 origin: Vector2F,
277 visible_bounds: RectF,
278 view: &mut V,
279 cx: &mut ViewContext<V>,
280 ) {
281 *self = match mem::take(self) {
282 Lifecycle::PostLayout {
283 mut element,
284 constraint,
285 size,
286 mut layout,
287 } => {
288 let bounds = RectF::new(origin, size);
289 let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
290 Lifecycle::PostPaint {
291 element,
292 constraint,
293 bounds,
294 visible_bounds,
295 layout,
296 paint,
297 }
298 }
299 Lifecycle::PostPaint {
300 mut element,
301 constraint,
302 bounds,
303 mut layout,
304 ..
305 } => {
306 let bounds = RectF::new(origin, bounds.size());
307 let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
308 Lifecycle::PostPaint {
309 element,
310 constraint,
311 bounds,
312 visible_bounds,
313 layout,
314 paint,
315 }
316 }
317 Lifecycle::Empty => panic!("invalid element lifecycle state"),
318 Lifecycle::Init { .. } => {
319 panic!("invalid element lifecycle state, paint called before layout")
320 }
321 }
322 }
323
324 fn rect_for_text_range(
325 &self,
326 range_utf16: Range<usize>,
327 view: &V,
328 cx: &ViewContext<V>,
329 ) -> Option<RectF> {
330 if let Lifecycle::PostPaint {
331 element,
332 bounds,
333 visible_bounds,
334 layout,
335 paint,
336 ..
337 } = self
338 {
339 element.rect_for_text_range(
340 range_utf16,
341 *bounds,
342 *visible_bounds,
343 layout,
344 paint,
345 view,
346 cx,
347 )
348 } else {
349 None
350 }
351 }
352
353 fn size(&self) -> Vector2F {
354 match self {
355 Lifecycle::Empty | Lifecycle::Init { .. } => panic!("invalid element lifecycle state"),
356 Lifecycle::PostLayout { size, .. } => *size,
357 Lifecycle::PostPaint { bounds, .. } => bounds.size(),
358 }
359 }
360
361 fn metadata(&self) -> Option<&dyn Any> {
362 match self {
363 Lifecycle::Empty => unreachable!(),
364 Lifecycle::Init { element }
365 | Lifecycle::PostLayout { element, .. }
366 | Lifecycle::PostPaint { element, .. } => element.metadata(),
367 }
368 }
369
370 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
371 match self {
372 Lifecycle::PostPaint {
373 element,
374 constraint,
375 bounds,
376 visible_bounds,
377 layout,
378 paint,
379 } => {
380 let mut value = element.debug(*bounds, layout, paint, view, cx);
381 if let json::Value::Object(map) = &mut value {
382 let mut new_map: crate::json::Map<String, serde_json::Value> =
383 Default::default();
384 if let Some(typ) = map.remove("type") {
385 new_map.insert("type".into(), typ);
386 }
387 new_map.insert("constraint".into(), constraint.to_json());
388 new_map.insert("bounds".into(), bounds.to_json());
389 new_map.insert("visible_bounds".into(), visible_bounds.to_json());
390 new_map.append(map);
391 json::Value::Object(new_map)
392 } else {
393 value
394 }
395 }
396
397 _ => panic!("invalid element lifecycle state"),
398 }
399 }
400}
401
402impl<V: View, E: Element<V>> Default for Lifecycle<V, E> {
403 fn default() -> Self {
404 Self::Empty
405 }
406}
407
408pub struct ElementBox<V: View> {
409 element: Box<dyn AnyElement<V>>,
410 view_type: PhantomData<V>,
411 name: Option<Cow<'static, str>>,
412}
413
414impl<V: View> ElementBox<V> {
415 pub fn name(&self) -> Option<&str> {
416 self.name.as_deref()
417 }
418
419 pub fn metadata<T: 'static>(&self) -> Option<&T> {
420 self.element
421 .metadata()
422 .and_then(|data| data.downcast_ref::<T>())
423 }
424
425 pub fn layout(
426 &mut self,
427 constraint: SizeConstraint,
428 view: &mut V,
429 cx: &mut ViewContext<V>,
430 ) -> Vector2F {
431 self.element.layout(constraint, view, cx)
432 }
433
434 pub fn paint(
435 &mut self,
436 scene: &mut SceneBuilder,
437 origin: Vector2F,
438 visible_bounds: RectF,
439 view: &mut V,
440 cx: &mut ViewContext<V>,
441 ) {
442 self.element.paint(scene, origin, visible_bounds, view, cx);
443 }
444
445 pub fn rect_for_text_range(
446 &self,
447 range_utf16: Range<usize>,
448 view: &V,
449 cx: &ViewContext<V>,
450 ) -> Option<RectF> {
451 self.element.rect_for_text_range(range_utf16, view, cx)
452 }
453
454 pub fn size(&self) -> Vector2F {
455 self.element.size()
456 }
457
458 pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
459 let mut value = self.element.debug(view, cx);
460
461 if let Some(name) = &self.name {
462 if let json::Value::Object(map) = &mut value {
463 let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
464 new_map.insert("name".into(), json::Value::String(name.to_string()));
465 new_map.append(map);
466 return json::Value::Object(new_map);
467 }
468 }
469
470 value
471 }
472
473 pub fn with_metadata<T, F, R>(&self, f: F) -> R
474 where
475 T: 'static,
476 F: FnOnce(Option<&T>) -> R,
477 {
478 f(self.element.metadata().and_then(|m| m.downcast_ref()))
479 }
480}
481
482pub trait ParentElement<'a, V: View>: Extend<ElementBox<V>> + Sized {
483 fn add_children(&mut self, children: impl IntoIterator<Item = ElementBox<V>>) {
484 self.extend(children);
485 }
486
487 fn add_child(&mut self, child: ElementBox<V>) {
488 self.add_children(Some(child));
489 }
490
491 fn with_children(mut self, children: impl IntoIterator<Item = ElementBox<V>>) -> Self {
492 self.add_children(children);
493 self
494 }
495
496 fn with_child(self, child: ElementBox<V>) -> Self {
497 self.with_children(Some(child))
498 }
499}
500
501impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<ElementBox<V>> {}
502
503pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
504 if max_size.x().is_infinite() && max_size.y().is_infinite() {
505 size
506 } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
507 vec2f(size.x() * max_size.y() / size.y(), max_size.y())
508 } else {
509 vec2f(max_size.x(), size.y() * max_size.x() / size.x())
510 }
511}