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