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