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}