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