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