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