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