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