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,
 37};
 38use core::panic;
 39use json::ToJson;
 40use std::{any::Any, borrow::Cow, marker::PhantomData, mem, ops::Range};
 41
 42trait AnyElement<V: View> {
 43    fn layout(
 44        &mut self,
 45        constraint: SizeConstraint,
 46        view: &mut V,
 47        cx: &mut ViewContext<V>,
 48    ) -> Vector2F;
 49
 50    fn paint(
 51        &mut self,
 52        scene: &mut SceneBuilder,
 53        origin: Vector2F,
 54        visible_bounds: RectF,
 55        view: &mut V,
 56        cx: &mut ViewContext<V>,
 57    );
 58
 59    fn rect_for_text_range(
 60        &self,
 61        range_utf16: Range<usize>,
 62        view: &V,
 63        cx: &ViewContext<V>,
 64    ) -> Option<RectF>;
 65
 66    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value;
 67
 68    fn size(&self) -> Vector2F;
 69
 70    fn metadata(&self) -> Option<&dyn Any>;
 71}
 72
 73pub trait Element<V: View> {
 74    type LayoutState;
 75    type PaintState;
 76
 77    fn layout(
 78        &mut self,
 79        constraint: SizeConstraint,
 80        view: &mut V,
 81        cx: &mut ViewContext<V>,
 82    ) -> (Vector2F, Self::LayoutState);
 83
 84    fn paint(
 85        &mut self,
 86        scene: &mut SceneBuilder,
 87        bounds: RectF,
 88        visible_bounds: RectF,
 89        layout: &mut Self::LayoutState,
 90        view: &mut V,
 91        cx: &mut ViewContext<V>,
 92    ) -> Self::PaintState;
 93
 94    fn rect_for_text_range(
 95        &self,
 96        range_utf16: Range<usize>,
 97        bounds: RectF,
 98        visible_bounds: RectF,
 99        layout: &Self::LayoutState,
100        paint: &Self::PaintState,
101        view: &V,
102        cx: &ViewContext<V>,
103    ) -> Option<RectF>;
104
105    fn metadata(&self) -> Option<&dyn Any> {
106        None
107    }
108
109    fn debug(
110        &self,
111        bounds: RectF,
112        layout: &Self::LayoutState,
113        paint: &Self::PaintState,
114        view: &V,
115        cx: &ViewContext<V>,
116    ) -> serde_json::Value;
117
118    fn boxed(self) -> ElementBox<V>
119    where
120        Self: 'static + Sized,
121    {
122        ElementBox {
123            element: Box::new(Lifecycle::Init { element: self }),
124            view_type: PhantomData,
125            name: None,
126        }
127    }
128
129    fn named(self, name: impl Into<Cow<'static, str>>) -> ElementBox<V>
130    where
131        Self: 'static + Sized,
132    {
133        ElementBox {
134            element: Box::new(Lifecycle::Init { element: self }),
135            view_type: PhantomData,
136            name: Some(name.into()),
137        }
138    }
139
140    fn constrained(self) -> ConstrainedBox<V>
141    where
142        Self: 'static + Sized,
143    {
144        ConstrainedBox::new(self.boxed())
145    }
146
147    fn aligned(self) -> Align<V>
148    where
149        Self: 'static + Sized,
150    {
151        Align::new(self.boxed())
152    }
153
154    fn clipped(self) -> Clipped<V>
155    where
156        Self: 'static + Sized,
157    {
158        Clipped::new(self.boxed())
159    }
160
161    fn contained(self) -> Container<V>
162    where
163        Self: 'static + Sized,
164    {
165        Container::new(self.boxed())
166    }
167
168    fn expanded(self) -> Expanded<V>
169    where
170        Self: 'static + Sized,
171    {
172        Expanded::new(self.boxed())
173    }
174
175    fn flex(self, flex: f32, expanded: bool) -> FlexItem<V>
176    where
177        Self: 'static + Sized,
178    {
179        FlexItem::new(self.boxed()).flex(flex, expanded)
180    }
181
182    fn flex_float(self) -> FlexItem<V>
183    where
184        Self: 'static + Sized,
185    {
186        FlexItem::new(self.boxed()).float()
187    }
188
189    fn with_tooltip<Tag: 'static>(
190        self,
191        id: usize,
192        text: String,
193        action: Option<Box<dyn Action>>,
194        style: TooltipStyle,
195        cx: &mut ViewContext<V>,
196    ) -> Tooltip<V>
197    where
198        Self: 'static + Sized,
199    {
200        Tooltip::new::<Tag, V>(id, text, action, style, self.boxed(), cx)
201    }
202
203    fn with_resize_handle<Tag: 'static>(
204        self,
205        element_id: usize,
206        side: Side,
207        handle_size: f32,
208        initial_size: f32,
209        cx: &mut ViewContext<V>,
210    ) -> Resizable<V>
211    where
212        Self: 'static + Sized,
213    {
214        Resizable::new::<Tag, V>(
215            self.boxed(),
216            element_id,
217            side,
218            handle_size,
219            initial_size,
220            cx,
221        )
222    }
223}
224
225pub enum Lifecycle<V: View, E: Element<V>> {
226    Empty,
227    Init {
228        element: E,
229    },
230    PostLayout {
231        element: E,
232        constraint: SizeConstraint,
233        size: Vector2F,
234        layout: E::LayoutState,
235    },
236    PostPaint {
237        element: E,
238        constraint: SizeConstraint,
239        bounds: RectF,
240        visible_bounds: RectF,
241        layout: E::LayoutState,
242        paint: E::PaintState,
243    },
244}
245
246impl<V: View, E: Element<V>> AnyElement<V> for Lifecycle<V, E> {
247    fn layout(
248        &mut self,
249        constraint: SizeConstraint,
250        view: &mut V,
251        cx: &mut ViewContext<V>,
252    ) -> Vector2F {
253        let result;
254        *self = match mem::take(self) {
255            Lifecycle::Empty => unreachable!(),
256            Lifecycle::Init { mut element }
257            | Lifecycle::PostLayout { mut element, .. }
258            | Lifecycle::PostPaint { mut element, .. } => {
259                let (size, layout) = element.layout(constraint, view, cx);
260                debug_assert!(size.x().is_finite());
261                debug_assert!(size.y().is_finite());
262
263                result = size;
264                Lifecycle::PostLayout {
265                    element,
266                    constraint,
267                    size,
268                    layout,
269                }
270            }
271        };
272        result
273    }
274
275    fn paint(
276        &mut self,
277        scene: &mut SceneBuilder,
278        origin: Vector2F,
279        visible_bounds: RectF,
280        view: &mut V,
281        cx: &mut ViewContext<V>,
282    ) {
283        *self = match mem::take(self) {
284            Lifecycle::PostLayout {
285                mut element,
286                constraint,
287                size,
288                mut layout,
289            } => {
290                let bounds = RectF::new(origin, size);
291                let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
292                Lifecycle::PostPaint {
293                    element,
294                    constraint,
295                    bounds,
296                    visible_bounds,
297                    layout,
298                    paint,
299                }
300            }
301            Lifecycle::PostPaint {
302                mut element,
303                constraint,
304                bounds,
305                mut layout,
306                ..
307            } => {
308                let bounds = RectF::new(origin, bounds.size());
309                let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
310                Lifecycle::PostPaint {
311                    element,
312                    constraint,
313                    bounds,
314                    visible_bounds,
315                    layout,
316                    paint,
317                }
318            }
319            Lifecycle::Empty => panic!("invalid element lifecycle state"),
320            Lifecycle::Init { .. } => {
321                panic!("invalid element lifecycle state, paint called before layout")
322            }
323        }
324    }
325
326    fn rect_for_text_range(
327        &self,
328        range_utf16: Range<usize>,
329        view: &V,
330        cx: &ViewContext<V>,
331    ) -> Option<RectF> {
332        if let Lifecycle::PostPaint {
333            element,
334            bounds,
335            visible_bounds,
336            layout,
337            paint,
338            ..
339        } = self
340        {
341            element.rect_for_text_range(
342                range_utf16,
343                *bounds,
344                *visible_bounds,
345                layout,
346                paint,
347                view,
348                cx,
349            )
350        } else {
351            None
352        }
353    }
354
355    fn size(&self) -> Vector2F {
356        match self {
357            Lifecycle::Empty | Lifecycle::Init { .. } => panic!("invalid element lifecycle state"),
358            Lifecycle::PostLayout { size, .. } => *size,
359            Lifecycle::PostPaint { bounds, .. } => bounds.size(),
360        }
361    }
362
363    fn metadata(&self) -> Option<&dyn Any> {
364        match self {
365            Lifecycle::Empty => unreachable!(),
366            Lifecycle::Init { element }
367            | Lifecycle::PostLayout { element, .. }
368            | Lifecycle::PostPaint { element, .. } => element.metadata(),
369        }
370    }
371
372    fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
373        match self {
374            Lifecycle::PostPaint {
375                element,
376                constraint,
377                bounds,
378                visible_bounds,
379                layout,
380                paint,
381            } => {
382                let mut value = element.debug(*bounds, layout, paint, view, cx);
383                if let json::Value::Object(map) = &mut value {
384                    let mut new_map: crate::json::Map<String, serde_json::Value> =
385                        Default::default();
386                    if let Some(typ) = map.remove("type") {
387                        new_map.insert("type".into(), typ);
388                    }
389                    new_map.insert("constraint".into(), constraint.to_json());
390                    new_map.insert("bounds".into(), bounds.to_json());
391                    new_map.insert("visible_bounds".into(), visible_bounds.to_json());
392                    new_map.append(map);
393                    json::Value::Object(new_map)
394                } else {
395                    value
396                }
397            }
398
399            _ => panic!("invalid element lifecycle state"),
400        }
401    }
402}
403
404impl<V: View, E: Element<V>> Default for Lifecycle<V, E> {
405    fn default() -> Self {
406        Self::Empty
407    }
408}
409
410pub struct ElementBox<V: View> {
411    element: Box<dyn AnyElement<V>>,
412    view_type: PhantomData<V>,
413    name: Option<Cow<'static, str>>,
414}
415
416impl<V: View> ElementBox<V> {
417    pub fn name(&self) -> Option<&str> {
418        self.name.as_deref()
419    }
420
421    pub fn metadata<T: 'static>(&self) -> Option<&T> {
422        self.element
423            .metadata()
424            .and_then(|data| data.downcast_ref::<T>())
425    }
426
427    pub fn layout(
428        &mut self,
429        constraint: SizeConstraint,
430        view: &mut V,
431        cx: &mut ViewContext<V>,
432    ) -> Vector2F {
433        self.element.layout(constraint, view, cx)
434    }
435
436    pub fn paint(
437        &mut self,
438        scene: &mut SceneBuilder,
439        origin: Vector2F,
440        visible_bounds: RectF,
441        view: &mut V,
442        cx: &mut ViewContext<V>,
443    ) {
444        self.element.paint(scene, origin, visible_bounds, view, cx);
445    }
446
447    pub fn rect_for_text_range(
448        &self,
449        range_utf16: Range<usize>,
450        view: &V,
451        cx: &ViewContext<V>,
452    ) -> Option<RectF> {
453        self.element.rect_for_text_range(range_utf16, view, cx)
454    }
455
456    pub fn size(&self) -> Vector2F {
457        self.element.size()
458    }
459
460    pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
461        let mut value = self.element.debug(view, cx);
462
463        if let Some(name) = &self.name {
464            if let json::Value::Object(map) = &mut value {
465                let mut new_map: crate::json::Map<String, serde_json::Value> = Default::default();
466                new_map.insert("name".into(), json::Value::String(name.to_string()));
467                new_map.append(map);
468                return json::Value::Object(new_map);
469            }
470        }
471
472        value
473    }
474
475    pub fn with_metadata<T, F, R>(&self, f: F) -> R
476    where
477        T: 'static,
478        F: FnOnce(Option<&T>) -> R,
479    {
480        f(self.element.metadata().and_then(|m| m.downcast_ref()))
481    }
482}
483
484pub trait ParentElement<'a, V: View>: Extend<ElementBox<V>> + Sized {
485    fn add_children(&mut self, children: impl IntoIterator<Item = ElementBox<V>>) {
486        self.extend(children);
487    }
488
489    fn add_child(&mut self, child: ElementBox<V>) {
490        self.add_children(Some(child));
491    }
492
493    fn with_children(mut self, children: impl IntoIterator<Item = ElementBox<V>>) -> Self {
494        self.add_children(children);
495        self
496    }
497
498    fn with_child(self, child: ElementBox<V>) -> Self {
499        self.with_children(Some(child))
500    }
501}
502
503impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<ElementBox<V>> {}
504
505pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
506    if max_size.x().is_infinite() && max_size.y().is_infinite() {
507        size
508    } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
509        vec2f(size.x() * max_size.y() / size.y(), max_size.y())
510    } else {
511        vec2f(max_size.x(), size.y() * max_size.x() / size.x())
512    }
513}