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