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