zng_ext_window/
lib.rs
1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11#![expect(clippy::type_complexity)]
13
14#[macro_use]
15extern crate bitflags;
16
17mod control;
18pub use control::{NestedWindowNode, NestedWindowWidgetInfoExt, OpenNestedHandlerArgs};
19
20mod ime;
21pub use ime::*;
22
23mod types;
24pub use types::*;
25
26mod monitor;
27pub use monitor::*;
28
29mod vars;
30pub use vars::*;
31
32mod service;
33pub use service::*;
34
35use zng_app::{
36 AppControlFlow, AppExtended, AppExtension, HeadlessApp,
37 update::{EventUpdate, InfoUpdates, LayoutUpdates, RenderUpdates, WidgetUpdates},
38 view_process::raw_events::{RAW_WINDOW_FOCUS_EVENT, RawWindowFocusArgs},
39 window::WindowId,
40};
41use zng_ext_image::{IMAGES_WINDOW, ImageVar};
42use zng_view_api::image::ImageMaskMode;
43
44pub mod cmd;
45
46#[derive(Default)]
70pub struct WindowManager {}
71impl AppExtension for WindowManager {
72 fn init(&mut self) {
73 IMAGES_WINDOW.hook_render_windows_service(Box::new(WINDOWS));
74 }
75
76 fn event_preview(&mut self, update: &mut EventUpdate) {
77 MonitorsService::on_pre_event(update);
78 WINDOWS::on_pre_event(update);
79 }
80
81 fn event_ui(&mut self, update: &mut EventUpdate) {
82 WINDOWS::on_ui_event(update);
83 }
84
85 fn event(&mut self, update: &mut EventUpdate) {
86 WINDOWS::on_event(update);
87 }
88
89 fn update_ui(&mut self, update_widgets: &mut WidgetUpdates) {
90 WINDOWS::on_ui_update(update_widgets);
91 }
92
93 fn update(&mut self) {
94 WINDOWS::on_update();
95 }
96
97 fn info(&mut self, info_widgets: &mut InfoUpdates) {
98 WINDOWS::on_info(info_widgets);
99 }
100
101 fn layout(&mut self, layout_widgets: &mut LayoutUpdates) {
102 WINDOWS::on_layout(layout_widgets);
103 }
104
105 fn render(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
106 WINDOWS::on_render(render_widgets, render_update_widgets);
107 }
108}
109
110pub trait AppRunWindowExt {
115 fn run_window<F>(self, new_window: impl IntoFuture<IntoFuture = F>)
162 where
163 F: Future<Output = WindowRoot> + Send + 'static;
164}
165impl<E: AppExtension> AppRunWindowExt for AppExtended<E> {
166 fn run_window<F>(self, new_window: impl IntoFuture<IntoFuture = F>)
167 where
168 F: Future<Output = WindowRoot> + Send + 'static,
169 {
170 let new_window = new_window.into_future();
171 self.run(async move {
172 WINDOWS.open(new_window);
173 })
174 }
175}
176
177pub trait HeadlessAppWindowExt {
182 fn open_window<F>(&mut self, new_window: impl IntoFuture<IntoFuture = F>) -> WindowId
192 where
193 F: Future<Output = WindowRoot> + Send + 'static;
194
195 fn focus_window(&mut self, window_id: WindowId);
197 fn blur_window(&mut self, window_id: WindowId);
199
200 fn window_frame_image(&mut self, window_id: WindowId, mask: Option<ImageMaskMode>) -> ImageVar;
204
205 fn close_window(&mut self, window_id: WindowId) -> bool;
209
210 fn run_window<F>(&mut self, new_window: impl IntoFuture<IntoFuture = F>)
212 where
213 F: Send + Future<Output = WindowRoot> + 'static;
214
215 #[cfg(any(test, doc, feature = "test_util"))]
217 fn doc_test_window<F>(&mut self, new_window: impl IntoFuture<IntoFuture = F>)
218 where
219 F: Future<Output = WindowRoot> + 'static + Send;
220}
221impl HeadlessAppWindowExt for HeadlessApp {
222 fn open_window<F>(&mut self, new_window: impl IntoFuture<IntoFuture = F>) -> WindowId
223 where
224 F: Future<Output = WindowRoot> + Send + 'static,
225 {
226 zng_app::APP.extensions().require::<WindowManager>();
227
228 let response = WINDOWS.open(new_window);
229 self.run_task(async move {
230 let window_id = response.wait_rsp().await;
231 if !WINDOWS.is_loaded(window_id) {
232 let close_rcv = WINDOW_CLOSE_EVENT.receiver();
233 let frame_rcv = FRAME_IMAGE_READY_EVENT.receiver();
234 zng_task::any!(
235 async {
236 while let Ok(args) = close_rcv.recv_async().await {
237 if args.windows.contains(&window_id) {
238 break;
239 }
240 }
241 },
242 async {
243 while let Ok(args) = frame_rcv.recv_async().await {
244 if args.window_id == window_id {
245 break;
246 }
247 }
248 }
249 )
250 .await;
251 }
252 window_id
253 })
254 .unwrap()
255 }
256
257 fn focus_window(&mut self, window_id: WindowId) {
258 let args = RawWindowFocusArgs::now(None, Some(window_id));
259 RAW_WINDOW_FOCUS_EVENT.notify(args);
260 let _ = self.update(false);
261 }
262
263 fn blur_window(&mut self, window_id: WindowId) {
264 let args = RawWindowFocusArgs::now(Some(window_id), None);
265 RAW_WINDOW_FOCUS_EVENT.notify(args);
266 let _ = self.update(false);
267 }
268
269 fn window_frame_image(&mut self, window_id: WindowId, mask: Option<ImageMaskMode>) -> ImageVar {
270 WINDOWS.frame_image(window_id, mask)
271 }
272
273 fn close_window(&mut self, window_id: WindowId) -> bool {
274 use zng_app::view_process::raw_events::*;
275
276 let args = RawWindowCloseRequestedArgs::now(window_id);
277 RAW_WINDOW_CLOSE_REQUESTED_EVENT.notify(args);
278
279 let mut requested = false;
280 let mut closed = false;
281
282 let _ = self.update_observe_event(
283 |update| {
284 if let Some(args) = WINDOW_CLOSE_REQUESTED_EVENT.on(update) {
285 requested |= args.windows.contains(&window_id);
286 } else if let Some(args) = WINDOW_CLOSE_EVENT.on(update) {
287 closed |= args.windows.contains(&window_id);
288 }
289 },
290 false,
291 );
292
293 assert_eq!(requested, closed);
294
295 closed
296 }
297
298 fn run_window<F>(&mut self, new_window: impl IntoFuture<IntoFuture = F>)
299 where
300 F: Future<Output = WindowRoot> + Send + 'static,
301 {
302 let window_id = self.open_window(new_window);
303 while WINDOWS.is_open(window_id) {
304 if let AppControlFlow::Exit = self.update(true) {
305 return;
306 }
307 }
308 }
309
310 #[cfg(any(test, doc, feature = "test_util"))]
311 fn doc_test_window<F>(&mut self, new_window: impl IntoFuture<IntoFuture = F>)
312 where
313 F: Future<Output = WindowRoot> + Send + 'static,
314 {
315 use zng_layout::unit::TimeUnits;
316 use zng_var::Var;
317 let timer = zng_app::timer::TIMERS.deadline(60.secs());
318
319 zng_task::spawn(async {
320 zng_task::deadline(65.secs()).await;
321 eprintln!("doc_test_window reached 65s fallback deadline");
322 zng_env::exit(-1);
323 });
324 let window_id = self.open_window(new_window);
325
326 while WINDOWS.is_open(window_id) {
327 if let AppControlFlow::Exit = self.update(true) {
328 return;
329 }
330 if timer.get().has_elapsed() {
331 panic!("doc_test_window reached 60s deadline");
332 }
333 }
334 }
335}