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::{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 ViewContext<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: String,
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, V>(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
208trait AnyElementState<V: View> {
209    fn layout(
210        &mut self,
211        constraint: SizeConstraint,
212        view: &mut V,
213        cx: &mut LayoutContext<V>,
214    ) -> Vector2F;
215
216    fn paint(
217        &mut self,
218        scene: &mut SceneBuilder,
219        origin: Vector2F,
220        visible_bounds: RectF,
221        view: &mut V,
222        cx: &mut ViewContext<V>,
223    );
224
225    fn rect_for_text_range(
226        &self,
227        range_utf16: Range<usize>,
228        view: &V,
229        cx: &ViewContext<V>,
230    ) -> Option<RectF>;
231
232    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value;
233
234    fn size(&self) -> Vector2F;
235
236    fn metadata(&self) -> Option<&dyn Any>;
237}
238
239enum ElementState<V: View, E: Element<V>> {
240    Empty,
241    Init {
242        element: E,
243    },
244    PostLayout {
245        element: E,
246        constraint: SizeConstraint,
247        size: Vector2F,
248        layout: E::LayoutState,
249    },
250    PostPaint {
251        element: E,
252        constraint: SizeConstraint,
253        bounds: RectF,
254        visible_bounds: RectF,
255        layout: E::LayoutState,
256        paint: E::PaintState,
257    },
258}
259
260impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
261    fn layout(
262        &mut self,
263        constraint: SizeConstraint,
264        view: &mut V,
265        cx: &mut LayoutContext<V>,
266    ) -> Vector2F {
267        let result;
268        *self = match mem::take(self) {
269            ElementState::Empty => unreachable!(),
270            ElementState::Init { mut element }
271            | ElementState::PostLayout { mut element, .. }
272            | ElementState::PostPaint { mut element, .. } => {
273                let (size, layout) = element.layout(constraint, view, cx);
274                debug_assert!(
275                    size.x().is_finite(),
276                    "Element for {:?} had infinite x size after layout",
277                    element.view_name()
278                );
279                debug_assert!(
280                    size.y().is_finite(),
281                    "Element for {:?} had infinite y size after layout",
282                    element.view_name()
283                );
284
285                result = size;
286                ElementState::PostLayout {
287                    element,
288                    constraint,
289                    size,
290                    layout,
291                }
292            }
293        };
294        result
295    }
296
297    fn paint(
298        &mut self,
299        scene: &mut SceneBuilder,
300        origin: Vector2F,
301        visible_bounds: RectF,
302        view: &mut V,
303        cx: &mut ViewContext<V>,
304    ) {
305        *self = match mem::take(self) {
306            ElementState::PostLayout {
307                mut element,
308                constraint,
309                size,
310                mut layout,
311            } => {
312                let bounds = RectF::new(origin, size);
313                let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
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(scene, bounds, visible_bounds, &mut layout, view, cx);
332                ElementState::PostPaint {
333                    element,
334                    constraint,
335                    bounds,
336                    visible_bounds,
337                    layout,
338                    paint,
339                }
340            }
341            ElementState::Empty => panic!("invalid element lifecycle state"),
342            ElementState::Init { .. } => {
343                panic!("invalid element lifecycle state, paint called before layout")
344            }
345        }
346    }
347
348    fn rect_for_text_range(
349        &self,
350        range_utf16: Range<usize>,
351        view: &V,
352        cx: &ViewContext<V>,
353    ) -> Option<RectF> {
354        if let ElementState::PostPaint {
355            element,
356            bounds,
357            visible_bounds,
358            layout,
359            paint,
360            ..
361        } = self
362        {
363            element.rect_for_text_range(
364                range_utf16,
365                *bounds,
366                *visible_bounds,
367                layout,
368                paint,
369                view,
370                cx,
371            )
372        } else {
373            None
374        }
375    }
376
377    fn size(&self) -> Vector2F {
378        match self {
379            ElementState::Empty | ElementState::Init { .. } => {
380                panic!("invalid element lifecycle state")
381            }
382            ElementState::PostLayout { size, .. } => *size,
383            ElementState::PostPaint { bounds, .. } => bounds.size(),
384        }
385    }
386
387    fn metadata(&self) -> Option<&dyn Any> {
388        match self {
389            ElementState::Empty => unreachable!(),
390            ElementState::Init { element }
391            | ElementState::PostLayout { element, .. }
392            | ElementState::PostPaint { element, .. } => element.metadata(),
393        }
394    }
395
396    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
397        match self {
398            ElementState::PostPaint {
399                element,
400                constraint,
401                bounds,
402                visible_bounds,
403                layout,
404                paint,
405            } => {
406                let mut value = element.debug(*bounds, layout, paint, view, cx);
407                if let json::Value::Object(map) = &mut value {
408                    let mut new_map: crate::json::Map<String, serde_json::Value> =
409                        Default::default();
410                    if let Some(typ) = map.remove("type") {
411                        new_map.insert("type".into(), typ);
412                    }
413                    new_map.insert("constraint".into(), constraint.to_json());
414                    new_map.insert("bounds".into(), bounds.to_json());
415                    new_map.insert("visible_bounds".into(), visible_bounds.to_json());
416                    new_map.append(map);
417                    json::Value::Object(new_map)
418                } else {
419                    value
420                }
421            }
422
423            _ => panic!("invalid element lifecycle state"),
424        }
425    }
426}
427
428impl<V: View, E: Element<V>> Default for ElementState<V, E> {
429    fn default() -> Self {
430        Self::Empty
431    }
432}
433
434pub struct AnyElement<V: View> {
435    state: Box<dyn AnyElementState<V>>,
436    name: Option<Cow<'static, str>>,
437}
438
439impl<V: View> AnyElement<V> {
440    pub fn name(&self) -> Option<&str> {
441        self.name.as_deref()
442    }
443
444    pub fn metadata<T: 'static>(&self) -> Option<&T> {
445        self.state
446            .metadata()
447            .and_then(|data| data.downcast_ref::<T>())
448    }
449
450    pub fn layout(
451        &mut self,
452        constraint: SizeConstraint,
453        view: &mut V,
454        cx: &mut LayoutContext<V>,
455    ) -> Vector2F {
456        self.state.layout(constraint, view, cx)
457    }
458
459    pub fn paint(
460        &mut self,
461        scene: &mut SceneBuilder,
462        origin: Vector2F,
463        visible_bounds: RectF,
464        view: &mut V,
465        cx: &mut ViewContext<V>,
466    ) {
467        self.state.paint(scene, origin, visible_bounds, view, cx);
468    }
469
470    pub fn rect_for_text_range(
471        &self,
472        range_utf16: Range<usize>,
473        view: &V,
474        cx: &ViewContext<V>,
475    ) -> Option<RectF> {
476        self.state.rect_for_text_range(range_utf16, view, cx)
477    }
478
479    pub fn size(&self) -> Vector2F {
480        self.state.size()
481    }
482
483    pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
484        let mut value = self.state.debug(view, cx);
485
486        if let Some(name) = &self.name {
487            if let json::Value::Object(map) = &mut value {
488                let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
489                new_map.insert("name".into(), json::Value::String(name.to_string()));
490                new_map.append(map);
491                return json::Value::Object(new_map);
492            }
493        }
494
495        value
496    }
497
498    pub fn with_metadata<T, F, R>(&self, f: F) -> R
499    where
500        T: 'static,
501        F: FnOnce(Option<&T>) -> R,
502    {
503        f(self.state.metadata().and_then(|m| m.downcast_ref()))
504    }
505}
506
507impl<V: View> Element<V> for AnyElement<V> {
508    type LayoutState = ();
509    type PaintState = ();
510
511    fn layout(
512        &mut self,
513        constraint: SizeConstraint,
514        view: &mut V,
515        cx: &mut LayoutContext<V>,
516    ) -> (Vector2F, Self::LayoutState) {
517        let size = self.layout(constraint, view, cx);
518        (size, ())
519    }
520
521    fn paint(
522        &mut self,
523        scene: &mut SceneBuilder,
524        bounds: RectF,
525        visible_bounds: RectF,
526        _: &mut Self::LayoutState,
527        view: &mut V,
528        cx: &mut ViewContext<V>,
529    ) -> Self::PaintState {
530        self.paint(scene, bounds.origin(), visible_bounds, view, cx);
531    }
532
533    fn rect_for_text_range(
534        &self,
535        range_utf16: Range<usize>,
536        _: RectF,
537        _: RectF,
538        _: &Self::LayoutState,
539        _: &Self::PaintState,
540        view: &V,
541        cx: &ViewContext<V>,
542    ) -> Option<RectF> {
543        self.rect_for_text_range(range_utf16, view, cx)
544    }
545
546    fn debug(
547        &self,
548        _: RectF,
549        _: &Self::LayoutState,
550        _: &Self::PaintState,
551        view: &V,
552        cx: &ViewContext<V>,
553    ) -> serde_json::Value {
554        self.debug(view, cx)
555    }
556
557    fn into_any(self) -> AnyElement<V>
558    where
559        Self: Sized,
560    {
561        self
562    }
563}
564
565pub struct RootElement<V: View> {
566    element: AnyElement<V>,
567    view: WeakViewHandle<V>,
568}
569
570impl<V: View> RootElement<V> {
571    pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
572        Self { element, view }
573    }
574}
575
576pub trait AnyRootElement {
577    fn layout(
578        &mut self,
579        constraint: SizeConstraint,
580        new_parents: &mut HashMap<usize, usize>,
581        views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
582        refreshing: bool,
583        cx: &mut WindowContext,
584    ) -> Result<Vector2F>;
585    fn paint(
586        &mut self,
587        scene: &mut SceneBuilder,
588        origin: Vector2F,
589        visible_bounds: RectF,
590        cx: &mut WindowContext,
591    ) -> Result<()>;
592    fn rect_for_text_range(
593        &self,
594        range_utf16: Range<usize>,
595        cx: &WindowContext,
596    ) -> Result<Option<RectF>>;
597    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
598    fn name(&self) -> Option<&str>;
599}
600
601impl<V: View> AnyRootElement for RootElement<V> {
602    fn layout(
603        &mut self,
604        constraint: SizeConstraint,
605        new_parents: &mut HashMap<usize, usize>,
606        views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
607        refreshing: bool,
608        cx: &mut WindowContext,
609    ) -> Result<Vector2F> {
610        let view = self
611            .view
612            .upgrade(cx)
613            .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
614        view.update(cx, |view, cx| {
615            let mut cx = LayoutContext::new(
616                cx,
617                new_parents,
618                views_to_notify_if_ancestors_change,
619                refreshing,
620            );
621            Ok(self.element.layout(constraint, view, &mut cx))
622        })
623    }
624
625    fn paint(
626        &mut self,
627        scene: &mut SceneBuilder,
628        origin: Vector2F,
629        visible_bounds: RectF,
630        cx: &mut WindowContext,
631    ) -> Result<()> {
632        let view = self
633            .view
634            .upgrade(cx)
635            .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
636
637        view.update(cx, |view, cx| {
638            self.element.paint(scene, origin, visible_bounds, view, cx);
639            Ok(())
640        })
641    }
642
643    fn rect_for_text_range(
644        &self,
645        range_utf16: Range<usize>,
646        cx: &WindowContext,
647    ) -> Result<Option<RectF>> {
648        let view = self.view.upgrade(cx).ok_or_else(|| {
649            anyhow!("rect_for_text_range called on a root element for a dropped view")
650        })?;
651        let view = view.read(cx);
652        let view_context = ViewContext::immutable(cx, self.view.id());
653        Ok(self
654            .element
655            .rect_for_text_range(range_utf16, view, &view_context))
656    }
657
658    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
659        let view = self
660            .view
661            .upgrade(cx)
662            .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
663        let view = view.read(cx);
664        let view_context = ViewContext::immutable(cx, self.view.id());
665        Ok(serde_json::json!({
666            "view_id": self.view.id(),
667            "view_name": V::ui_name(),
668            "view": view.debug_json(cx),
669            "element": self.element.debug(view, &view_context)
670        }))
671    }
672
673    fn name(&self) -> Option<&str> {
674        self.element.name()
675    }
676}
677
678pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
679    fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
680        self.extend(children.into_iter().map(|child| child.into_any()));
681    }
682
683    fn add_child<D: Element<V>>(&mut self, child: D) {
684        self.extend(Some(child.into_any()));
685    }
686
687    fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
688        self.extend(children.into_iter().map(|child| child.into_any()));
689        self
690    }
691
692    fn with_child<D: Element<V>>(mut self, child: D) -> Self {
693        self.extend(Some(child.into_any()));
694        self
695    }
696}
697
698impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
699
700pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
701    if max_size.x().is_infinite() && max_size.y().is_infinite() {
702        size
703    } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
704        vec2f(size.x() * max_size.y() / size.y(), max_size.y())
705    } else {
706        vec2f(max_size.x(), size.y() * max_size.x() / size.x())
707    }
708}