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, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext,
38 WeakViewHandle, 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 PaintContext<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(
303 scene,
304 bounds,
305 visible_bounds,
306 &mut layout,
307 view,
308 &mut PaintContext::new(cx),
309 );
310 ElementState::PostPaint {
311 element,
312 constraint,
313 bounds,
314 visible_bounds,
315 layout,
316 paint,
317 }
318 }
319 ElementState::PostPaint {
320 mut element,
321 constraint,
322 bounds,
323 mut layout,
324 ..
325 } => {
326 let bounds = RectF::new(origin, bounds.size());
327 let paint = element.paint(
328 scene,
329 bounds,
330 visible_bounds,
331 &mut layout,
332 view,
333 &mut PaintContext::new(cx),
334 );
335 ElementState::PostPaint {
336 element,
337 constraint,
338 bounds,
339 visible_bounds,
340 layout,
341 paint,
342 }
343 }
344 ElementState::Empty => panic!("invalid element lifecycle state"),
345 ElementState::Init { .. } => {
346 panic!("invalid element lifecycle state, paint called before layout")
347 }
348 }
349 }
350
351 fn rect_for_text_range(
352 &self,
353 range_utf16: Range<usize>,
354 view: &V,
355 cx: &ViewContext<V>,
356 ) -> Option<RectF> {
357 if let ElementState::PostPaint {
358 element,
359 bounds,
360 visible_bounds,
361 layout,
362 paint,
363 ..
364 } = self
365 {
366 element.rect_for_text_range(
367 range_utf16,
368 *bounds,
369 *visible_bounds,
370 layout,
371 paint,
372 view,
373 cx,
374 )
375 } else {
376 None
377 }
378 }
379
380 fn size(&self) -> Vector2F {
381 match self {
382 ElementState::Empty | ElementState::Init { .. } => {
383 panic!("invalid element lifecycle state")
384 }
385 ElementState::PostLayout { size, .. } => *size,
386 ElementState::PostPaint { bounds, .. } => bounds.size(),
387 }
388 }
389
390 fn metadata(&self) -> Option<&dyn Any> {
391 match self {
392 ElementState::Empty => unreachable!(),
393 ElementState::Init { element }
394 | ElementState::PostLayout { element, .. }
395 | ElementState::PostPaint { element, .. } => element.metadata(),
396 }
397 }
398
399 fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
400 match self {
401 ElementState::PostPaint {
402 element,
403 constraint,
404 bounds,
405 visible_bounds,
406 layout,
407 paint,
408 } => {
409 let mut value = element.debug(*bounds, layout, paint, view, cx);
410 if let json::Value::Object(map) = &mut value {
411 let mut new_map: crate::json::Map<String, serde_json::Value> =
412 Default::default();
413 if let Some(typ) = map.remove("type") {
414 new_map.insert("type".into(), typ);
415 }
416 new_map.insert("constraint".into(), constraint.to_json());
417 new_map.insert("bounds".into(), bounds.to_json());
418 new_map.insert("visible_bounds".into(), visible_bounds.to_json());
419 new_map.append(map);
420 json::Value::Object(new_map)
421 } else {
422 value
423 }
424 }
425
426 _ => panic!("invalid element lifecycle state"),
427 }
428 }
429}
430
431impl<V: View, E: Element<V>> Default for ElementState<V, E> {
432 fn default() -> Self {
433 Self::Empty
434 }
435}
436
437pub struct AnyElement<V: View> {
438 state: Box<dyn AnyElementState<V>>,
439 name: Option<Cow<'static, str>>,
440}
441
442impl<V: View> AnyElement<V> {
443 pub fn name(&self) -> Option<&str> {
444 self.name.as_deref()
445 }
446
447 pub fn metadata<T: 'static>(&self) -> Option<&T> {
448 self.state
449 .metadata()
450 .and_then(|data| data.downcast_ref::<T>())
451 }
452
453 pub fn layout(
454 &mut self,
455 constraint: SizeConstraint,
456 view: &mut V,
457 cx: &mut LayoutContext<V>,
458 ) -> Vector2F {
459 self.state.layout(constraint, view, cx)
460 }
461
462 pub fn paint(
463 &mut self,
464 scene: &mut SceneBuilder,
465 origin: Vector2F,
466 visible_bounds: RectF,
467 view: &mut V,
468 cx: &mut ViewContext<V>,
469 ) {
470 self.state.paint(scene, origin, visible_bounds, view, cx);
471 }
472
473 pub fn rect_for_text_range(
474 &self,
475 range_utf16: Range<usize>,
476 view: &V,
477 cx: &ViewContext<V>,
478 ) -> Option<RectF> {
479 self.state.rect_for_text_range(range_utf16, view, cx)
480 }
481
482 pub fn size(&self) -> Vector2F {
483 self.state.size()
484 }
485
486 pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
487 let mut value = self.state.debug(view, cx);
488
489 if let Some(name) = &self.name {
490 if let json::Value::Object(map) = &mut value {
491 let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
492 new_map.insert("name".into(), json::Value::String(name.to_string()));
493 new_map.append(map);
494 return json::Value::Object(new_map);
495 }
496 }
497
498 value
499 }
500
501 pub fn with_metadata<T, F, R>(&self, f: F) -> R
502 where
503 T: 'static,
504 F: FnOnce(Option<&T>) -> R,
505 {
506 f(self.state.metadata().and_then(|m| m.downcast_ref()))
507 }
508}
509
510impl<V: View> Element<V> for AnyElement<V> {
511 type LayoutState = ();
512 type PaintState = ();
513
514 fn layout(
515 &mut self,
516 constraint: SizeConstraint,
517 view: &mut V,
518 cx: &mut LayoutContext<V>,
519 ) -> (Vector2F, Self::LayoutState) {
520 let size = self.layout(constraint, view, cx);
521 (size, ())
522 }
523
524 fn paint(
525 &mut self,
526 scene: &mut SceneBuilder,
527 bounds: RectF,
528 visible_bounds: RectF,
529 _: &mut Self::LayoutState,
530 view: &mut V,
531 cx: &mut PaintContext<V>,
532 ) -> Self::PaintState {
533 self.paint(scene, bounds.origin(), visible_bounds, view, cx);
534 }
535
536 fn rect_for_text_range(
537 &self,
538 range_utf16: Range<usize>,
539 _: RectF,
540 _: RectF,
541 _: &Self::LayoutState,
542 _: &Self::PaintState,
543 view: &V,
544 cx: &ViewContext<V>,
545 ) -> Option<RectF> {
546 self.rect_for_text_range(range_utf16, view, cx)
547 }
548
549 fn debug(
550 &self,
551 _: RectF,
552 _: &Self::LayoutState,
553 _: &Self::PaintState,
554 view: &V,
555 cx: &ViewContext<V>,
556 ) -> serde_json::Value {
557 self.debug(view, cx)
558 }
559
560 fn into_any(self) -> AnyElement<V>
561 where
562 Self: Sized,
563 {
564 self
565 }
566}
567
568pub struct RootElement<V: View> {
569 element: AnyElement<V>,
570 view: WeakViewHandle<V>,
571}
572
573impl<V: View> RootElement<V> {
574 pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
575 Self { element, view }
576 }
577}
578
579pub trait AnyRootElement {
580 fn layout(
581 &mut self,
582 constraint: SizeConstraint,
583 new_parents: &mut HashMap<usize, usize>,
584 views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
585 refreshing: bool,
586 cx: &mut WindowContext,
587 ) -> Result<Vector2F>;
588 fn paint(
589 &mut self,
590 scene: &mut SceneBuilder,
591 origin: Vector2F,
592 visible_bounds: RectF,
593 cx: &mut WindowContext,
594 ) -> Result<()>;
595 fn rect_for_text_range(
596 &self,
597 range_utf16: Range<usize>,
598 cx: &WindowContext,
599 ) -> Result<Option<RectF>>;
600 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
601 fn name(&self) -> Option<&str>;
602}
603
604impl<V: View> AnyRootElement for RootElement<V> {
605 fn layout(
606 &mut self,
607 constraint: SizeConstraint,
608 new_parents: &mut HashMap<usize, usize>,
609 views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
610 refreshing: bool,
611 cx: &mut WindowContext,
612 ) -> Result<Vector2F> {
613 let view = self
614 .view
615 .upgrade(cx)
616 .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
617 view.update(cx, |view, cx| {
618 let mut cx = LayoutContext::new(
619 cx,
620 new_parents,
621 views_to_notify_if_ancestors_change,
622 refreshing,
623 );
624 Ok(self.element.layout(constraint, view, &mut cx))
625 })
626 }
627
628 fn paint(
629 &mut self,
630 scene: &mut SceneBuilder,
631 origin: Vector2F,
632 visible_bounds: RectF,
633 cx: &mut WindowContext,
634 ) -> Result<()> {
635 let view = self
636 .view
637 .upgrade(cx)
638 .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
639
640 view.update(cx, |view, cx| {
641 self.element.paint(scene, origin, visible_bounds, view, cx);
642 Ok(())
643 })
644 }
645
646 fn rect_for_text_range(
647 &self,
648 range_utf16: Range<usize>,
649 cx: &WindowContext,
650 ) -> Result<Option<RectF>> {
651 let view = self.view.upgrade(cx).ok_or_else(|| {
652 anyhow!("rect_for_text_range called on a root element for a dropped view")
653 })?;
654 let view = view.read(cx);
655 let view_context = ViewContext::immutable(cx, self.view.id());
656 Ok(self
657 .element
658 .rect_for_text_range(range_utf16, view, &view_context))
659 }
660
661 fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
662 let view = self
663 .view
664 .upgrade(cx)
665 .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
666 let view = view.read(cx);
667 let view_context = ViewContext::immutable(cx, self.view.id());
668 Ok(serde_json::json!({
669 "view_id": self.view.id(),
670 "view_name": V::ui_name(),
671 "view": view.debug_json(cx),
672 "element": self.element.debug(view, &view_context)
673 }))
674 }
675
676 fn name(&self) -> Option<&str> {
677 self.element.name()
678 }
679}
680
681pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
682 fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
683 self.extend(children.into_iter().map(|child| child.into_any()));
684 }
685
686 fn add_child<D: Element<V>>(&mut self, child: D) {
687 self.extend(Some(child.into_any()));
688 }
689
690 fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
691 self.extend(children.into_iter().map(|child| child.into_any()));
692 self
693 }
694
695 fn with_child<D: Element<V>>(mut self, child: D) -> Self {
696 self.extend(Some(child.into_any()));
697 self
698 }
699}
700
701impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
702
703pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
704 if max_size.x().is_infinite() && max_size.y().is_infinite() {
705 size
706 } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
707 vec2f(size.x() * max_size.y() / size.y(), max_size.y())
708 } else {
709 vec2f(max_size.x(), size.y() * max_size.x() / size.x())
710 }
711}