elements.rs

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