elements.rs

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