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