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