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