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