euv_ui/component/layout/hook/
impl.rs1use crate::*;
2
3impl UseEuvLayout {
7 pub fn use_resize() -> Signal<bool> {
18 let mobile_signal: Signal<bool> = App::use_signal(Router::is_mobile);
19 let timer_signal: Signal<Option<i32>> = App::use_signal(|| None);
20 let debounce_closure: Closure<dyn FnMut()> = Closure::wrap(Box::new(move || {
21 let mobile: bool = Router::is_mobile();
22 mobile_signal.set(mobile);
23 }));
24 let debounce_callback: Function = debounce_closure
25 .as_ref()
26 .unchecked_ref::<Function>()
27 .clone();
28 debounce_closure.forget();
29 let timeout_window: Window = window().expect("no global window exists");
30 App::use_window_event("resize", move || {
31 let old_timer: Option<i32> = timer_signal.get();
32 if let Some(timer_id) = old_timer {
33 timeout_window.clear_timeout_with_handle(timer_id);
34 }
35 let new_timer: i32 = timeout_window
36 .set_timeout_with_callback_and_timeout_and_arguments_0(
37 &debounce_callback,
38 RESIZE_DEBOUNCE_MILLIS,
39 )
40 .unwrap_or_default();
41 timer_signal.set(Some(new_timer));
42 });
43 mobile_signal
44 }
45
46 pub fn use_drawer_toggle(drawer_open: Signal<bool>) -> Option<Rc<dyn Fn(Event)>> {
62 Some(Rc::new(move |_: Event| {
63 let is_open: bool = drawer_open.get();
64 if is_open {
65 Router::overlay_stack_close();
66 }
67 drawer_open.set(!is_open);
68 }))
69 }
70
71 pub fn use_safe_area_fix() {
97 Self::cache_safe_area_insets();
98 App::use_window_event("fullscreenchange", || {
99 let is_fullscreen: bool = window()
100 .expect("no global window exists")
101 .document()
102 .expect("should have a document")
103 .fullscreen_element()
104 .is_some();
105 if is_fullscreen {
106 NATIVE_FULLSCREEN_ACTIVE.with(|flag: &Cell<bool>| flag.set(true));
107 Router::overlay_push_state();
108 } else {
109 let was_active: bool =
110 NATIVE_FULLSCREEN_ACTIVE.with(|flag: &Cell<bool>| flag.get());
111 if was_active {
112 NATIVE_FULLSCREEN_ACTIVE.with(|flag: &Cell<bool>| flag.set(false));
113 let exit_by_popstate: bool =
114 NATIVE_FULLSCREEN_EXIT_BY_POPSTATE.with(|flag: &Cell<bool>| flag.get());
115 if exit_by_popstate {
116 NATIVE_FULLSCREEN_EXIT_BY_POPSTATE
117 .with(|flag: &Cell<bool>| flag.set(false));
118 } else {
119 Router::overlay_back(None);
120 }
121 }
122 Self::apply_cached_insets();
123 }
124 });
125 App::use_window_event("webkitfullscreenchange", || {
126 Self::apply_cached_insets();
127 });
128 App::use_window_event("resize", || {
129 Self::apply_cached_insets();
130 });
131 Router::register_popstate_guard(Rc::new(|| {
132 if !NATIVE_FULLSCREEN_ACTIVE.with(|flag: &Cell<bool>| flag.get()) {
133 return false;
134 }
135 NATIVE_FULLSCREEN_EXIT_BY_POPSTATE.with(|flag: &Cell<bool>| flag.set(true));
136 let document_value: Document = window()
137 .expect("no global window exists")
138 .document()
139 .expect("should have a document");
140 document_value.exit_fullscreen();
141 true
142 }));
143 }
144
145 fn cache_safe_area_insets() {
157 let top_cached: String =
158 SAFE_AREA_INSET_TOP.with(|cell: &RefCell<String>| cell.borrow().clone());
159 if !top_cached.is_empty() {
160 return;
161 }
162 let win: Window = window().expect("no global window exists");
163 let document_value: Document = win.document().expect("should have a document");
164 let body: HtmlElement = document_value.body().expect("should have a body");
165 let sentinel: HtmlElement = document_value
166 .create_element("div")
167 .expect("should create div")
168 .unchecked_into();
169 let _ = sentinel.style().set_property("position", "absolute");
170 let _ = sentinel.style().set_property("visibility", "hidden");
171 let _ = sentinel.style().set_property("pointer-events", "none");
172 let _ = sentinel
173 .style()
174 .set_property("padding-top", "env(safe-area-inset-top, 0px)");
175 let _ = sentinel
176 .style()
177 .set_property("padding-right", "env(safe-area-inset-right, 0px)");
178 let _ = sentinel
179 .style()
180 .set_property("padding-bottom", "env(safe-area-inset-bottom, 0px)");
181 let _ = sentinel
182 .style()
183 .set_property("padding-left", "env(safe-area-inset-left, 0px)");
184 let _ = body.append_child(&sentinel);
185 let Some(computed) = win.get_computed_style(&sentinel).ok().flatten() else {
186 let _ = body.remove_child(&sentinel);
187 return;
188 };
189 let top_value: String = computed
190 .get_property_value("padding-top")
191 .unwrap_or_default();
192 let right_value: String = computed
193 .get_property_value("padding-right")
194 .unwrap_or_default();
195 let bottom_value: String = computed
196 .get_property_value("padding-bottom")
197 .unwrap_or_default();
198 let left_value: String = computed
199 .get_property_value("padding-left")
200 .unwrap_or_default();
201 let _ = body.remove_child(&sentinel);
202 if top_value.is_empty() || top_value == "0px" {
203 return;
204 }
205 SAFE_AREA_INSET_TOP.with(|cell: &RefCell<String>| *cell.borrow_mut() = top_value);
206 SAFE_AREA_INSET_RIGHT.with(|cell: &RefCell<String>| *cell.borrow_mut() = right_value);
207 SAFE_AREA_INSET_BOTTOM.with(|cell: &RefCell<String>| *cell.borrow_mut() = bottom_value);
208 SAFE_AREA_INSET_LEFT.with(|cell: &RefCell<String>| *cell.borrow_mut() = left_value);
209 }
210
211 pub fn apply_cached_insets() {
226 let top_value: String =
227 SAFE_AREA_INSET_TOP.with(|cell: &RefCell<String>| cell.borrow().clone());
228 if top_value.is_empty() {
229 return;
230 }
231 let right_value: String =
232 SAFE_AREA_INSET_RIGHT.with(|cell: &RefCell<String>| cell.borrow().clone());
233 let bottom_value: String =
234 SAFE_AREA_INSET_BOTTOM.with(|cell: &RefCell<String>| cell.borrow().clone());
235 let left_value: String =
236 SAFE_AREA_INSET_LEFT.with(|cell: &RefCell<String>| cell.borrow().clone());
237 let document_value: Document = window()
238 .expect("no global window exists")
239 .document()
240 .expect("should have a document");
241 let apply_to = |element: &HtmlElement| {
242 let _ = element
243 .style()
244 .set_property("--safe-area-inset-top", &top_value);
245 let _ = element
246 .style()
247 .set_property("--safe-area-inset-right", &right_value);
248 let _ = element
249 .style()
250 .set_property("--safe-area-inset-bottom", &bottom_value);
251 let _ = element
252 .style()
253 .set_property("--safe-area-inset-left", &left_value);
254 };
255 if let Some(app_root) = document_value
256 .query_selector(".c_mobile_app_root")
257 .ok()
258 .flatten()
259 .map(|element: Element| element.unchecked_into::<HtmlElement>())
260 .or_else(|| {
261 document_value
262 .query_selector(".c_app_root")
263 .ok()
264 .flatten()
265 .map(|element: Element| element.unchecked_into::<HtmlElement>())
266 })
267 {
268 apply_to(&app_root);
269 }
270 if let Some(canvas_fullscreen) = document_value
271 .query_selector(".c_canvas_container_fullscreen")
272 .ok()
273 .flatten()
274 .map(|element: Element| element.unchecked_into::<HtmlElement>())
275 {
276 apply_to(&canvas_fullscreen);
277 }
278 }
279}