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, HashSet};
 41use core::panic;
 42use json::ToJson;
 43use std::{
 44    any::Any,
 45    borrow::Cow,
 46    marker::PhantomData,
 47    mem,
 48    ops::{Deref, DerefMut, Range},
 49};
 50
 51pub trait Element<V: View>: 'static {
 52    type LayoutState;
 53    type PaintState;
 54
 55    fn layout(
 56        &mut self,
 57        constraint: SizeConstraint,
 58        view: &mut V,
 59        cx: &mut LayoutContext<V>,
 60    ) -> (Vector2F, Self::LayoutState);
 61
 62    fn paint(
 63        &mut self,
 64        scene: &mut SceneBuilder,
 65        bounds: RectF,
 66        visible_bounds: RectF,
 67        layout: &mut Self::LayoutState,
 68        view: &mut V,
 69        cx: &mut ViewContext<V>,
 70    ) -> Self::PaintState;
 71
 72    fn rect_for_text_range(
 73        &self,
 74        range_utf16: Range<usize>,
 75        bounds: RectF,
 76        visible_bounds: RectF,
 77        layout: &Self::LayoutState,
 78        paint: &Self::PaintState,
 79        view: &V,
 80        cx: &ViewContext<V>,
 81    ) -> Option<RectF>;
 82
 83    fn metadata(&self) -> Option<&dyn Any> {
 84        None
 85    }
 86
 87    fn debug(
 88        &self,
 89        bounds: RectF,
 90        layout: &Self::LayoutState,
 91        paint: &Self::PaintState,
 92        view: &V,
 93        cx: &ViewContext<V>,
 94    ) -> serde_json::Value;
 95
 96    fn into_any(self) -> AnyElement<V>
 97    where
 98        Self: 'static + Sized,
 99    {
100        AnyElement {
101            state: Box::new(ElementState::Init { element: self }),
102            name: None,
103        }
104    }
105
106    fn into_any_named(self, name: impl Into<Cow<'static, str>>) -> AnyElement<V>
107    where
108        Self: 'static + Sized,
109    {
110        AnyElement {
111            state: Box::new(ElementState::Init { element: self }),
112            name: Some(name.into()),
113        }
114    }
115
116    fn into_root_element(self, cx: &ViewContext<V>) -> RootElement<V>
117    where
118        Self: 'static + Sized,
119    {
120        RootElement {
121            element: self.into_any(),
122            view: cx.handle().downgrade(),
123        }
124    }
125
126    fn constrained(self) -> ConstrainedBox<V>
127    where
128        Self: 'static + Sized,
129    {
130        ConstrainedBox::new(self.into_any())
131    }
132
133    fn aligned(self) -> Align<V>
134    where
135        Self: 'static + Sized,
136    {
137        Align::new(self.into_any())
138    }
139
140    fn clipped(self) -> Clipped<V>
141    where
142        Self: 'static + Sized,
143    {
144        Clipped::new(self.into_any())
145    }
146
147    fn contained(self) -> Container<V>
148    where
149        Self: 'static + Sized,
150    {
151        Container::new(self.into_any())
152    }
153
154    fn expanded(self) -> Expanded<V>
155    where
156        Self: 'static + Sized,
157    {
158        Expanded::new(self.into_any())
159    }
160
161    fn flex(self, flex: f32, expanded: bool) -> FlexItem<V>
162    where
163        Self: 'static + Sized,
164    {
165        FlexItem::new(self.into_any()).flex(flex, expanded)
166    }
167
168    fn flex_float(self) -> FlexItem<V>
169    where
170        Self: 'static + Sized,
171    {
172        FlexItem::new(self.into_any()).float()
173    }
174
175    fn with_tooltip<Tag: 'static>(
176        self,
177        id: usize,
178        text: String,
179        action: Option<Box<dyn Action>>,
180        style: TooltipStyle,
181        cx: &mut ViewContext<V>,
182    ) -> Tooltip<V>
183    where
184        Self: 'static + Sized,
185    {
186        Tooltip::new::<Tag, V>(id, text, action, style, self.into_any(), cx)
187    }
188
189    fn with_resize_handle<Tag: 'static>(
190        self,
191        element_id: usize,
192        side: Side,
193        handle_size: f32,
194        initial_size: f32,
195        cx: &mut ViewContext<V>,
196    ) -> Resizable<V>
197    where
198        Self: 'static + Sized,
199    {
200        Resizable::new::<Tag, V>(
201            self.into_any(),
202            element_id,
203            side,
204            handle_size,
205            initial_size,
206            cx,
207        )
208    }
209}
210
211trait AnyElementState<V: View> {
212    fn layout(
213        &mut self,
214        constraint: SizeConstraint,
215        view: &mut V,
216        cx: &mut LayoutContext<V>,
217    ) -> Vector2F;
218
219    fn paint(
220        &mut self,
221        scene: &mut SceneBuilder,
222        origin: Vector2F,
223        visible_bounds: RectF,
224        view: &mut V,
225        cx: &mut ViewContext<V>,
226    );
227
228    fn rect_for_text_range(
229        &self,
230        range_utf16: Range<usize>,
231        view: &V,
232        cx: &ViewContext<V>,
233    ) -> Option<RectF>;
234
235    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value;
236
237    fn size(&self) -> Vector2F;
238
239    fn metadata(&self) -> Option<&dyn Any>;
240}
241
242enum ElementState<V: View, E: Element<V>> {
243    Empty,
244    Init {
245        element: E,
246    },
247    PostLayout {
248        element: E,
249        constraint: SizeConstraint,
250        size: Vector2F,
251        layout: E::LayoutState,
252    },
253    PostPaint {
254        element: E,
255        constraint: SizeConstraint,
256        bounds: RectF,
257        visible_bounds: RectF,
258        layout: E::LayoutState,
259        paint: E::PaintState,
260    },
261}
262
263impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
264    fn layout(
265        &mut self,
266        constraint: SizeConstraint,
267        view: &mut V,
268        cx: &mut LayoutContext<V>,
269    ) -> Vector2F {
270        let result;
271        *self = match mem::take(self) {
272            ElementState::Empty => unreachable!(),
273            ElementState::Init { mut element }
274            | ElementState::PostLayout { mut element, .. }
275            | ElementState::PostPaint { mut element, .. } => {
276                let (size, layout) = element.layout(constraint, view, cx);
277                debug_assert!(size.x().is_finite());
278                debug_assert!(size.y().is_finite());
279
280                result = size;
281                ElementState::PostLayout {
282                    element,
283                    constraint,
284                    size,
285                    layout,
286                }
287            }
288        };
289        result
290    }
291
292    fn paint(
293        &mut self,
294        scene: &mut SceneBuilder,
295        origin: Vector2F,
296        visible_bounds: RectF,
297        view: &mut V,
298        cx: &mut ViewContext<V>,
299    ) {
300        *self = match mem::take(self) {
301            ElementState::PostLayout {
302                mut element,
303                constraint,
304                size,
305                mut layout,
306            } => {
307                let bounds = RectF::new(origin, size);
308                let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
309                ElementState::PostPaint {
310                    element,
311                    constraint,
312                    bounds,
313                    visible_bounds,
314                    layout,
315                    paint,
316                }
317            }
318            ElementState::PostPaint {
319                mut element,
320                constraint,
321                bounds,
322                mut layout,
323                ..
324            } => {
325                let bounds = RectF::new(origin, bounds.size());
326                let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
327                ElementState::PostPaint {
328                    element,
329                    constraint,
330                    bounds,
331                    visible_bounds,
332                    layout,
333                    paint,
334                }
335            }
336            ElementState::Empty => panic!("invalid element lifecycle state"),
337            ElementState::Init { .. } => {
338                panic!("invalid element lifecycle state, paint called before layout")
339            }
340        }
341    }
342
343    fn rect_for_text_range(
344        &self,
345        range_utf16: Range<usize>,
346        view: &V,
347        cx: &ViewContext<V>,
348    ) -> Option<RectF> {
349        if let ElementState::PostPaint {
350            element,
351            bounds,
352            visible_bounds,
353            layout,
354            paint,
355            ..
356        } = self
357        {
358            element.rect_for_text_range(
359                range_utf16,
360                *bounds,
361                *visible_bounds,
362                layout,
363                paint,
364                view,
365                cx,
366            )
367        } else {
368            None
369        }
370    }
371
372    fn size(&self) -> Vector2F {
373        match self {
374            ElementState::Empty | ElementState::Init { .. } => {
375                panic!("invalid element lifecycle state")
376            }
377            ElementState::PostLayout { size, .. } => *size,
378            ElementState::PostPaint { bounds, .. } => bounds.size(),
379        }
380    }
381
382    fn metadata(&self) -> Option<&dyn Any> {
383        match self {
384            ElementState::Empty => unreachable!(),
385            ElementState::Init { element }
386            | ElementState::PostLayout { element, .. }
387            | ElementState::PostPaint { element, .. } => element.metadata(),
388        }
389    }
390
391    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
392        match self {
393            ElementState::PostPaint {
394                element,
395                constraint,
396                bounds,
397                visible_bounds,
398                layout,
399                paint,
400            } => {
401                let mut value = element.debug(*bounds, layout, paint, view, cx);
402                if let json::Value::Object(map) = &mut value {
403                    let mut new_map: crate::json::Map<String, serde_json::Value> =
404                        Default::default();
405                    if let Some(typ) = map.remove("type") {
406                        new_map.insert("type".into(), typ);
407                    }
408                    new_map.insert("constraint".into(), constraint.to_json());
409                    new_map.insert("bounds".into(), bounds.to_json());
410                    new_map.insert("visible_bounds".into(), visible_bounds.to_json());
411                    new_map.append(map);
412                    json::Value::Object(new_map)
413                } else {
414                    value
415                }
416            }
417
418            _ => panic!("invalid element lifecycle state"),
419        }
420    }
421}
422
423impl<V: View, E: Element<V>> Default for ElementState<V, E> {
424    fn default() -> Self {
425        Self::Empty
426    }
427}
428
429pub struct AnyElement<V: View> {
430    state: Box<dyn AnyElementState<V>>,
431    name: Option<Cow<'static, str>>,
432}
433
434impl<V: View> AnyElement<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.state
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 LayoutContext<V>,
450    ) -> Vector2F {
451        self.state.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.state.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.state.rect_for_text_range(range_utf16, view, cx)
472    }
473
474    pub fn size(&self) -> Vector2F {
475        self.state.size()
476    }
477
478    pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
479        let mut value = self.state.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.state.metadata().and_then(|m| m.downcast_ref()))
499    }
500}
501
502impl<V: View> Element<V> for AnyElement<V> {
503    type LayoutState = ();
504    type PaintState = ();
505
506    fn layout(
507        &mut self,
508        constraint: SizeConstraint,
509        view: &mut V,
510        cx: &mut LayoutContext<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_any(self) -> AnyElement<V>
553    where
554        Self: Sized,
555    {
556        self
557    }
558}
559
560pub struct RootElement<V: View> {
561    element: AnyElement<V>,
562    view: WeakViewHandle<V>,
563}
564
565impl<V: View> RootElement<V> {
566    pub fn new(element: AnyElement<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>) -> AnyElement<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>> Element<V> for ComponentHost<V, C> {
595    type LayoutState = AnyElement<V>;
596    type PaintState = ();
597
598    fn layout(
599        &mut self,
600        constraint: SizeConstraint,
601        view: &mut V,
602        cx: &mut LayoutContext<V>,
603    ) -> (Vector2F, AnyElement<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 AnyElement<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: &AnyElement<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: &AnyElement<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(
648        &mut self,
649        constraint: SizeConstraint,
650        new_parents: &mut HashMap<usize, usize>,
651        views_to_notify_if_ancestors_change: &mut HashSet<usize>,
652        refreshing: bool,
653        cx: &mut WindowContext,
654    ) -> Result<Vector2F>;
655    fn paint(
656        &mut self,
657        scene: &mut SceneBuilder,
658        origin: Vector2F,
659        visible_bounds: RectF,
660        cx: &mut WindowContext,
661    ) -> Result<()>;
662    fn rect_for_text_range(
663        &self,
664        range_utf16: Range<usize>,
665        cx: &WindowContext,
666    ) -> Result<Option<RectF>>;
667    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
668    fn name(&self) -> Option<&str>;
669}
670
671impl<V: View> AnyRootElement for RootElement<V> {
672    fn layout(
673        &mut self,
674        constraint: SizeConstraint,
675        new_parents: &mut HashMap<usize, usize>,
676        views_to_notify_if_ancestors_change: &mut HashSet<usize>,
677        refreshing: bool,
678        cx: &mut WindowContext,
679    ) -> Result<Vector2F> {
680        let view = self
681            .view
682            .upgrade(cx)
683            .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
684        view.update(cx, |view, cx| {
685            let mut cx = LayoutContext::new(
686                cx,
687                new_parents,
688                views_to_notify_if_ancestors_change,
689                refreshing,
690            );
691            Ok(self.element.layout(constraint, view, &mut cx))
692        })
693    }
694
695    fn paint(
696        &mut self,
697        scene: &mut SceneBuilder,
698        origin: Vector2F,
699        visible_bounds: RectF,
700        cx: &mut WindowContext,
701    ) -> Result<()> {
702        let view = self
703            .view
704            .upgrade(cx)
705            .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
706
707        view.update(cx, |view, cx| {
708            self.element.paint(scene, origin, visible_bounds, view, cx);
709            Ok(())
710        })
711    }
712
713    fn rect_for_text_range(
714        &self,
715        range_utf16: Range<usize>,
716        cx: &WindowContext,
717    ) -> Result<Option<RectF>> {
718        let view = self.view.upgrade(cx).ok_or_else(|| {
719            anyhow!("rect_for_text_range called on a root element for a dropped view")
720        })?;
721        let view = view.read(cx);
722        let view_context = ViewContext::immutable(cx, self.view.id());
723        Ok(self
724            .element
725            .rect_for_text_range(range_utf16, view, &view_context))
726    }
727
728    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
729        let view = self
730            .view
731            .upgrade(cx)
732            .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
733        let view = view.read(cx);
734        let view_context = ViewContext::immutable(cx, self.view.id());
735        Ok(serde_json::json!({
736            "view_id": self.view.id(),
737            "view_name": V::ui_name(),
738            "view": view.debug_json(cx),
739            "element": self.element.debug(view, &view_context)
740        }))
741    }
742
743    fn name(&self) -> Option<&str> {
744        self.element.name()
745    }
746}
747
748pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
749    fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
750        self.extend(children.into_iter().map(|child| child.into_any()));
751    }
752
753    fn add_child<D: Element<V>>(&mut self, child: D) {
754        self.extend(Some(child.into_any()));
755    }
756
757    fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
758        self.extend(children.into_iter().map(|child| child.into_any()));
759        self
760    }
761
762    fn with_child<D: Element<V>>(mut self, child: D) -> Self {
763        self.extend(Some(child.into_any()));
764        self
765    }
766}
767
768impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
769
770pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
771    if max_size.x().is_infinite() && max_size.y().is_infinite() {
772        size
773    } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
774        vec2f(size.x() * max_size.y() / size.y(), max_size.y())
775    } else {
776        vec2f(max_size.x(), size.y() * max_size.x() / size.x())
777    }
778}