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>: 'static {
 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 into_element(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_named_element(self, name: impl Into<Cow<'static, str>>) -> Element<V>
107    where
108        Self: 'static + Sized,
109    {
110        Element {
111            drawable: Box::new(Lifecycle::Init { element: self }),
112            view_type: PhantomData,
113            name: Some(name.into()),
114        }
115    }
116
117    fn into_root_element(self, cx: &ViewContext<V>) -> RootElement<V>
118    where
119        Self: 'static + Sized,
120    {
121        RootElement {
122            element: self.into_element(),
123            view: cx.handle().downgrade(),
124        }
125    }
126
127    fn constrained(self) -> ConstrainedBox<V>
128    where
129        Self: 'static + Sized,
130    {
131        ConstrainedBox::new(self.into_element())
132    }
133
134    fn aligned(self) -> Align<V>
135    where
136        Self: 'static + Sized,
137    {
138        Align::new(self.into_element())
139    }
140
141    fn clipped(self) -> Clipped<V>
142    where
143        Self: 'static + Sized,
144    {
145        Clipped::new(self.into_element())
146    }
147
148    fn contained(self) -> Container<V>
149    where
150        Self: 'static + Sized,
151    {
152        Container::new(self.into_element())
153    }
154
155    fn expanded(self) -> Expanded<V>
156    where
157        Self: 'static + Sized,
158    {
159        Expanded::new(self.into_element())
160    }
161
162    fn flex(self, flex: f32, expanded: bool) -> FlexItem<V>
163    where
164        Self: 'static + Sized,
165    {
166        FlexItem::new(self.into_element()).flex(flex, expanded)
167    }
168
169    fn flex_float(self) -> FlexItem<V>
170    where
171        Self: 'static + Sized,
172    {
173        FlexItem::new(self.into_element()).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.into_element(), 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.into_element(),
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
502impl<V: View> Drawable<V> for Element<V> {
503    type LayoutState = ();
504    type PaintState = ();
505
506    fn layout(
507        &mut self,
508        constraint: SizeConstraint,
509        view: &mut V,
510        cx: &mut ViewContext<V>,
511    ) -> (Vector2F, Self::LayoutState) {
512        let size = self.layout(constraint, view, cx);
513        (size, ())
514    }
515
516    fn paint(
517        &mut self,
518        scene: &mut SceneBuilder,
519        bounds: RectF,
520        visible_bounds: RectF,
521        _: &mut Self::LayoutState,
522        view: &mut V,
523        cx: &mut ViewContext<V>,
524    ) -> Self::PaintState {
525        self.paint(scene, bounds.origin(), visible_bounds, view, cx);
526    }
527
528    fn rect_for_text_range(
529        &self,
530        range_utf16: Range<usize>,
531        _: RectF,
532        _: RectF,
533        _: &Self::LayoutState,
534        _: &Self::PaintState,
535        view: &V,
536        cx: &ViewContext<V>,
537    ) -> Option<RectF> {
538        self.rect_for_text_range(range_utf16, view, cx)
539    }
540
541    fn debug(
542        &self,
543        _: RectF,
544        _: &Self::LayoutState,
545        _: &Self::PaintState,
546        view: &V,
547        cx: &ViewContext<V>,
548    ) -> serde_json::Value {
549        self.debug(view, cx)
550    }
551
552    fn into_element(self) -> Element<V>
553    where
554        Self: Sized,
555    {
556        self
557    }
558}
559
560pub struct RootElement<V: View> {
561    element: Element<V>,
562    view: WeakViewHandle<V>,
563}
564
565impl<V: View> RootElement<V> {
566    pub fn new(element: Element<V>, view: WeakViewHandle<V>) -> Self {
567        Self { element, view }
568    }
569}
570
571pub trait Component<V: View>: 'static {
572    fn render(&self, view: &mut V, cx: &mut ViewContext<V>) -> Element<V>;
573}
574
575pub struct ComponentHost<V: View, C: Component<V>> {
576    component: C,
577    view_type: PhantomData<V>,
578}
579
580impl<V: View, C: Component<V>> Deref for ComponentHost<V, C> {
581    type Target = C;
582
583    fn deref(&self) -> &Self::Target {
584        &self.component
585    }
586}
587
588impl<V: View, C: Component<V>> DerefMut for ComponentHost<V, C> {
589    fn deref_mut(&mut self) -> &mut Self::Target {
590        &mut self.component
591    }
592}
593
594impl<V: View, C: Component<V>> Drawable<V> for ComponentHost<V, C> {
595    type LayoutState = Element<V>;
596    type PaintState = ();
597
598    fn layout(
599        &mut self,
600        constraint: SizeConstraint,
601        view: &mut V,
602        cx: &mut ViewContext<V>,
603    ) -> (Vector2F, Element<V>) {
604        let mut element = self.component.render(view, cx);
605        let size = element.layout(constraint, view, cx);
606        (size, element)
607    }
608
609    fn paint(
610        &mut self,
611        scene: &mut SceneBuilder,
612        bounds: RectF,
613        visible_bounds: RectF,
614        element: &mut Element<V>,
615        view: &mut V,
616        cx: &mut ViewContext<V>,
617    ) {
618        element.paint(scene, bounds.origin(), visible_bounds, view, cx);
619    }
620
621    fn rect_for_text_range(
622        &self,
623        range_utf16: Range<usize>,
624        _: RectF,
625        _: RectF,
626        element: &Element<V>,
627        _: &(),
628        view: &V,
629        cx: &ViewContext<V>,
630    ) -> Option<RectF> {
631        element.rect_for_text_range(range_utf16, view, cx)
632    }
633
634    fn debug(
635        &self,
636        _: RectF,
637        element: &Element<V>,
638        _: &(),
639        view: &V,
640        cx: &ViewContext<V>,
641    ) -> serde_json::Value {
642        element.debug(view, cx)
643    }
644}
645
646pub trait AnyRootElement {
647    fn layout(&mut self, constraint: SizeConstraint, cx: &mut WindowContext) -> Result<Vector2F>;
648    fn paint(
649        &mut self,
650        scene: &mut SceneBuilder,
651        origin: Vector2F,
652        visible_bounds: RectF,
653        cx: &mut WindowContext,
654    ) -> Result<()>;
655    fn rect_for_text_range(
656        &self,
657        range_utf16: Range<usize>,
658        cx: &WindowContext,
659    ) -> Result<Option<RectF>>;
660    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
661    fn name(&self) -> Option<&str>;
662}
663
664impl<V: View> AnyRootElement for RootElement<V> {
665    fn layout(&mut self, constraint: SizeConstraint, cx: &mut WindowContext) -> Result<Vector2F> {
666        let view = self
667            .view
668            .upgrade(cx)
669            .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
670        view.update(cx, |view, cx| Ok(self.element.layout(constraint, view, cx)))
671    }
672
673    fn paint(
674        &mut self,
675        scene: &mut SceneBuilder,
676        origin: Vector2F,
677        visible_bounds: RectF,
678        cx: &mut WindowContext,
679    ) -> Result<()> {
680        let view = self
681            .view
682            .upgrade(cx)
683            .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
684
685        view.update(cx, |view, cx| {
686            self.element.paint(scene, origin, visible_bounds, view, cx);
687            Ok(())
688        })
689    }
690
691    fn rect_for_text_range(
692        &self,
693        range_utf16: Range<usize>,
694        cx: &WindowContext,
695    ) -> Result<Option<RectF>> {
696        let view = self.view.upgrade(cx).ok_or_else(|| {
697            anyhow!("rect_for_text_range called on a root element for a dropped view")
698        })?;
699        let view = view.read(cx);
700        let view_context = ViewContext::immutable(cx, self.view.id());
701        Ok(self
702            .element
703            .rect_for_text_range(range_utf16, view, &view_context))
704    }
705
706    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
707        let view = self
708            .view
709            .upgrade(cx)
710            .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
711        let view = view.read(cx);
712        let view_context = ViewContext::immutable(cx, self.view.id());
713        Ok(self.element.debug(view, &view_context))
714    }
715
716    fn name(&self) -> Option<&str> {
717        self.element.name()
718    }
719}
720
721impl<V: View, R: View> Drawable<V> for RootElement<R> {
722    type LayoutState = ();
723    type PaintState = ();
724
725    fn layout(
726        &mut self,
727        constraint: SizeConstraint,
728        _view: &mut V,
729        cx: &mut ViewContext<V>,
730    ) -> (Vector2F, ()) {
731        let size = AnyRootElement::layout(self, constraint, cx)
732            .log_err()
733            .unwrap_or_else(|| Vector2F::zero());
734        (size, ())
735    }
736
737    fn paint(
738        &mut self,
739        scene: &mut SceneBuilder,
740        bounds: RectF,
741        visible_bounds: RectF,
742        _layout: &mut Self::LayoutState,
743        _view: &mut V,
744        cx: &mut ViewContext<V>,
745    ) {
746        AnyRootElement::paint(self, scene, bounds.origin(), visible_bounds, cx).log_err();
747    }
748
749    fn rect_for_text_range(
750        &self,
751        range_utf16: Range<usize>,
752        _bounds: RectF,
753        _visible_bounds: RectF,
754        _layout: &Self::LayoutState,
755        _paint: &Self::PaintState,
756        _view: &V,
757        cx: &ViewContext<V>,
758    ) -> Option<RectF> {
759        AnyRootElement::rect_for_text_range(self, range_utf16, cx)
760            .log_err()
761            .flatten()
762    }
763
764    fn debug(
765        &self,
766        _bounds: RectF,
767        _layout: &Self::LayoutState,
768        _paint: &Self::PaintState,
769        _view: &V,
770        cx: &ViewContext<V>,
771    ) -> serde_json::Value {
772        AnyRootElement::debug(self, cx)
773            .log_err()
774            .unwrap_or_default()
775    }
776}
777
778pub trait ParentElement<'a, V: View>: Extend<Element<V>> + Sized {
779    fn add_children<D: Drawable<V>>(&mut self, children: impl IntoIterator<Item = D>) {
780        self.extend(children.into_iter().map(|child| child.into_element()));
781    }
782
783    fn add_child<D: Drawable<V>>(&mut self, child: D) {
784        self.extend(Some(child.into_element()));
785    }
786
787    fn with_children<D: Drawable<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
788        self.extend(children.into_iter().map(|child| child.into_element()));
789        self
790    }
791
792    fn with_child<D: Drawable<V>>(mut self, child: D) -> Self {
793        self.extend(Some(child.into_element()));
794        self
795    }
796}
797
798impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<Element<V>> {}
799
800pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
801    if max_size.x().is_infinite() && max_size.y().is_infinite() {
802        size
803    } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
804        vec2f(size.x() * max_size.y() / size.y(), max_size.y())
805    } else {
806        vec2f(max_size.x(), size.y() * max_size.x() / size.x())
807    }
808}