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