elements.rs

  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, SceneBuilder, SizeConstraint, View, ViewContext, WeakViewHandle, WindowContext,
 37};
 38use anyhow::{anyhow, Result};
 39use core::panic;
 40use json::ToJson;
 41use std::{
 42    any::Any,
 43    borrow::Cow,
 44    marker::PhantomData,
 45    mem,
 46    ops::{Deref, DerefMut, Range},
 47};
 48use util::ResultExt;
 49
 50pub trait Element<V: View>: 'static {
 51    type LayoutState;
 52    type PaintState;
 53
 54    fn layout(
 55        &mut self,
 56        constraint: SizeConstraint,
 57        view: &mut V,
 58        cx: &mut ViewContext<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 with_resize_handle<Tag: 'static>(
189        self,
190        element_id: usize,
191        side: Side,
192        handle_size: f32,
193        initial_size: f32,
194        cx: &mut ViewContext<V>,
195    ) -> Resizable<V>
196    where
197        Self: 'static + Sized,
198    {
199        Resizable::new::<Tag, V>(
200            self.into_any(),
201            element_id,
202            side,
203            handle_size,
204            initial_size,
205            cx,
206        )
207    }
208}
209
210trait AnyElementState<V: View> {
211    fn layout(
212        &mut self,
213        constraint: SizeConstraint,
214        view: &mut V,
215        cx: &mut ViewContext<V>,
216    ) -> Vector2F;
217
218    fn paint(
219        &mut self,
220        scene: &mut SceneBuilder,
221        origin: Vector2F,
222        visible_bounds: RectF,
223        view: &mut V,
224        cx: &mut ViewContext<V>,
225    );
226
227    fn rect_for_text_range(
228        &self,
229        range_utf16: Range<usize>,
230        view: &V,
231        cx: &ViewContext<V>,
232    ) -> Option<RectF>;
233
234    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value;
235
236    fn size(&self) -> Vector2F;
237
238    fn metadata(&self) -> Option<&dyn Any>;
239}
240
241enum ElementState<V: View, E: Element<V>> {
242    Empty,
243    Init {
244        element: E,
245    },
246    PostLayout {
247        element: E,
248        constraint: SizeConstraint,
249        size: Vector2F,
250        layout: E::LayoutState,
251    },
252    PostPaint {
253        element: E,
254        constraint: SizeConstraint,
255        bounds: RectF,
256        visible_bounds: RectF,
257        layout: E::LayoutState,
258        paint: E::PaintState,
259    },
260}
261
262impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
263    fn layout(
264        &mut self,
265        constraint: SizeConstraint,
266        view: &mut V,
267        cx: &mut ViewContext<V>,
268    ) -> Vector2F {
269        let result;
270        *self = match mem::take(self) {
271            ElementState::Empty => unreachable!(),
272            ElementState::Init { mut element }
273            | ElementState::PostLayout { mut element, .. }
274            | ElementState::PostPaint { mut element, .. } => {
275                let (size, layout) = element.layout(constraint, view, cx);
276                debug_assert!(size.x().is_finite());
277                debug_assert!(size.y().is_finite());
278
279                result = size;
280                ElementState::PostLayout {
281                    element,
282                    constraint,
283                    size,
284                    layout,
285                }
286            }
287        };
288        result
289    }
290
291    fn paint(
292        &mut self,
293        scene: &mut SceneBuilder,
294        origin: Vector2F,
295        visible_bounds: RectF,
296        view: &mut V,
297        cx: &mut ViewContext<V>,
298    ) {
299        *self = match mem::take(self) {
300            ElementState::PostLayout {
301                mut element,
302                constraint,
303                size,
304                mut layout,
305            } => {
306                let bounds = RectF::new(origin, size);
307                let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
308                ElementState::PostPaint {
309                    element,
310                    constraint,
311                    bounds,
312                    visible_bounds,
313                    layout,
314                    paint,
315                }
316            }
317            ElementState::PostPaint {
318                mut element,
319                constraint,
320                bounds,
321                mut layout,
322                ..
323            } => {
324                let bounds = RectF::new(origin, bounds.size());
325                let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
326                ElementState::PostPaint {
327                    element,
328                    constraint,
329                    bounds,
330                    visible_bounds,
331                    layout,
332                    paint,
333                }
334            }
335            ElementState::Empty => panic!("invalid element lifecycle state"),
336            ElementState::Init { .. } => {
337                panic!("invalid element lifecycle state, paint called before layout")
338            }
339        }
340    }
341
342    fn rect_for_text_range(
343        &self,
344        range_utf16: Range<usize>,
345        view: &V,
346        cx: &ViewContext<V>,
347    ) -> Option<RectF> {
348        if let ElementState::PostPaint {
349            element,
350            bounds,
351            visible_bounds,
352            layout,
353            paint,
354            ..
355        } = self
356        {
357            element.rect_for_text_range(
358                range_utf16,
359                *bounds,
360                *visible_bounds,
361                layout,
362                paint,
363                view,
364                cx,
365            )
366        } else {
367            None
368        }
369    }
370
371    fn size(&self) -> Vector2F {
372        match self {
373            ElementState::Empty | ElementState::Init { .. } => {
374                panic!("invalid element lifecycle state")
375            }
376            ElementState::PostLayout { size, .. } => *size,
377            ElementState::PostPaint { bounds, .. } => bounds.size(),
378        }
379    }
380
381    fn metadata(&self) -> Option<&dyn Any> {
382        match self {
383            ElementState::Empty => unreachable!(),
384            ElementState::Init { element }
385            | ElementState::PostLayout { element, .. }
386            | ElementState::PostPaint { element, .. } => element.metadata(),
387        }
388    }
389
390    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
391        match self {
392            ElementState::PostPaint {
393                element,
394                constraint,
395                bounds,
396                visible_bounds,
397                layout,
398                paint,
399            } => {
400                let mut value = element.debug(*bounds, layout, paint, view, cx);
401                if let json::Value::Object(map) = &mut value {
402                    let mut new_map: crate::json::Map<String, serde_json::Value> =
403                        Default::default();
404                    if let Some(typ) = map.remove("type") {
405                        new_map.insert("type".into(), typ);
406                    }
407                    new_map.insert("constraint".into(), constraint.to_json());
408                    new_map.insert("bounds".into(), bounds.to_json());
409                    new_map.insert("visible_bounds".into(), visible_bounds.to_json());
410                    new_map.append(map);
411                    json::Value::Object(new_map)
412                } else {
413                    value
414                }
415            }
416
417            _ => panic!("invalid element lifecycle state"),
418        }
419    }
420}
421
422impl<V: View, E: Element<V>> Default for ElementState<V, E> {
423    fn default() -> Self {
424        Self::Empty
425    }
426}
427
428pub struct AnyElement<V: View> {
429    state: Box<dyn AnyElementState<V>>,
430    name: Option<Cow<'static, str>>,
431}
432
433impl<V: View> AnyElement<V> {
434    pub fn name(&self) -> Option<&str> {
435        self.name.as_deref()
436    }
437
438    pub fn metadata<T: 'static>(&self) -> Option<&T> {
439        self.state
440            .metadata()
441            .and_then(|data| data.downcast_ref::<T>())
442    }
443
444    pub fn layout(
445        &mut self,
446        constraint: SizeConstraint,
447        view: &mut V,
448        cx: &mut ViewContext<V>,
449    ) -> Vector2F {
450        self.state.layout(constraint, view, cx)
451    }
452
453    pub fn paint(
454        &mut self,
455        scene: &mut SceneBuilder,
456        origin: Vector2F,
457        visible_bounds: RectF,
458        view: &mut V,
459        cx: &mut ViewContext<V>,
460    ) {
461        self.state.paint(scene, origin, visible_bounds, view, cx);
462    }
463
464    pub fn rect_for_text_range(
465        &self,
466        range_utf16: Range<usize>,
467        view: &V,
468        cx: &ViewContext<V>,
469    ) -> Option<RectF> {
470        self.state.rect_for_text_range(range_utf16, view, cx)
471    }
472
473    pub fn size(&self) -> Vector2F {
474        self.state.size()
475    }
476
477    pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
478        let mut value = self.state.debug(view, cx);
479
480        if let Some(name) = &self.name {
481            if let json::Value::Object(map) = &mut value {
482                let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
483                new_map.insert("name".into(), json::Value::String(name.to_string()));
484                new_map.append(map);
485                return json::Value::Object(new_map);
486            }
487        }
488
489        value
490    }
491
492    pub fn with_metadata<T, F, R>(&self, f: F) -> R
493    where
494        T: 'static,
495        F: FnOnce(Option<&T>) -> R,
496    {
497        f(self.state.metadata().and_then(|m| m.downcast_ref()))
498    }
499}
500
501impl<V: View> Element<V> for AnyElement<V> {
502    type LayoutState = ();
503    type PaintState = ();
504
505    fn layout(
506        &mut self,
507        constraint: SizeConstraint,
508        view: &mut V,
509        cx: &mut ViewContext<V>,
510    ) -> (Vector2F, Self::LayoutState) {
511        let size = self.layout(constraint, view, cx);
512        (size, ())
513    }
514
515    fn paint(
516        &mut self,
517        scene: &mut SceneBuilder,
518        bounds: RectF,
519        visible_bounds: RectF,
520        _: &mut Self::LayoutState,
521        view: &mut V,
522        cx: &mut ViewContext<V>,
523    ) -> Self::PaintState {
524        self.paint(scene, bounds.origin(), visible_bounds, view, cx);
525    }
526
527    fn rect_for_text_range(
528        &self,
529        range_utf16: Range<usize>,
530        _: RectF,
531        _: RectF,
532        _: &Self::LayoutState,
533        _: &Self::PaintState,
534        view: &V,
535        cx: &ViewContext<V>,
536    ) -> Option<RectF> {
537        self.rect_for_text_range(range_utf16, view, cx)
538    }
539
540    fn debug(
541        &self,
542        _: RectF,
543        _: &Self::LayoutState,
544        _: &Self::PaintState,
545        view: &V,
546        cx: &ViewContext<V>,
547    ) -> serde_json::Value {
548        self.debug(view, cx)
549    }
550
551    fn into_any(self) -> AnyElement<V>
552    where
553        Self: Sized,
554    {
555        self
556    }
557}
558
559pub struct RootElement<V: View> {
560    element: AnyElement<V>,
561    view: WeakViewHandle<V>,
562}
563
564impl<V: View> RootElement<V> {
565    pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
566        Self { element, view }
567    }
568}
569
570pub trait Component<V: View>: 'static {
571    fn render(&self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
572}
573
574pub struct ComponentHost<V: View, C: Component<V>> {
575    component: C,
576    view_type: PhantomData<V>,
577}
578
579impl<V: View, C: Component<V>> Deref for ComponentHost<V, C> {
580    type Target = C;
581
582    fn deref(&self) -> &Self::Target {
583        &self.component
584    }
585}
586
587impl<V: View, C: Component<V>> DerefMut for ComponentHost<V, C> {
588    fn deref_mut(&mut self) -> &mut Self::Target {
589        &mut self.component
590    }
591}
592
593impl<V: View, C: Component<V>> Element<V> for ComponentHost<V, C> {
594    type LayoutState = AnyElement<V>;
595    type PaintState = ();
596
597    fn layout(
598        &mut self,
599        constraint: SizeConstraint,
600        view: &mut V,
601        cx: &mut ViewContext<V>,
602    ) -> (Vector2F, AnyElement<V>) {
603        let mut element = self.component.render(view, cx);
604        let size = element.layout(constraint, view, cx);
605        (size, element)
606    }
607
608    fn paint(
609        &mut self,
610        scene: &mut SceneBuilder,
611        bounds: RectF,
612        visible_bounds: RectF,
613        element: &mut AnyElement<V>,
614        view: &mut V,
615        cx: &mut ViewContext<V>,
616    ) {
617        element.paint(scene, bounds.origin(), visible_bounds, view, cx);
618    }
619
620    fn rect_for_text_range(
621        &self,
622        range_utf16: Range<usize>,
623        _: RectF,
624        _: RectF,
625        element: &AnyElement<V>,
626        _: &(),
627        view: &V,
628        cx: &ViewContext<V>,
629    ) -> Option<RectF> {
630        element.rect_for_text_range(range_utf16, view, cx)
631    }
632
633    fn debug(
634        &self,
635        _: RectF,
636        element: &AnyElement<V>,
637        _: &(),
638        view: &V,
639        cx: &ViewContext<V>,
640    ) -> serde_json::Value {
641        element.debug(view, cx)
642    }
643}
644
645pub trait AnyRootElement {
646    fn layout(&mut self, constraint: SizeConstraint, cx: &mut WindowContext) -> Result<Vector2F>;
647    fn paint(
648        &mut self,
649        scene: &mut SceneBuilder,
650        origin: Vector2F,
651        visible_bounds: RectF,
652        cx: &mut WindowContext,
653    ) -> Result<()>;
654    fn rect_for_text_range(
655        &self,
656        range_utf16: Range<usize>,
657        cx: &WindowContext,
658    ) -> Result<Option<RectF>>;
659    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
660    fn name(&self) -> Option<&str>;
661}
662
663impl<V: View> AnyRootElement for RootElement<V> {
664    fn layout(&mut self, constraint: SizeConstraint, cx: &mut WindowContext) -> Result<Vector2F> {
665        let view = self
666            .view
667            .upgrade(cx)
668            .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
669        view.update(cx, |view, cx| Ok(self.element.layout(constraint, view, cx)))
670    }
671
672    fn paint(
673        &mut self,
674        scene: &mut SceneBuilder,
675        origin: Vector2F,
676        visible_bounds: RectF,
677        cx: &mut WindowContext,
678    ) -> Result<()> {
679        let view = self
680            .view
681            .upgrade(cx)
682            .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
683
684        view.update(cx, |view, cx| {
685            self.element.paint(scene, origin, visible_bounds, view, cx);
686            Ok(())
687        })
688    }
689
690    fn rect_for_text_range(
691        &self,
692        range_utf16: Range<usize>,
693        cx: &WindowContext,
694    ) -> Result<Option<RectF>> {
695        let view = self.view.upgrade(cx).ok_or_else(|| {
696            anyhow!("rect_for_text_range called on a root element for a dropped view")
697        })?;
698        let view = view.read(cx);
699        let view_context = ViewContext::immutable(cx, self.view.id());
700        Ok(self
701            .element
702            .rect_for_text_range(range_utf16, view, &view_context))
703    }
704
705    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
706        let view = self
707            .view
708            .upgrade(cx)
709            .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
710        let view = view.read(cx);
711        let view_context = ViewContext::immutable(cx, self.view.id());
712        Ok(self.element.debug(view, &view_context))
713    }
714
715    fn name(&self) -> Option<&str> {
716        self.element.name()
717    }
718}
719
720impl<V: View, R: View> Element<V> for RootElement<R> {
721    type LayoutState = ();
722    type PaintState = ();
723
724    fn layout(
725        &mut self,
726        constraint: SizeConstraint,
727        _view: &mut V,
728        cx: &mut ViewContext<V>,
729    ) -> (Vector2F, ()) {
730        let size = AnyRootElement::layout(self, constraint, cx)
731            .log_err()
732            .unwrap_or_else(|| Vector2F::zero());
733        (size, ())
734    }
735
736    fn paint(
737        &mut self,
738        scene: &mut SceneBuilder,
739        bounds: RectF,
740        visible_bounds: RectF,
741        _layout: &mut Self::LayoutState,
742        _view: &mut V,
743        cx: &mut ViewContext<V>,
744    ) {
745        AnyRootElement::paint(self, scene, bounds.origin(), visible_bounds, cx).log_err();
746    }
747
748    fn rect_for_text_range(
749        &self,
750        range_utf16: Range<usize>,
751        _bounds: RectF,
752        _visible_bounds: RectF,
753        _layout: &Self::LayoutState,
754        _paint: &Self::PaintState,
755        _view: &V,
756        cx: &ViewContext<V>,
757    ) -> Option<RectF> {
758        AnyRootElement::rect_for_text_range(self, range_utf16, cx)
759            .log_err()
760            .flatten()
761    }
762
763    fn debug(
764        &self,
765        _bounds: RectF,
766        _layout: &Self::LayoutState,
767        _paint: &Self::PaintState,
768        _view: &V,
769        cx: &ViewContext<V>,
770    ) -> serde_json::Value {
771        AnyRootElement::debug(self, cx)
772            .log_err()
773            .unwrap_or_default()
774    }
775}
776
777pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
778    fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
779        self.extend(children.into_iter().map(|child| child.into_any()));
780    }
781
782    fn add_child<D: Element<V>>(&mut self, child: D) {
783        self.extend(Some(child.into_any()));
784    }
785
786    fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
787        self.extend(children.into_iter().map(|child| child.into_any()));
788        self
789    }
790
791    fn with_child<D: Element<V>>(mut self, child: D) -> Self {
792        self.extend(Some(child.into_any()));
793        self
794    }
795}
796
797impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
798
799pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
800    if max_size.x().is_infinite() && max_size.y().is_infinite() {
801        size
802    } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
803        vec2f(size.x() * max_size.y() / size.y(), max_size.y())
804    } else {
805        vec2f(max_size.x(), size.y() * max_size.x() / size.x())
806    }
807}