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}