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;
15pub mod node;
16mod overlay;
17mod resizable;
18mod stack;
19mod svg;
20mod text;
21mod tooltip;
22mod uniform_list;
23
24pub use self::{
25 align::*, canvas::*, constrained_box::*, container::*, empty::*, flex::*, hook::*, image::*,
26 keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*, resizable::*,
27 stack::*, svg::*, text::*, tooltip::*, uniform_list::*,
28};
29pub use crate::window::ChildView;
30
31use self::{clipped::Clipped, expanded::Expanded};
32use crate::{
33 geometry::{
34 rect::RectF,
35 vector::{vec2f, Vector2F},
36 },
37 json, Action, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, WeakViewHandle,
38 WindowContext,
39};
40use anyhow::{anyhow, Result};
41use collections::HashMap;
42use core::panic;
43use json::ToJson;
44use smallvec::SmallVec;
45use std::{any::Any, borrow::Cow, mem, ops::Range};
46
47pub trait Element<V: View>: 'static {
48 type LayoutState;
49 type PaintState;
50
51 fn layout(
52 &mut self,
53 constraint: SizeConstraint,
54 view: &mut V,
55 cx: &mut LayoutContext<V>,
56 ) -> (Vector2F, Self::LayoutState);
57
58 fn paint(
59 &mut self,
60 scene: &mut SceneBuilder,
61 bounds: RectF,
62 visible_bounds: RectF,
63 layout: &mut Self::LayoutState,
64 view: &mut V,
65 cx: &mut ViewContext<V>,
66 ) -> Self::PaintState;
67
68 fn rect_for_text_range(
69 &self,
70 range_utf16: Range<usize>,
71 bounds: RectF,
72 visible_bounds: RectF,
73 layout: &Self::LayoutState,
74 paint: &Self::PaintState,
75 view: &V,
76 cx: &ViewContext<V>,
77 ) -> Option<RectF>;
78
79 fn metadata(&self) -> Option<&dyn Any> {
80 None
81 }
82
83 fn debug(
84 &self,
85 bounds: RectF,
86 layout: &Self::LayoutState,
87 paint: &Self::PaintState,
88 view: &V,
89 cx: &ViewContext<V>,
90 ) -> serde_json::Value;
91
92 fn into_any(self) -> AnyElement<V>
93 where
94 Self: 'static + Sized,
95 {
96 AnyElement {
97 state: Box::new(ElementState::Init { element: self }),
98 name: None,
99 }
100 }
101
102 fn into_any_named(self, name: impl Into<Cow<'static, str>>) -> AnyElement<V>
103 where
104 Self: 'static + Sized,
105 {
106 AnyElement {
107 state: Box::new(ElementState::Init { element: self }),
108 name: Some(name.into()),
109 }
110 }
111
112 fn into_root_element(self, cx: &ViewContext<V>) -> RootElement<V>
113 where
114 Self: 'static + Sized,
115 {
116 RootElement {
117 element: self.into_any(),
118 view: cx.handle().downgrade(),
119 }
120 }
121
122 fn constrained(self) -> ConstrainedBox<V>
123 where
124 Self: 'static + Sized,
125 {
126 ConstrainedBox::new(self.into_any())
127 }
128
129 fn aligned(self) -> Align<V>
130 where
131 Self: 'static + Sized,
132 {
133 Align::new(self.into_any())
134 }
135
136 fn clipped(self) -> Clipped<V>
137 where
138 Self: 'static + Sized,
139 {
140 Clipped::new(self.into_any())
141 }
142
143 fn contained(self) -> Container<V>
144 where
145 Self: 'static + Sized,
146 {
147 Container::new(self.into_any())
148 }
149
150 fn expanded(self) -> Expanded<V>
151 where
152 Self: 'static + Sized,
153 {
154 Expanded::new(self.into_any())
155 }
156
157 fn flex(self, flex: f32, expanded: bool) -> FlexItem<V>
158 where
159 Self: 'static + Sized,
160 {
161 FlexItem::new(self.into_any()).flex(flex, expanded)
162 }
163
164 fn flex_float(self) -> FlexItem<V>
165 where
166 Self: 'static + Sized,
167 {
168 FlexItem::new(self.into_any()).float()
169 }
170
171 fn with_tooltip<Tag: 'static>(
172 self,
173 id: usize,
174 text: String,
175 action: Option<Box<dyn Action>>,
176 style: TooltipStyle,
177 cx: &mut ViewContext<V>,
178 ) -> Tooltip<V>
179 where
180 Self: 'static + Sized,
181 {
182 Tooltip::new::<Tag, V>(id, text, action, style, self.into_any(), cx)
183 }
184
185 fn resizable(
186 self,
187 side: HandleSide,
188 size: f32,
189 on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext<V>),
190 ) -> Resizable<V>
191 where
192 Self: 'static + Sized,
193 {
194 Resizable::new(self.into_any(), side, size, on_resize)
195 }
196
197 fn mouse<Tag>(self, region_id: usize) -> MouseEventHandler<Tag, V>
198 where
199 Self: Sized,
200 {
201 MouseEventHandler::for_child(self.into_any(), region_id)
202 }
203}
204
205trait AnyElementState<V: View> {
206 fn layout(
207 &mut self,
208 constraint: SizeConstraint,
209 view: &mut V,
210 cx: &mut LayoutContext<V>,
211 ) -> Vector2F;
212
213 fn paint(
214 &mut self,
215 scene: &mut SceneBuilder,
216 origin: Vector2F,
217 visible_bounds: RectF,
218 view: &mut V,
219 cx: &mut ViewContext<V>,
220 );
221
222 fn rect_for_text_range(
223 &self,
224 range_utf16: Range<usize>,
225 view: &V,
226 cx: &ViewContext<V>,
227 ) -> Option<RectF>;
228
229 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value;
230
231 fn size(&self) -> Vector2F;
232
233 fn metadata(&self) -> Option<&dyn Any>;
234}
235
236enum ElementState<V: View, E: Element<V>> {
237 Empty,
238 Init {
239 element: E,
240 },
241 PostLayout {
242 element: E,
243 constraint: SizeConstraint,
244 size: Vector2F,
245 layout: E::LayoutState,
246 },
247 PostPaint {
248 element: E,
249 constraint: SizeConstraint,
250 bounds: RectF,
251 visible_bounds: RectF,
252 layout: E::LayoutState,
253 paint: E::PaintState,
254 },
255}
256
257impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
258 fn layout(
259 &mut self,
260 constraint: SizeConstraint,
261 view: &mut V,
262 cx: &mut LayoutContext<V>,
263 ) -> Vector2F {
264 let result;
265 *self = match mem::take(self) {
266 ElementState::Empty => unreachable!(),
267 ElementState::Init { mut element }
268 | ElementState::PostLayout { mut element, .. }
269 | ElementState::PostPaint { mut element, .. } => {
270 let (size, layout) = element.layout(constraint, view, cx);
271 debug_assert!(size.x().is_finite());
272 debug_assert!(size.y().is_finite());
273
274 result = size;
275 ElementState::PostLayout {
276 element,
277 constraint,
278 size,
279 layout,
280 }
281 }
282 };
283 result
284 }
285
286 fn paint(
287 &mut self,
288 scene: &mut SceneBuilder,
289 origin: Vector2F,
290 visible_bounds: RectF,
291 view: &mut V,
292 cx: &mut ViewContext<V>,
293 ) {
294 *self = match mem::take(self) {
295 ElementState::PostLayout {
296 mut element,
297 constraint,
298 size,
299 mut layout,
300 } => {
301 let bounds = RectF::new(origin, size);
302 let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
303 ElementState::PostPaint {
304 element,
305 constraint,
306 bounds,
307 visible_bounds,
308 layout,
309 paint,
310 }
311 }
312 ElementState::PostPaint {
313 mut element,
314 constraint,
315 bounds,
316 mut layout,
317 ..
318 } => {
319 let bounds = RectF::new(origin, bounds.size());
320 let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
321 ElementState::PostPaint {
322 element,
323 constraint,
324 bounds,
325 visible_bounds,
326 layout,
327 paint,
328 }
329 }
330 ElementState::Empty => panic!("invalid element lifecycle state"),
331 ElementState::Init { .. } => {
332 panic!("invalid element lifecycle state, paint called before layout")
333 }
334 }
335 }
336
337 fn rect_for_text_range(
338 &self,
339 range_utf16: Range<usize>,
340 view: &V,
341 cx: &ViewContext<V>,
342 ) -> Option<RectF> {
343 if let ElementState::PostPaint {
344 element,
345 bounds,
346 visible_bounds,
347 layout,
348 paint,
349 ..
350 } = self
351 {
352 element.rect_for_text_range(
353 range_utf16,
354 *bounds,
355 *visible_bounds,
356 layout,
357 paint,
358 view,
359 cx,
360 )
361 } else {
362 None
363 }
364 }
365
366 fn size(&self) -> Vector2F {
367 match self {
368 ElementState::Empty | ElementState::Init { .. } => {
369 panic!("invalid element lifecycle state")
370 }
371 ElementState::PostLayout { size, .. } => *size,
372 ElementState::PostPaint { bounds, .. } => bounds.size(),
373 }
374 }
375
376 fn metadata(&self) -> Option<&dyn Any> {
377 match self {
378 ElementState::Empty => unreachable!(),
379 ElementState::Init { element }
380 | ElementState::PostLayout { element, .. }
381 | ElementState::PostPaint { element, .. } => element.metadata(),
382 }
383 }
384
385 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
386 match self {
387 ElementState::PostPaint {
388 element,
389 constraint,
390 bounds,
391 visible_bounds,
392 layout,
393 paint,
394 } => {
395 let mut value = element.debug(*bounds, layout, paint, view, cx);
396 if let json::Value::Object(map) = &mut value {
397 let mut new_map: crate::json::Map<String, serde_json::Value> =
398 Default::default();
399 if let Some(typ) = map.remove("type") {
400 new_map.insert("type".into(), typ);
401 }
402 new_map.insert("constraint".into(), constraint.to_json());
403 new_map.insert("bounds".into(), bounds.to_json());
404 new_map.insert("visible_bounds".into(), visible_bounds.to_json());
405 new_map.append(map);
406 json::Value::Object(new_map)
407 } else {
408 value
409 }
410 }
411
412 _ => panic!("invalid element lifecycle state"),
413 }
414 }
415}
416
417impl<V: View, E: Element<V>> Default for ElementState<V, E> {
418 fn default() -> Self {
419 Self::Empty
420 }
421}
422
423pub struct AnyElement<V: View> {
424 state: Box<dyn AnyElementState<V>>,
425 name: Option<Cow<'static, str>>,
426}
427
428impl<V: View> AnyElement<V> {
429 pub fn name(&self) -> Option<&str> {
430 self.name.as_deref()
431 }
432
433 pub fn metadata<T: 'static>(&self) -> Option<&T> {
434 self.state
435 .metadata()
436 .and_then(|data| data.downcast_ref::<T>())
437 }
438
439 pub fn layout(
440 &mut self,
441 constraint: SizeConstraint,
442 view: &mut V,
443 cx: &mut LayoutContext<V>,
444 ) -> Vector2F {
445 self.state.layout(constraint, view, cx)
446 }
447
448 pub fn paint(
449 &mut self,
450 scene: &mut SceneBuilder,
451 origin: Vector2F,
452 visible_bounds: RectF,
453 view: &mut V,
454 cx: &mut ViewContext<V>,
455 ) {
456 self.state.paint(scene, origin, visible_bounds, view, cx);
457 }
458
459 pub fn rect_for_text_range(
460 &self,
461 range_utf16: Range<usize>,
462 view: &V,
463 cx: &ViewContext<V>,
464 ) -> Option<RectF> {
465 self.state.rect_for_text_range(range_utf16, view, cx)
466 }
467
468 pub fn size(&self) -> Vector2F {
469 self.state.size()
470 }
471
472 pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
473 let mut value = self.state.debug(view, cx);
474
475 if let Some(name) = &self.name {
476 if let json::Value::Object(map) = &mut value {
477 let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
478 new_map.insert("name".into(), json::Value::String(name.to_string()));
479 new_map.append(map);
480 return json::Value::Object(new_map);
481 }
482 }
483
484 value
485 }
486
487 pub fn with_metadata<T, F, R>(&self, f: F) -> R
488 where
489 T: 'static,
490 F: FnOnce(Option<&T>) -> R,
491 {
492 f(self.state.metadata().and_then(|m| m.downcast_ref()))
493 }
494}
495
496impl<V: View> Element<V> for AnyElement<V> {
497 type LayoutState = ();
498 type PaintState = ();
499
500 fn layout(
501 &mut self,
502 constraint: SizeConstraint,
503 view: &mut V,
504 cx: &mut LayoutContext<V>,
505 ) -> (Vector2F, Self::LayoutState) {
506 let size = self.layout(constraint, view, cx);
507 (size, ())
508 }
509
510 fn paint(
511 &mut self,
512 scene: &mut SceneBuilder,
513 bounds: RectF,
514 visible_bounds: RectF,
515 _: &mut Self::LayoutState,
516 view: &mut V,
517 cx: &mut ViewContext<V>,
518 ) -> Self::PaintState {
519 self.paint(scene, bounds.origin(), visible_bounds, view, cx);
520 }
521
522 fn rect_for_text_range(
523 &self,
524 range_utf16: Range<usize>,
525 _: RectF,
526 _: RectF,
527 _: &Self::LayoutState,
528 _: &Self::PaintState,
529 view: &V,
530 cx: &ViewContext<V>,
531 ) -> Option<RectF> {
532 self.rect_for_text_range(range_utf16, view, cx)
533 }
534
535 fn debug(
536 &self,
537 _: RectF,
538 _: &Self::LayoutState,
539 _: &Self::PaintState,
540 view: &V,
541 cx: &ViewContext<V>,
542 ) -> serde_json::Value {
543 self.debug(view, cx)
544 }
545
546 fn into_any(self) -> AnyElement<V>
547 where
548 Self: Sized,
549 {
550 self
551 }
552}
553
554pub struct RootElement<V: View> {
555 element: AnyElement<V>,
556 view: WeakViewHandle<V>,
557}
558
559impl<V: View> RootElement<V> {
560 pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
561 Self { element, view }
562 }
563}
564
565pub trait AnyRootElement {
566 fn layout(
567 &mut self,
568 constraint: SizeConstraint,
569 new_parents: &mut HashMap<usize, usize>,
570 views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
571 refreshing: bool,
572 cx: &mut WindowContext,
573 ) -> Result<Vector2F>;
574 fn paint(
575 &mut self,
576 scene: &mut SceneBuilder,
577 origin: Vector2F,
578 visible_bounds: RectF,
579 cx: &mut WindowContext,
580 ) -> Result<()>;
581 fn rect_for_text_range(
582 &self,
583 range_utf16: Range<usize>,
584 cx: &WindowContext,
585 ) -> Result<Option<RectF>>;
586 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
587 fn name(&self) -> Option<&str>;
588}
589
590impl<V: View> AnyRootElement for RootElement<V> {
591 fn layout(
592 &mut self,
593 constraint: SizeConstraint,
594 new_parents: &mut HashMap<usize, usize>,
595 views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
596 refreshing: bool,
597 cx: &mut WindowContext,
598 ) -> Result<Vector2F> {
599 let view = self
600 .view
601 .upgrade(cx)
602 .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
603 view.update(cx, |view, cx| {
604 let mut cx = LayoutContext::new(
605 cx,
606 new_parents,
607 views_to_notify_if_ancestors_change,
608 refreshing,
609 );
610 Ok(self.element.layout(constraint, view, &mut cx))
611 })
612 }
613
614 fn paint(
615 &mut self,
616 scene: &mut SceneBuilder,
617 origin: Vector2F,
618 visible_bounds: RectF,
619 cx: &mut WindowContext,
620 ) -> Result<()> {
621 let view = self
622 .view
623 .upgrade(cx)
624 .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
625
626 view.update(cx, |view, cx| {
627 self.element.paint(scene, origin, visible_bounds, view, cx);
628 Ok(())
629 })
630 }
631
632 fn rect_for_text_range(
633 &self,
634 range_utf16: Range<usize>,
635 cx: &WindowContext,
636 ) -> Result<Option<RectF>> {
637 let view = self.view.upgrade(cx).ok_or_else(|| {
638 anyhow!("rect_for_text_range called on a root element for a dropped view")
639 })?;
640 let view = view.read(cx);
641 let view_context = ViewContext::immutable(cx, self.view.id());
642 Ok(self
643 .element
644 .rect_for_text_range(range_utf16, view, &view_context))
645 }
646
647 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
648 let view = self
649 .view
650 .upgrade(cx)
651 .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
652 let view = view.read(cx);
653 let view_context = ViewContext::immutable(cx, self.view.id());
654 Ok(serde_json::json!({
655 "view_id": self.view.id(),
656 "view_name": V::ui_name(),
657 "view": view.debug_json(cx),
658 "element": self.element.debug(view, &view_context)
659 }))
660 }
661
662 fn name(&self) -> Option<&str> {
663 self.element.name()
664 }
665}
666
667pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
668 fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
669 self.extend(children.into_iter().map(|child| child.into_any()));
670 }
671
672 fn add_child<D: Element<V>>(&mut self, child: D) {
673 self.extend(Some(child.into_any()));
674 }
675
676 fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
677 self.extend(children.into_iter().map(|child| child.into_any()));
678 self
679 }
680
681 fn with_child<D: Element<V>>(mut self, child: D) -> Self {
682 self.extend(Some(child.into_any()));
683 self
684 }
685}
686
687impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
688
689pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
690 if max_size.x().is_infinite() && max_size.y().is_infinite() {
691 size
692 } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
693 vec2f(size.x() * max_size.y() / size.y(), max_size.y())
694 } else {
695 vec2f(max_size.x(), size.y() * max_size.x() / size.x())
696 }
697}