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