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