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