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, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext,
 38    WeakViewHandle, 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 PaintContext<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(
303                    scene,
304                    bounds,
305                    visible_bounds,
306                    &mut layout,
307                    view,
308                    &mut PaintContext::new(cx),
309                );
310                ElementState::PostPaint {
311                    element,
312                    constraint,
313                    bounds,
314                    visible_bounds,
315                    layout,
316                    paint,
317                }
318            }
319            ElementState::PostPaint {
320                mut element,
321                constraint,
322                bounds,
323                mut layout,
324                ..
325            } => {
326                let bounds = RectF::new(origin, bounds.size());
327                let paint = element.paint(
328                    scene,
329                    bounds,
330                    visible_bounds,
331                    &mut layout,
332                    view,
333                    &mut PaintContext::new(cx),
334                );
335                ElementState::PostPaint {
336                    element,
337                    constraint,
338                    bounds,
339                    visible_bounds,
340                    layout,
341                    paint,
342                }
343            }
344            ElementState::Empty => panic!("invalid element lifecycle state"),
345            ElementState::Init { .. } => {
346                panic!("invalid element lifecycle state, paint called before layout")
347            }
348        }
349    }
350
351    fn rect_for_text_range(
352        &self,
353        range_utf16: Range<usize>,
354        view: &V,
355        cx: &ViewContext<V>,
356    ) -> Option<RectF> {
357        if let ElementState::PostPaint {
358            element,
359            bounds,
360            visible_bounds,
361            layout,
362            paint,
363            ..
364        } = self
365        {
366            element.rect_for_text_range(
367                range_utf16,
368                *bounds,
369                *visible_bounds,
370                layout,
371                paint,
372                view,
373                cx,
374            )
375        } else {
376            None
377        }
378    }
379
380    fn size(&self) -> Vector2F {
381        match self {
382            ElementState::Empty | ElementState::Init { .. } => {
383                panic!("invalid element lifecycle state")
384            }
385            ElementState::PostLayout { size, .. } => *size,
386            ElementState::PostPaint { bounds, .. } => bounds.size(),
387        }
388    }
389
390    fn metadata(&self) -> Option<&dyn Any> {
391        match self {
392            ElementState::Empty => unreachable!(),
393            ElementState::Init { element }
394            | ElementState::PostLayout { element, .. }
395            | ElementState::PostPaint { element, .. } => element.metadata(),
396        }
397    }
398
399    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
400        match self {
401            ElementState::PostPaint {
402                element,
403                constraint,
404                bounds,
405                visible_bounds,
406                layout,
407                paint,
408            } => {
409                let mut value = element.debug(*bounds, layout, paint, view, cx);
410                if let json::Value::Object(map) = &mut value {
411                    let mut new_map: crate::json::Map<String, serde_json::Value> =
412                        Default::default();
413                    if let Some(typ) = map.remove("type") {
414                        new_map.insert("type".into(), typ);
415                    }
416                    new_map.insert("constraint".into(), constraint.to_json());
417                    new_map.insert("bounds".into(), bounds.to_json());
418                    new_map.insert("visible_bounds".into(), visible_bounds.to_json());
419                    new_map.append(map);
420                    json::Value::Object(new_map)
421                } else {
422                    value
423                }
424            }
425
426            _ => panic!("invalid element lifecycle state"),
427        }
428    }
429}
430
431impl<V: View, E: Element<V>> Default for ElementState<V, E> {
432    fn default() -> Self {
433        Self::Empty
434    }
435}
436
437pub struct AnyElement<V: View> {
438    state: Box<dyn AnyElementState<V>>,
439    name: Option<Cow<'static, str>>,
440}
441
442impl<V: View> AnyElement<V> {
443    pub fn name(&self) -> Option<&str> {
444        self.name.as_deref()
445    }
446
447    pub fn metadata<T: 'static>(&self) -> Option<&T> {
448        self.state
449            .metadata()
450            .and_then(|data| data.downcast_ref::<T>())
451    }
452
453    pub fn layout(
454        &mut self,
455        constraint: SizeConstraint,
456        view: &mut V,
457        cx: &mut LayoutContext<V>,
458    ) -> Vector2F {
459        self.state.layout(constraint, view, cx)
460    }
461
462    pub fn paint(
463        &mut self,
464        scene: &mut SceneBuilder,
465        origin: Vector2F,
466        visible_bounds: RectF,
467        view: &mut V,
468        cx: &mut ViewContext<V>,
469    ) {
470        self.state.paint(scene, origin, visible_bounds, view, cx);
471    }
472
473    pub fn rect_for_text_range(
474        &self,
475        range_utf16: Range<usize>,
476        view: &V,
477        cx: &ViewContext<V>,
478    ) -> Option<RectF> {
479        self.state.rect_for_text_range(range_utf16, view, cx)
480    }
481
482    pub fn size(&self) -> Vector2F {
483        self.state.size()
484    }
485
486    pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
487        let mut value = self.state.debug(view, cx);
488
489        if let Some(name) = &self.name {
490            if let json::Value::Object(map) = &mut value {
491                let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
492                new_map.insert("name".into(), json::Value::String(name.to_string()));
493                new_map.append(map);
494                return json::Value::Object(new_map);
495            }
496        }
497
498        value
499    }
500
501    pub fn with_metadata<T, F, R>(&self, f: F) -> R
502    where
503        T: 'static,
504        F: FnOnce(Option<&T>) -> R,
505    {
506        f(self.state.metadata().and_then(|m| m.downcast_ref()))
507    }
508}
509
510impl<V: View> Element<V> for AnyElement<V> {
511    type LayoutState = ();
512    type PaintState = ();
513
514    fn layout(
515        &mut self,
516        constraint: SizeConstraint,
517        view: &mut V,
518        cx: &mut LayoutContext<V>,
519    ) -> (Vector2F, Self::LayoutState) {
520        let size = self.layout(constraint, view, cx);
521        (size, ())
522    }
523
524    fn paint(
525        &mut self,
526        scene: &mut SceneBuilder,
527        bounds: RectF,
528        visible_bounds: RectF,
529        _: &mut Self::LayoutState,
530        view: &mut V,
531        cx: &mut PaintContext<V>,
532    ) -> Self::PaintState {
533        self.paint(scene, bounds.origin(), visible_bounds, view, cx);
534    }
535
536    fn rect_for_text_range(
537        &self,
538        range_utf16: Range<usize>,
539        _: RectF,
540        _: RectF,
541        _: &Self::LayoutState,
542        _: &Self::PaintState,
543        view: &V,
544        cx: &ViewContext<V>,
545    ) -> Option<RectF> {
546        self.rect_for_text_range(range_utf16, view, cx)
547    }
548
549    fn debug(
550        &self,
551        _: RectF,
552        _: &Self::LayoutState,
553        _: &Self::PaintState,
554        view: &V,
555        cx: &ViewContext<V>,
556    ) -> serde_json::Value {
557        self.debug(view, cx)
558    }
559
560    fn into_any(self) -> AnyElement<V>
561    where
562        Self: Sized,
563    {
564        self
565    }
566}
567
568pub struct RootElement<V: View> {
569    element: AnyElement<V>,
570    view: WeakViewHandle<V>,
571}
572
573impl<V: View> RootElement<V> {
574    pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
575        Self { element, view }
576    }
577}
578
579pub trait AnyRootElement {
580    fn layout(
581        &mut self,
582        constraint: SizeConstraint,
583        new_parents: &mut HashMap<usize, usize>,
584        views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
585        refreshing: bool,
586        cx: &mut WindowContext,
587    ) -> Result<Vector2F>;
588    fn paint(
589        &mut self,
590        scene: &mut SceneBuilder,
591        origin: Vector2F,
592        visible_bounds: RectF,
593        cx: &mut WindowContext,
594    ) -> Result<()>;
595    fn rect_for_text_range(
596        &self,
597        range_utf16: Range<usize>,
598        cx: &WindowContext,
599    ) -> Result<Option<RectF>>;
600    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value>;
601    fn name(&self) -> Option<&str>;
602}
603
604impl<V: View> AnyRootElement for RootElement<V> {
605    fn layout(
606        &mut self,
607        constraint: SizeConstraint,
608        new_parents: &mut HashMap<usize, usize>,
609        views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
610        refreshing: bool,
611        cx: &mut WindowContext,
612    ) -> Result<Vector2F> {
613        let view = self
614            .view
615            .upgrade(cx)
616            .ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
617        view.update(cx, |view, cx| {
618            let mut cx = LayoutContext::new(
619                cx,
620                new_parents,
621                views_to_notify_if_ancestors_change,
622                refreshing,
623            );
624            Ok(self.element.layout(constraint, view, &mut cx))
625        })
626    }
627
628    fn paint(
629        &mut self,
630        scene: &mut SceneBuilder,
631        origin: Vector2F,
632        visible_bounds: RectF,
633        cx: &mut WindowContext,
634    ) -> Result<()> {
635        let view = self
636            .view
637            .upgrade(cx)
638            .ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
639
640        view.update(cx, |view, cx| {
641            self.element.paint(scene, origin, visible_bounds, view, cx);
642            Ok(())
643        })
644    }
645
646    fn rect_for_text_range(
647        &self,
648        range_utf16: Range<usize>,
649        cx: &WindowContext,
650    ) -> Result<Option<RectF>> {
651        let view = self.view.upgrade(cx).ok_or_else(|| {
652            anyhow!("rect_for_text_range called on a root element for a dropped view")
653        })?;
654        let view = view.read(cx);
655        let view_context = ViewContext::immutable(cx, self.view.id());
656        Ok(self
657            .element
658            .rect_for_text_range(range_utf16, view, &view_context))
659    }
660
661    fn debug(&self, cx: &WindowContext) -> Result<serde_json::Value> {
662        let view = self
663            .view
664            .upgrade(cx)
665            .ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
666        let view = view.read(cx);
667        let view_context = ViewContext::immutable(cx, self.view.id());
668        Ok(serde_json::json!({
669            "view_id": self.view.id(),
670            "view_name": V::ui_name(),
671            "view": view.debug_json(cx),
672            "element": self.element.debug(view, &view_context)
673        }))
674    }
675
676    fn name(&self) -> Option<&str> {
677        self.element.name()
678    }
679}
680
681pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
682    fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
683        self.extend(children.into_iter().map(|child| child.into_any()));
684    }
685
686    fn add_child<D: Element<V>>(&mut self, child: D) {
687        self.extend(Some(child.into_any()));
688    }
689
690    fn with_children<D: Element<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
691        self.extend(children.into_iter().map(|child| child.into_any()));
692        self
693    }
694
695    fn with_child<D: Element<V>>(mut self, child: D) -> Self {
696        self.extend(Some(child.into_any()));
697        self
698    }
699}
700
701impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
702
703pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
704    if max_size.x().is_infinite() && max_size.y().is_infinite() {
705        size
706    } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
707        vec2f(size.x() * max_size.y() / size.y(), max_size.y())
708    } else {
709        vec2f(max_size.x(), size.y() * max_size.x() / size.x())
710    }
711}