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