elements.rs

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