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