1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/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};
41pub mod cmd;
42
43#[derive(Default)]
67#[non_exhaustive]
68pub struct WindowManager {}
69impl AppExtension for WindowManager {
70 fn init(&mut self) {
71 #[cfg(feature = "image")]
72 zng_ext_image::IMAGES_WINDOW.hook_render_windows_service(Box::new(WINDOWS));
73 }
74
75 fn event_preview(&mut self, update: &mut EventUpdate) {
76 MonitorsService::on_pre_event(update);
77 WINDOWS::on_pre_event(update);
78 }
79
80 fn event_ui(&mut self, update: &mut EventUpdate) {
81 WINDOWS::on_ui_event(update);
82 }
83
84 fn event(&mut self, update: &mut EventUpdate) {
85 WINDOWS::on_event(update);
86 }
87
88 fn update_ui(&mut self, update_widgets: &mut WidgetUpdates) {
89 WINDOWS::on_ui_update(update_widgets);
90 }
91
92 fn update(&mut self) {
93 WINDOWS::on_update();
94 }
95
96 fn info(&mut self, info_widgets: &mut InfoUpdates) {
97 WINDOWS::on_info(info_widgets);
98 }
99
100 fn layout(&mut self, layout_widgets: &mut LayoutUpdates) {
101 WINDOWS::on_layout(layout_widgets);
102 }
103
104 fn render(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
105 WINDOWS::on_render(render_widgets, render_update_widgets);
106 }
107}
108
109pub trait AppRunWindowExt {
114 fn run_window<F>(self, new_window: impl IntoFuture<IntoFuture = F>)
161 where
162 F: Future<Output = WindowRoot> + Send + 'static;
163}
164impl<E: AppExtension> AppRunWindowExt for AppExtended<E> {
165 fn run_window<F>(self, new_window: impl IntoFuture<IntoFuture = F>)
166 where
167 F: Future<Output = WindowRoot> + Send + 'static,
168 {
169 let new_window = new_window.into_future();
170 self.run(async move {
171 WINDOWS.open(new_window);
172 })
173 }
174}
175
176pub trait HeadlessAppWindowExt {
181 fn open_window<F>(&mut self, new_window: impl IntoFuture<IntoFuture = F>) -> WindowId
191 where
192 F: Future<Output = WindowRoot> + Send + 'static;
193
194 fn focus_window(&mut self, window_id: WindowId);
196 fn blur_window(&mut self, window_id: WindowId);
198
199 #[cfg(feature = "image")]
203 fn window_frame_image(&mut self, window_id: WindowId, mask: Option<zng_view_api::image::ImageMaskMode>) -> zng_ext_image::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 #[cfg(feature = "image")]
234 let frame_rcv = FRAME_IMAGE_READY_EVENT.receiver();
235 zng_task::any!(
236 async {
237 while let Ok(args) = close_rcv.recv_async().await {
238 if args.windows.contains(&window_id) {
239 break;
240 }
241 }
242 },
243 async {
244 #[cfg(feature = "image")]
245 while let Ok(args) = frame_rcv.recv_async().await {
246 if args.window_id == window_id {
247 break;
248 }
249 }
250 }
251 )
252 .await;
253 }
254 window_id
255 })
256 .unwrap()
257 }
258
259 fn focus_window(&mut self, window_id: WindowId) {
260 let args = RawWindowFocusArgs::now(None, Some(window_id));
261 RAW_WINDOW_FOCUS_EVENT.notify(args);
262 let _ = self.update(false);
263 }
264
265 fn blur_window(&mut self, window_id: WindowId) {
266 let args = RawWindowFocusArgs::now(Some(window_id), None);
267 RAW_WINDOW_FOCUS_EVENT.notify(args);
268 let _ = self.update(false);
269 }
270
271 #[cfg(feature = "image")]
272 fn window_frame_image(&mut self, window_id: WindowId, mask: Option<zng_view_api::image::ImageMaskMode>) -> zng_ext_image::ImageVar {
273 WINDOWS.frame_image(window_id, mask)
274 }
275
276 fn close_window(&mut self, window_id: WindowId) -> bool {
277 use zng_app::view_process::raw_events::*;
278
279 let args = RawWindowCloseRequestedArgs::now(window_id);
280 RAW_WINDOW_CLOSE_REQUESTED_EVENT.notify(args);
281
282 let mut requested = false;
283 let mut closed = false;
284
285 let _ = self.update_observe_event(
286 |update| {
287 if let Some(args) = WINDOW_CLOSE_REQUESTED_EVENT.on(update) {
288 requested |= args.windows.contains(&window_id);
289 } else if let Some(args) = WINDOW_CLOSE_EVENT.on(update) {
290 closed |= args.windows.contains(&window_id);
291 }
292 },
293 false,
294 );
295
296 assert_eq!(requested, closed);
297
298 closed
299 }
300
301 fn run_window<F>(&mut self, new_window: impl IntoFuture<IntoFuture = F>)
302 where
303 F: Future<Output = WindowRoot> + Send + 'static,
304 {
305 let window_id = self.open_window(new_window);
306 while WINDOWS.is_open(window_id) {
307 if let AppControlFlow::Exit = self.update(true) {
308 return;
309 }
310 }
311 }
312
313 #[cfg(any(test, doc, feature = "test_util"))]
314 fn doc_test_window<F>(&mut self, new_window: impl IntoFuture<IntoFuture = F>)
315 where
316 F: Future<Output = WindowRoot> + Send + 'static,
317 {
318 use zng_layout::unit::TimeUnits;
319
320 let timer = zng_app::timer::TIMERS.deadline(60.secs());
321
322 zng_task::spawn(async {
323 zng_task::deadline(65.secs()).await;
324 eprintln!("doc_test_window reached 65s fallback deadline");
325 zng_env::exit(-1);
326 });
327 let window_id = self.open_window(new_window);
328
329 while WINDOWS.is_open(window_id) {
330 if let AppControlFlow::Exit = self.update(true) {
331 return;
332 }
333 if timer.get().has_elapsed() {
334 panic!("doc_test_window reached 60s deadline");
335 }
336 }
337 }
338}