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