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