overlay.rs

  1use serde_json::json;
  2
  3use crate::{
  4    geometry::{rect::RectF, vector::Vector2F},
  5    json::ToJson,
  6    DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MouseRegion,
  7    PaintContext, SizeConstraint,
  8};
  9
 10pub struct Overlay {
 11    child: ElementBox,
 12    abs_position: Option<Vector2F>,
 13    align_to_fit: bool,
 14    hoverable: bool,
 15}
 16
 17impl Overlay {
 18    pub fn new(child: ElementBox) -> Self {
 19        Self {
 20            child,
 21            abs_position: None,
 22            align_to_fit: false,
 23            hoverable: false,
 24        }
 25    }
 26
 27    pub fn with_abs_position(mut self, position: Vector2F) -> Self {
 28        self.abs_position = Some(position);
 29        self
 30    }
 31
 32    pub fn align_to_fit(mut self, align_to_fit: bool) -> Self {
 33        self.align_to_fit = align_to_fit;
 34        self
 35    }
 36
 37    pub fn hoverable(mut self, hoverable: bool) -> Self {
 38        self.hoverable = hoverable;
 39        self
 40    }
 41}
 42
 43impl Element for Overlay {
 44    type LayoutState = Vector2F;
 45    type PaintState = ();
 46
 47    fn layout(
 48        &mut self,
 49        constraint: SizeConstraint,
 50        cx: &mut LayoutContext,
 51    ) -> (Vector2F, Self::LayoutState) {
 52        let constraint = if self.abs_position.is_some() {
 53            SizeConstraint::new(Vector2F::zero(), cx.window_size)
 54        } else {
 55            constraint
 56        };
 57        let size = self.child.layout(constraint, cx);
 58        (Vector2F::zero(), size)
 59    }
 60
 61    fn paint(
 62        &mut self,
 63        bounds: RectF,
 64        _: RectF,
 65        size: &mut Self::LayoutState,
 66        cx: &mut PaintContext,
 67    ) {
 68        let mut bounds = RectF::new(self.abs_position.unwrap_or(bounds.origin()), *size);
 69        cx.scene.push_stacking_context(None);
 70
 71        if self.hoverable {
 72            cx.scene.push_mouse_region(MouseRegion {
 73                view_id: cx.current_view_id(),
 74                bounds,
 75                ..Default::default()
 76            });
 77        }
 78
 79        if self.align_to_fit {
 80            // Align overlay to the left if its bounds overflow the window width.
 81            if bounds.lower_right().x() > cx.window_size.x() {
 82                bounds.set_origin_x(bounds.origin_x() - bounds.width());
 83            }
 84
 85            // Align overlay to the top if its bounds overflow the window height.
 86            if bounds.lower_right().y() > cx.window_size.y() {
 87                bounds.set_origin_y(bounds.origin_y() - bounds.height());
 88            }
 89        }
 90
 91        self.child.paint(bounds.origin(), bounds, cx);
 92        cx.scene.pop_stacking_context();
 93    }
 94
 95    fn dispatch_event(
 96        &mut self,
 97        event: &Event,
 98        _: RectF,
 99        _: RectF,
100        _: &mut Self::LayoutState,
101        _: &mut Self::PaintState,
102        cx: &mut EventContext,
103    ) -> bool {
104        self.child.dispatch_event(event, cx)
105    }
106
107    fn debug(
108        &self,
109        _: RectF,
110        _: &Self::LayoutState,
111        _: &Self::PaintState,
112        cx: &DebugContext,
113    ) -> serde_json::Value {
114        json!({
115            "type": "Overlay",
116            "abs_position": self.abs_position.to_json(),
117            "child": self.child.debug(cx),
118        })
119    }
120}