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