1use super::*;
2use crate::ffi::{FlexDirection, PositionType, Unit};
3use crate::node::portal;
4use crate::popup_presenter::{PopupPlacement, PopupPresenter};
5use std::cell::Cell;
6
7#[derive(Clone)]
8pub struct Popup {
9 root: FlexBox,
10 surface_node: FlexBox,
11 presenter: PopupPresenter,
12 dismiss_on_backdrop_click: Rc<Cell<bool>>,
13 appearance_value: Rc<RefCell<Option<PopupAppearance>>>,
14}
15
16impl Default for Popup {
17 fn default() -> Self {
18 Self::new()
19 }
20}
21
22impl Popup {
23 pub fn new() -> Self {
24 let root = portal();
25 let surface_node = flex_box();
26 surface_node
27 .position_type(PositionType::Absolute)
28 .flex_direction(FlexDirection::Column);
29 let presenter = PopupPresenter::new(root.clone(), surface_node.clone());
30 root.position_type(PositionType::Absolute)
31 .position(0.0, 0.0)
32 .width(100.0, Unit::Percent)
33 .height(100.0, Unit::Percent);
34 let dismiss_on_backdrop_click = Rc::new(Cell::new(true));
35 let dismiss_flag = dismiss_on_backdrop_click.clone();
36 let presenter_target = presenter.event_target();
37 presenter
38 .overlay_node()
39 .interactive(true)
40 .on_pointer_click(move |_event| {
41 if dismiss_flag.get() {
42 presenter_target.hide();
43 }
44 });
45 Self {
46 root,
47 surface_node,
48 presenter,
49 dismiss_on_backdrop_click,
50 appearance_value: Rc::new(RefCell::new(None)),
51 }
52 }
53
54 pub fn is_open(&self) -> bool {
55 self.presenter.is_open()
56 }
57
58 pub fn surface(&self) -> FlexBox {
61 self.surface_node.clone()
62 }
63
64 pub fn placement(&self, value: PopupPlacement) -> &Self {
65 self.presenter.placement(value);
66 self
67 }
68
69 pub fn edge_padding(&self, value: f32) -> &Self {
70 self.presenter.edge_padding(value);
71 self
72 }
73
74 pub fn anchor_gap(&self, value: f32) -> &Self {
75 self.presenter.anchor_gap(value);
76 self
77 }
78
79 pub fn dismiss_on_backdrop_click(&self, flag: bool) -> &Self {
80 self.dismiss_on_backdrop_click.set(flag);
81 self
82 }
83
84 pub fn appearance(&self, appearance: PopupAppearance) -> &Self {
85 self.appearance_value.replace(Some(appearance));
86 self.sync_appearance();
87 self
88 }
89
90 pub fn clear_appearance(&self) -> &Self {
91 self.appearance_value.replace(None);
92 self.sync_appearance();
93 self
94 }
95
96 fn sync_appearance(&self) {
97 let appearance = self.appearance_value.borrow().clone().unwrap_or_default();
98 let panel = appearance.panel.unwrap_or_default();
99 let backdrop = appearance.backdrop.unwrap_or_default();
100 self.surface_node
101 .apply_presenter_style(panel.presenter_host_style())
102 .background_blur(panel.background_blur.unwrap_or(0.0));
103 self.presenter
104 .backdrop_color(backdrop.color.unwrap_or(0x00000000))
105 .background_blur(backdrop.blur.unwrap_or(0.0));
106 }
107
108 pub fn show_anchored(
109 &self,
110 anchor_x: f32,
111 anchor_y: f32,
112 anchor_width: f32,
113 anchor_height: f32,
114 width: f32,
115 height: f32,
116 ) {
117 self.surface_node.width(width, Unit::Pixel);
118 self.surface_node.height(height, Unit::Pixel);
119 self.presenter.show_anchored(
120 anchor_x,
121 anchor_y,
122 anchor_width,
123 anchor_height,
124 width,
125 height,
126 );
127 }
128
129 pub fn show_at_point(&self, x: f32, y: f32, width: f32, height: f32) {
130 self.surface_node.width(width, Unit::Pixel);
131 self.surface_node.height(height, Unit::Pixel);
132 self.presenter.show_at_point(x, y, width, height);
133 }
134
135 pub fn hide(&self) {
136 self.presenter.hide();
137 }
138}
139
140impl Node for Popup {
141 fn retained_node_ref(&self) -> NodeRef {
142 self.root.retained_node_ref()
143 }
144
145 fn build_self(&self) {
146 self.root.build_self();
147 }
148
149 fn dispose(&self) {
150 self.presenter.dispose();
151 self.root.dispose();
152 }
153}
154
155impl crate::node::HasFlexBoxRoot for Popup {
156 fn flex_box_root(&self) -> &FlexBox {
157 &self.root
158 }
159
160 fn append_flex_box_surface_child(&self, child: NodeRef) {
161 self.surface_node
162 .retained_node_ref()
163 .append_child_ref(&child);
164 }
165}
166
167impl crate::node::ThemeBindable for Popup {
168 fn theme_binding_node(&self) -> NodeRef {
169 self.root.retained_node_ref()
170 }
171
172 fn weak_theme_target(&self) -> Box<dyn Fn() -> Option<Self>> {
173 let root = self.root.downgrade();
174 let surface_node = self.surface_node.downgrade();
175 let presenter = self.presenter.downgrade();
176 let dismiss_on_backdrop_click = Rc::downgrade(&self.dismiss_on_backdrop_click);
177 let appearance_value = self.appearance_value.clone();
180 Box::new(move || {
181 Some(Self {
182 root: root.upgrade()?,
183 surface_node: surface_node.upgrade()?,
184 presenter: presenter.upgrade()?,
185 dismiss_on_backdrop_click: dismiss_on_backdrop_click.upgrade()?,
186 appearance_value: appearance_value.clone(),
187 })
188 })
189 }
190}