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 resizable(
191        self,
192        side: HandleSide,
193        size: f32,
194        on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext<V>),
195    ) -> Resizable<V>
196    where
197        Self: 'static + Sized,
198    {
199        Resizable::new(self.into_any(), side, size, on_resize)
200    }
201
202    fn mouse<Tag>(self, region_id: usize) -> MouseEventHandler<Tag, V>
203    where
204        Self: Sized,
205    {
206        MouseEventHandler::for_child(self.into_any(), region_id)
207    }
208}
209
210trait AnyElementState<V: View> {
211    fn layout(
212        &mut self,
213        constraint: SizeConstraint,
214        view: &mut V,
215        cx: &mut LayoutContext<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 LayoutContext<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 LayoutContext<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 LayoutContext<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>> ComponentHost<V, C> {
580    pub fn new(c: C) -> Self {
581        Self {
582            component: c,
583            view_type: PhantomData,
584        }
585    }
586}
587
588impl<V: View, C: Component<V>> Deref for ComponentHost<V, C> {
589    type Target = C;
590
591    fn deref(&self) -> &Self::Target {
592        &self.component
593    }
594}
595
596impl<V: View, C: Component<V>> DerefMut for ComponentHost<V, C> {
597    fn deref_mut(&mut self) -> &mut Self::Target {
598        &mut self.component
599    }
600}
601
602impl<V: View, C: Component<V>> Element<V> for ComponentHost<V, C> {
603    type LayoutState = AnyElement<V>;
604    type PaintState = ();
605
606    fn layout(
607        &mut self,
608        constraint: SizeConstraint,
609        view: &mut V,
610        cx: &mut LayoutContext<V>,
611    ) -> (Vector2F, AnyElement<V>) {
612        let mut element = self.component.render(view, cx);
613        let size = element.layout(constraint, view, cx);
614        (size, element)
615    }
616
617    fn paint(
618        &mut self,
619        scene: &mut SceneBuilder,
620        bounds: RectF,
621        visible_bounds: RectF,
622        element: &mut AnyElement<V>,
623        view: &mut V,
624        cx: &mut ViewContext<V>,
625    ) {
626        element.paint(scene, bounds.origin(), visible_bounds, view, cx);
627    }
628
629    fn rect_for_text_range(
630        &self,
631        range_utf16: Range<usize>,
632        _: RectF,
633        _: RectF,
634        element: &AnyElement<V>,
635        _: &(),
636        view: &V,
637        cx: &ViewContext<V>,
638    ) -> Option<RectF> {
639        element.rect_for_text_range(range_utf16, view, cx)
640    }
641
642    fn debug(
643        &self,
644        _: RectF,
645        element: &AnyElement<V>,
646        _: &(),
647        view: &V,
648        cx: &ViewContext<V>,
649    ) -> serde_json::Value {
650        element.debug(view, cx)
651    }
652}
653
654pub trait AnyRootElement {
655    fn layout(
656        &mut self,
657        constraint: SizeConstraint,
658        new_parents: &mut HashMap<usize, usize>,
659        views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
660        refreshing: bool,
661        cx: &mut WindowContext,
662    ) -> Result<Vector2F>;
663    fn paint(
664        &mut self,
665        scene: &mut SceneBuilder,
666        origin: Vector2F,
667        visible_bounds: RectF,
668        cx: &mut WindowContext,
669    ) -> Result<()>;
670    fn rect_for_text_range(
671        &self,
672        range_utf16: Range<usize>,
673        cx: &WindowContext,
674    ) -> Result<Option<RectF>>;
675    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
676    fn name(&self) -> Option<&str>;
677}
678
679impl<V: View> AnyRootElement for RootElement<V> {
680    fn layout(
681        &mut self,
682        constraint: SizeConstraint,
683        new_parents: &mut HashMap<usize, usize>,
684        views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
685        refreshing: bool,
686        cx: &mut WindowContext,
687    ) -> Result<Vector2F> {
688        let view = self
689            .view
690            .upgrade(cx)
691            .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
692        view.update(cx, |view, cx| {
693            let mut cx = LayoutContext::new(
694                cx,
695                new_parents,
696                views_to_notify_if_ancestors_change,
697                refreshing,
698            );
699            Ok(self.element.layout(constraint, view, &mut cx))
700        })
701    }
702
703    fn paint(
704        &mut self,
705        scene: &mut SceneBuilder,
706        origin: Vector2F,
707        visible_bounds: RectF,
708        cx: &mut WindowContext,
709    ) -> Result<()> {
710        let view = self
711            .view
712            .upgrade(cx)
713            .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
714
715        view.update(cx, |view, cx| {
716            self.element.paint(scene, origin, visible_bounds, view, cx);
717            Ok(())
718        })
719    }
720
721    fn rect_for_text_range(
722        &self,
723        range_utf16: Range<usize>,
724        cx: &WindowContext,
725    ) -> Result<Option<RectF>> {
726        let view = self.view.upgrade(cx).ok_or_else(|| {
727            anyhow!("rect_for_text_range called on a root element for a dropped view")
728        })?;
729        let view = view.read(cx);
730        let view_context = ViewContext::immutable(cx, self.view.id());
731        Ok(self
732            .element
733            .rect_for_text_range(range_utf16, view, &view_context))
734    }
735
736    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
737        let view = self
738            .view
739            .upgrade(cx)
740            .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
741        let view = view.read(cx);
742        let view_context = ViewContext::immutable(cx, self.view.id());
743        Ok(serde_json::json!({
744            "view_id": self.view.id(),
745            "view_name": V::ui_name(),
746            "view": view.debug_json(cx),
747            "element": self.element.debug(view, &view_context)
748        }))
749    }
750
751    fn name(&self) -> Option<&str> {
752        self.element.name()
753    }
754}
755
756pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
757    fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
758        self.extend(children.into_iter().map(|child| child.into_any()));
759    }
760
761    fn add_child<D: Element<V>>(&mut self, child: D) {
762        self.extend(Some(child.into_any()));
763    }
764
765    fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
766        self.extend(children.into_iter().map(|child| child.into_any()));
767        self
768    }
769
770    fn with_child<D: Element<V>>(mut self, child: D) -> Self {
771        self.extend(Some(child.into_any()));
772        self
773    }
774}
775
776impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
777
778pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
779    if max_size.x().is_infinite() && max_size.y().is_infinite() {
780        size
781    } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
782        vec2f(size.x() * max_size.y() / size.y(), max_size.y())
783    } else {
784        vec2f(max_size.x(), size.y() * max_size.x() / size.x())
785    }
786}