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