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    move_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            move_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 move_to_fit(mut self, align_to_fit: bool) -> Self {
 33        self.move_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.move_to_fit {
 80            // Snap the right edge of the overlay to the right edge of the window if
 81            // its horizontal bounds overflow.
 82            if bounds.lower_right().x() > cx.window_size.x() {
 83                bounds.set_origin_x((cx.window_size.x() - bounds.width()).max(0.));
 84            }
 85
 86            // Snap the bottom edge of the overlay to the bottom edge of the window if
 87            // its vertical bounds overflow.
 88            if bounds.lower_right().y() > cx.window_size.y() {
 89                bounds.set_origin_y((cx.window_size.y() - bounds.height()).max(0.));
 90            }
 91        }
 92
 93        self.child.paint(bounds.origin(), bounds, cx);
 94        cx.scene.pop_stacking_context();
 95    }
 96
 97    fn dispatch_event(
 98        &mut self,
 99        event: &Event,
100        _: RectF,
101        _: RectF,
102        _: &mut Self::LayoutState,
103        _: &mut Self::PaintState,
104        cx: &mut EventContext,
105    ) -> bool {
106        self.child.dispatch_event(event, cx)
107    }
108
109    fn debug(
110        &self,
111        _: RectF,
112        _: &Self::LayoutState,
113        _: &Self::PaintState,
114        cx: &DebugContext,
115    ) -> serde_json::Value {
116        json!({
117            "type": "Overlay",
118            "abs_position": self.abs_position.to_json(),
119            "child": self.child.debug(cx),
120        })
121    }
122}