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