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