overlay.rs

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