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