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, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext,
 37    WeakViewHandle, WindowContext,
 38};
 39use anyhow::{anyhow, Result};
 40use collections::HashMap;
 41use core::panic;
 42use json::ToJson;
 43use smallvec::SmallVec;
 44use std::{any::Any, borrow::Cow, mem, ops::Range};
 45
 46pub trait Element<V: View>: 'static {
 47    type LayoutState;
 48    type PaintState;
 49
 50    fn view_name(&self) -> &'static str {
 51        V::ui_name()
 52    }
 53
 54    fn layout(
 55        &mut self,
 56        constraint: SizeConstraint,
 57        view: &mut V,
 58        cx: &mut LayoutContext<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 PaintContext<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: impl Into<Cow<'static, str>>,
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>(id, text, action, style, self.into_any(), cx)
186    }
187
188    fn resizable(
189        self,
190        side: HandleSide,
191        size: f32,
192        on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext<V>),
193    ) -> Resizable<V>
194    where
195        Self: 'static + Sized,
196    {
197        Resizable::new(self.into_any(), side, size, on_resize)
198    }
199
200    fn mouse<Tag>(self, region_id: usize) -> MouseEventHandler<Tag, V>
201    where
202        Self: Sized,
203    {
204        MouseEventHandler::for_child(self.into_any(), region_id)
205    }
206}
207
208pub trait RenderElement {
209    fn render<V: View>(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
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!(
279                    size.x().is_finite(),
280                    "Element for {:?} had infinite x size after layout",
281                    element.view_name()
282                );
283                debug_assert!(
284                    size.y().is_finite(),
285                    "Element for {:?} had infinite y size after layout",
286                    element.view_name()
287                );
288
289                result = size;
290                ElementState::PostLayout {
291                    element,
292                    constraint,
293                    size,
294                    layout,
295                }
296            }
297        };
298        result
299    }
300
301    fn paint(
302        &mut self,
303        scene: &mut SceneBuilder,
304        origin: Vector2F,
305        visible_bounds: RectF,
306        view: &mut V,
307        cx: &mut ViewContext<V>,
308    ) {
309        *self = match mem::take(self) {
310            ElementState::PostLayout {
311                mut element,
312                constraint,
313                size,
314                mut layout,
315            } => {
316                let bounds = RectF::new(origin, size);
317                let paint = element.paint(
318                    scene,
319                    bounds,
320                    visible_bounds,
321                    &mut layout,
322                    view,
323                    &mut PaintContext::new(cx),
324                );
325                ElementState::PostPaint {
326                    element,
327                    constraint,
328                    bounds,
329                    visible_bounds,
330                    layout,
331                    paint,
332                }
333            }
334            ElementState::PostPaint {
335                mut element,
336                constraint,
337                bounds,
338                mut layout,
339                ..
340            } => {
341                let bounds = RectF::new(origin, bounds.size());
342                let paint = element.paint(
343                    scene,
344                    bounds,
345                    visible_bounds,
346                    &mut layout,
347                    view,
348                    &mut PaintContext::new(cx),
349                );
350                ElementState::PostPaint {
351                    element,
352                    constraint,
353                    bounds,
354                    visible_bounds,
355                    layout,
356                    paint,
357                }
358            }
359            ElementState::Empty => panic!("invalid element lifecycle state"),
360            ElementState::Init { .. } => {
361                panic!("invalid element lifecycle state, paint called before layout")
362            }
363        }
364    }
365
366    fn rect_for_text_range(
367        &self,
368        range_utf16: Range<usize>,
369        view: &V,
370        cx: &ViewContext<V>,
371    ) -> Option<RectF> {
372        if let ElementState::PostPaint {
373            element,
374            bounds,
375            visible_bounds,
376            layout,
377            paint,
378            ..
379        } = self
380        {
381            element.rect_for_text_range(
382                range_utf16,
383                *bounds,
384                *visible_bounds,
385                layout,
386                paint,
387                view,
388                cx,
389            )
390        } else {
391            None
392        }
393    }
394
395    fn size(&self) -> Vector2F {
396        match self {
397            ElementState::Empty | ElementState::Init { .. } => {
398                panic!("invalid element lifecycle state")
399            }
400            ElementState::PostLayout { size, .. } => *size,
401            ElementState::PostPaint { bounds, .. } => bounds.size(),
402        }
403    }
404
405    fn metadata(&self) -> Option<&dyn Any> {
406        match self {
407            ElementState::Empty => unreachable!(),
408            ElementState::Init { element }
409            | ElementState::PostLayout { element, .. }
410            | ElementState::PostPaint { element, .. } => element.metadata(),
411        }
412    }
413
414    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
415        match self {
416            ElementState::PostPaint {
417                element,
418                constraint,
419                bounds,
420                visible_bounds,
421                layout,
422                paint,
423            } => {
424                let mut value = element.debug(*bounds, layout, paint, view, cx);
425                if let json::Value::Object(map) = &mut value {
426                    let mut new_map: crate::json::Map<String, serde_json::Value> =
427                        Default::default();
428                    if let Some(typ) = map.remove("type") {
429                        new_map.insert("type".into(), typ);
430                    }
431                    new_map.insert("constraint".into(), constraint.to_json());
432                    new_map.insert("bounds".into(), bounds.to_json());
433                    new_map.insert("visible_bounds".into(), visible_bounds.to_json());
434                    new_map.append(map);
435                    json::Value::Object(new_map)
436                } else {
437                    value
438                }
439            }
440
441            _ => panic!("invalid element lifecycle state"),
442        }
443    }
444}
445
446impl<V: View, E: Element<V>> Default for ElementState<V, E> {
447    fn default() -> Self {
448        Self::Empty
449    }
450}
451
452pub struct AnyElement<V: View> {
453    state: Box<dyn AnyElementState<V>>,
454    name: Option<Cow<'static, str>>,
455}
456
457impl<V: View> AnyElement<V> {
458    pub fn name(&self) -> Option<&str> {
459        self.name.as_deref()
460    }
461
462    pub fn metadata<T: 'static>(&self) -> Option<&T> {
463        self.state
464            .metadata()
465            .and_then(|data| data.downcast_ref::<T>())
466    }
467
468    pub fn layout(
469        &mut self,
470        constraint: SizeConstraint,
471        view: &mut V,
472        cx: &mut LayoutContext<V>,
473    ) -> Vector2F {
474        self.state.layout(constraint, view, cx)
475    }
476
477    pub fn paint(
478        &mut self,
479        scene: &mut SceneBuilder,
480        origin: Vector2F,
481        visible_bounds: RectF,
482        view: &mut V,
483        cx: &mut ViewContext<V>,
484    ) {
485        self.state.paint(scene, origin, visible_bounds, view, cx);
486    }
487
488    pub fn rect_for_text_range(
489        &self,
490        range_utf16: Range<usize>,
491        view: &V,
492        cx: &ViewContext<V>,
493    ) -> Option<RectF> {
494        self.state.rect_for_text_range(range_utf16, view, cx)
495    }
496
497    pub fn size(&self) -> Vector2F {
498        self.state.size()
499    }
500
501    pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
502        let mut value = self.state.debug(view, cx);
503
504        if let Some(name) = &self.name {
505            if let json::Value::Object(map) = &mut value {
506                let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
507                new_map.insert("name".into(), json::Value::String(name.to_string()));
508                new_map.append(map);
509                return json::Value::Object(new_map);
510            }
511        }
512
513        value
514    }
515
516    pub fn with_metadata<T, F, R>(&self, f: F) -> R
517    where
518        T: 'static,
519        F: FnOnce(Option<&T>) -> R,
520    {
521        f(self.state.metadata().and_then(|m| m.downcast_ref()))
522    }
523}
524
525impl<V: View> Element<V> for AnyElement<V> {
526    type LayoutState = ();
527    type PaintState = ();
528
529    fn layout(
530        &mut self,
531        constraint: SizeConstraint,
532        view: &mut V,
533        cx: &mut LayoutContext<V>,
534    ) -> (Vector2F, Self::LayoutState) {
535        let size = self.layout(constraint, view, cx);
536        (size, ())
537    }
538
539    fn paint(
540        &mut self,
541        scene: &mut SceneBuilder,
542        bounds: RectF,
543        visible_bounds: RectF,
544        _: &mut Self::LayoutState,
545        view: &mut V,
546        cx: &mut PaintContext<V>,
547    ) -> Self::PaintState {
548        self.paint(scene, bounds.origin(), visible_bounds, view, cx);
549    }
550
551    fn rect_for_text_range(
552        &self,
553        range_utf16: Range<usize>,
554        _: RectF,
555        _: RectF,
556        _: &Self::LayoutState,
557        _: &Self::PaintState,
558        view: &V,
559        cx: &ViewContext<V>,
560    ) -> Option<RectF> {
561        self.rect_for_text_range(range_utf16, view, cx)
562    }
563
564    fn debug(
565        &self,
566        _: RectF,
567        _: &Self::LayoutState,
568        _: &Self::PaintState,
569        view: &V,
570        cx: &ViewContext<V>,
571    ) -> serde_json::Value {
572        self.debug(view, cx)
573    }
574
575    fn into_any(self) -> AnyElement<V>
576    where
577        Self: Sized,
578    {
579        self
580    }
581}
582
583pub struct RootElement<V: View> {
584    element: AnyElement<V>,
585    view: WeakViewHandle<V>,
586}
587
588impl<V: View> RootElement<V> {
589    pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
590        Self { element, view }
591    }
592}
593
594pub trait AnyRootElement {
595    fn layout(
596        &mut self,
597        constraint: SizeConstraint,
598        new_parents: &mut HashMap<usize, usize>,
599        views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
600        refreshing: bool,
601        cx: &mut WindowContext,
602    ) -> Result<Vector2F>;
603    fn paint(
604        &mut self,
605        scene: &mut SceneBuilder,
606        origin: Vector2F,
607        visible_bounds: RectF,
608        cx: &mut WindowContext,
609    ) -> Result<()>;
610    fn rect_for_text_range(
611        &self,
612        range_utf16: Range<usize>,
613        cx: &WindowContext,
614    ) -> Result<Option<RectF>>;
615    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
616    fn name(&self) -> Option<&str>;
617}
618
619impl<V: View> AnyRootElement for RootElement<V> {
620    fn layout(
621        &mut self,
622        constraint: SizeConstraint,
623        new_parents: &mut HashMap<usize, usize>,
624        views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
625        refreshing: bool,
626        cx: &mut WindowContext,
627    ) -> Result<Vector2F> {
628        let view = self
629            .view
630            .upgrade(cx)
631            .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
632        view.update(cx, |view, cx| {
633            let mut cx = LayoutContext::new(
634                cx,
635                new_parents,
636                views_to_notify_if_ancestors_change,
637                refreshing,
638            );
639            Ok(self.element.layout(constraint, view, &mut cx))
640        })
641    }
642
643    fn paint(
644        &mut self,
645        scene: &mut SceneBuilder,
646        origin: Vector2F,
647        visible_bounds: RectF,
648        cx: &mut WindowContext,
649    ) -> Result<()> {
650        let view = self
651            .view
652            .upgrade(cx)
653            .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
654
655        view.update(cx, |view, cx| {
656            self.element.paint(scene, origin, visible_bounds, view, cx);
657            Ok(())
658        })
659    }
660
661    fn rect_for_text_range(
662        &self,
663        range_utf16: Range<usize>,
664        cx: &WindowContext,
665    ) -> Result<Option<RectF>> {
666        let view = self.view.upgrade(cx).ok_or_else(|| {
667            anyhow!("rect_for_text_range called on a root element for a dropped view")
668        })?;
669        let view = view.read(cx);
670        let view_context = ViewContext::immutable(cx, self.view.id());
671        Ok(self
672            .element
673            .rect_for_text_range(range_utf16, view, &view_context))
674    }
675
676    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
677        let view = self
678            .view
679            .upgrade(cx)
680            .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
681        let view = view.read(cx);
682        let view_context = ViewContext::immutable(cx, self.view.id());
683        Ok(serde_json::json!({
684            "view_id": self.view.id(),
685            "view_name": V::ui_name(),
686            "view": view.debug_json(cx),
687            "element": self.element.debug(view, &view_context)
688        }))
689    }
690
691    fn name(&self) -> Option<&str> {
692        self.element.name()
693    }
694}
695
696pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
697    fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
698        self.extend(children.into_iter().map(|child| child.into_any()));
699    }
700
701    fn add_child<D: Element<V>>(&mut self, child: D) {
702        self.extend(Some(child.into_any()));
703    }
704
705    fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
706        self.extend(children.into_iter().map(|child| child.into_any()));
707        self
708    }
709
710    fn with_child<D: Element<V>>(mut self, child: D) -> Self {
711        self.extend(Some(child.into_any()));
712        self
713    }
714}
715
716impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
717
718pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
719    if max_size.x().is_infinite() && max_size.y().is_infinite() {
720        size
721    } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
722        vec2f(size.x() * max_size.y() / size.y(), max_size.y())
723    } else {
724        vec2f(max_size.x(), size.y() * max_size.x() / size.x())
725    }
726}