zng_ext_window/
service.rs

1use std::{mem, sync::Arc};
2
3use parking_lot::Mutex;
4use zng_app::{
5    APP, AppEventSender, Deadline, EXIT_REQUESTED_EVENT,
6    event::AnyEventArgs,
7    hn_once,
8    timer::{DeadlineHandle, TIMERS},
9    update::{EventUpdate, InfoUpdates, LayoutUpdates, RenderUpdates, UPDATES, WidgetUpdates},
10    view_process::{
11        self, VIEW_PROCESS, VIEW_PROCESS_INITED_EVENT, ViewWindowOrHeadless,
12        raw_events::{
13            RAW_CHROME_CONFIG_CHANGED_EVENT, RAW_COLORS_CONFIG_CHANGED_EVENT, RAW_WINDOW_CLOSE_EVENT, RAW_WINDOW_CLOSE_REQUESTED_EVENT,
14            RAW_WINDOW_FOCUS_EVENT,
15        },
16    },
17    widget::{
18        UiTaskWidget, WidgetId,
19        base::PARALLEL_VAR,
20        info::{InteractionPath, WidgetInfo, WidgetInfoTree},
21        node::UiNode,
22    },
23    window::{WINDOW, WindowCtx, WindowId, WindowMode},
24};
25use zng_app_context::app_local;
26
27use zng_color::{COLOR_SCHEME_VAR, colors::ACCENT_COLOR_VAR};
28use zng_layout::unit::FactorUnits;
29use zng_layout::unit::TimeUnits as _;
30use zng_task::{
31    ParallelIteratorExt, UiTask,
32    rayon::iter::{IntoParallelRefMutIterator, ParallelIterator},
33};
34use zng_txt::{ToTxt as _, Txt, formatx};
35use zng_unique_id::{IdMap, IdSet};
36use zng_var::{ResponderVar, ResponseVar, Var, const_var, impl_from_and_into_var, response_done_var, response_var, var, var_default};
37use zng_view_api::{
38    DragDropId,
39    api_extension::{ApiExtensionId, ApiExtensionPayload},
40    config::{ChromeConfig, ColorsConfig},
41    drag_drop::{DragDropData, DragDropEffect, DragDropError},
42    window::{RenderMode, WindowState},
43};
44use zng_wgt::node::with_context_var;
45
46use crate::{
47    CloseWindowResult, MONITORS, ViewExtensionError, WINDOW_CLOSE_EVENT, WINDOW_CLOSE_REQUESTED_EVENT, WINDOW_FOCUS_CHANGED_EVENT,
48    WINDOW_LOAD_EVENT, WINDOW_VARS_ID, WindowCloseArgs, WindowCloseRequestedArgs, WindowFocusChangedArgs, WindowLoadingHandle,
49    WindowManager, WindowNotFoundError, WindowOpenArgs, WindowRoot, WindowVars, cmd::WindowCommands, control::WindowCtrl,
50};
51
52#[cfg(feature = "image")]
53use std::any::Any;
54
55#[cfg(feature = "image")]
56use zng_app::view_process::{
57    ViewImage, ViewRenderer,
58    raw_events::{RAW_IMAGE_LOAD_ERROR_EVENT, RAW_IMAGE_LOADED_EVENT},
59};
60
61#[cfg(feature = "image")]
62use zng_ext_image::{ImageRenderWindowRoot, ImageRenderWindowsService, ImageVar, Img};
63
64#[cfg(feature = "image")]
65use crate::{FRAME_IMAGE_READY_EVENT, FrameCaptureMode, HeadlessMonitor, StartPosition};
66
67#[cfg(feature = "image")]
68use zng_view_api::{image::ImageMaskMode, ipc::ViewChannelError};
69
70#[cfg(feature = "image")]
71use zng_var::WeakVar;
72
73#[cfg(feature = "image")]
74use zng_layout::unit::{Factor, LengthUnits, PxRect};
75
76app_local! {
77    pub(super) static WINDOWS_SV: WindowsService = {
78        APP.extensions().require::<WindowManager>();
79        WindowsService::new()
80    };
81    static FOCUS_SV: Var<Option<InteractionPath>> = const_var(None);
82}
83pub(super) struct WindowsService {
84    exit_on_last_close: Var<bool>,
85    default_render_mode: Var<RenderMode>,
86    parallel: Var<ParallelWin>,
87    system_chrome: Var<ChromeConfig>,
88    root_extenders: Mutex<Vec<Box<dyn FnMut(WindowRootExtenderArgs) -> UiNode + Send>>>, // Mutex for +Sync only.
89    open_nested_handlers: Mutex<Vec<Box<dyn FnMut(&mut crate::OpenNestedHandlerArgs) + Send>>>,
90
91    windows: IdMap<WindowId, AppWindow>,
92    windows_info: IdMap<WindowId, AppWindowInfo>,
93
94    open_loading: IdMap<WindowId, WindowLoading>,
95    open_requests: Vec<OpenWindowRequest>,
96    open_tasks: Vec<AppWindowTask>,
97
98    close_requests: Vec<CloseWindowRequest>,
99    close_responders: IdMap<WindowId, Vec<ResponderVar<CloseWindowResult>>>,
100    exit_on_close: bool,
101
102    focus_request: Option<WindowId>,
103    bring_to_top_requests: Vec<WindowId>,
104
105    #[cfg(feature = "image")]
106    frame_images: Vec<WeakVar<Img>>,
107
108    loading_deadline: Option<DeadlineHandle>,
109    latest_colors_cfg: ColorsConfig,
110
111    view_window_tasks: Vec<ViewWindowTask>,
112}
113impl WindowsService {
114    fn new() -> Self {
115        Self {
116            exit_on_last_close: var(true),
117            default_render_mode: var_default(),
118            root_extenders: Mutex::new(vec![]),
119            open_nested_handlers: Mutex::new(vec![]),
120            system_chrome: var_default(),
121            parallel: var_default(),
122            windows: IdMap::default(),
123            windows_info: IdMap::default(),
124            open_loading: IdMap::new(),
125            open_tasks: vec![],
126            open_requests: Vec::with_capacity(1),
127            exit_on_close: false,
128            close_responders: IdMap::default(),
129            close_requests: vec![],
130            focus_request: None,
131            bring_to_top_requests: vec![],
132            #[cfg(feature = "image")]
133            frame_images: vec![],
134            loading_deadline: None,
135            latest_colors_cfg: ColorsConfig::default(),
136            view_window_tasks: vec![],
137        }
138    }
139
140    fn open_impl(&mut self, id: WindowId, new_window: UiTask<WindowRoot>, force_headless: Option<WindowMode>) -> ResponseVar<WindowId> {
141        let (responder, response) = response_var();
142        let request = OpenWindowRequest {
143            id,
144            new: Mutex::new(new_window),
145            force_headless,
146            responder,
147        };
148        self.open_requests.push(request);
149        self.open_loading.insert(id, WindowLoading::new());
150        UPDATES.update(None);
151
152        response
153    }
154
155    fn loading_handle_impl(&mut self, window_id: WindowId, deadline: Deadline) -> Option<WindowLoadingHandle> {
156        let mut handle = None;
157
158        if let Some(info) = self.windows_info.get_mut(&window_id) {
159            // window already opened, check if not loaded
160            if !info.is_loaded {
161                handle = Some(info.loading_handle.new_handle(UPDATES.sender(), deadline))
162            }
163
164            // drop timer to nearest deadline, will recreate in the next update.
165            self.loading_deadline = None;
166        } else if let Some(h) = self.open_loading.get_mut(&window_id) {
167            // window not opened yet
168            handle = Some(h.new_handle(UPDATES.sender(), deadline));
169        }
170
171        handle
172    }
173
174    fn close_together(
175        &mut self,
176        windows: impl IntoIterator<Item = WindowId>,
177    ) -> Result<ResponseVar<CloseWindowResult>, WindowNotFoundError> {
178        let mut group = IdSet::default();
179
180        for w in windows {
181            if !self.windows_info.contains_key(&w) {
182                return Err(WindowNotFoundError::new(w));
183            }
184            group.insert(w);
185        }
186
187        if group.is_empty() {
188            return Ok(response_done_var(CloseWindowResult::Cancel));
189        }
190
191        let (responder, response) = response_var();
192        self.close_requests.push(CloseWindowRequest { responder, windows: group });
193        UPDATES.update(None);
194
195        Ok(response)
196    }
197
198    #[cfg(feature = "image")]
199    fn frame_image_impl(
200        &mut self,
201        window_id: WindowId,
202        action: impl FnOnce(&ViewRenderer) -> std::result::Result<ViewImage, ViewChannelError>,
203    ) -> ImageVar {
204        if let Some(w) = self.windows_info.get(&window_id) {
205            if let Some(r) = &w.view {
206                match action(&r.renderer()) {
207                    Ok(img) => {
208                        let img = Img::new(img);
209                        let img = var(img);
210                        self.frame_images.retain(|i| i.strong_count() > 0);
211                        self.frame_images.push(img.downgrade());
212                        img.read_only()
213                    }
214                    Err(_) => var(Img::dummy(Some(formatx!("{}", WindowNotFoundError::new(window_id))))).read_only(),
215                }
216            } else {
217                var(Img::dummy(Some(formatx!("window `{window_id}` is headless without renderer")))).read_only()
218            }
219        } else {
220            var(Img::dummy(Some(formatx!("{}", WindowNotFoundError::new(window_id))))).read_only()
221        }
222    }
223
224    fn view_window_task(&mut self, window_id: WindowId, task: impl FnOnce(Option<&view_process::ViewWindow>) + Send + 'static) {
225        self.view_window_tasks.push(ViewWindowTask {
226            window_id,
227            task: Mutex::new(Box::new(task)),
228        });
229    }
230
231    fn take_requests(
232        &mut self,
233    ) -> (
234        Vec<OpenWindowRequest>,
235        Vec<AppWindowTask>,
236        Vec<CloseWindowRequest>,
237        Option<WindowId>,
238        Vec<WindowId>,
239        Vec<ViewWindowTask>,
240    ) {
241        (
242            mem::take(&mut self.open_requests),
243            mem::take(&mut self.open_tasks),
244            mem::take(&mut self.close_requests),
245            self.focus_request.take(),
246            mem::take(&mut self.bring_to_top_requests),
247            mem::take(&mut self.view_window_tasks),
248        )
249    }
250}
251
252bitflags! {
253    /// Defines what window operations can run in parallel, between windows.
254    ///
255    /// Note that this does not define parallelism inside the window, see [`WINDOWS.parallel`] for more details.
256    ///
257    /// [`WINDOWS.parallel`]: WINDOWS::parallel
258    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
259    #[serde(transparent)]
260    pub struct ParallelWin: u8 {
261        /// Windows can init, deinit, update and rebuild info in parallel.
262        const UPDATE = 0b0001;
263        /// Windows can handle event updates in parallel.
264        const EVENT = 0b0010;
265        /// Windows can layout in parallel.
266        const LAYOUT = 0b0100;
267        /// Windows with pending render or render update generate display lists in parallel.
268        const RENDER = 0b1000;
269    }
270}
271impl Default for ParallelWin {
272    /// Is all by default.
273    fn default() -> Self {
274        Self::all()
275    }
276}
277impl_from_and_into_var! {
278    fn from(all: bool) -> ParallelWin {
279        if all { ParallelWin::all() } else { ParallelWin::empty() }
280    }
281}
282
283/// Windows service.
284///
285/// # Provider
286///
287/// This service is provided by the [`WindowManager`] extension, it will panic if used in an app not extended.
288pub struct WINDOWS;
289impl WINDOWS {
290    /// Defines if app process exit should be requested when the last window closes. This is `true` by default.
291    ///
292    /// This setting does not consider headless windows and is fully ignored in headless apps.
293    ///
294    /// Note that if [`APP.exit`](APP::exit) is requested directly the windows service will cancel it, request
295    /// close for all headed and headless windows, and if all windows close request app exit again, independent
296    /// of this setting.
297    pub fn exit_on_last_close(&self) -> Var<bool> {
298        WINDOWS_SV.read().exit_on_last_close.clone()
299    }
300
301    /// Defines the render mode of windows opened by this service.
302    ///
303    /// Note that this setting only affects windows opened after it is changed, also the view-process may select
304    /// a different render mode if it cannot support the requested mode.
305    pub fn default_render_mode(&self) -> Var<RenderMode> {
306        WINDOWS_SV.read().default_render_mode.clone()
307    }
308
309    /// Defines what window operations can run in parallel, between windows.
310    ///
311    /// Note that this config is for parallel execution between windows, see the `parallel` property for parallel execution
312    /// within windows and widgets.
313    ///
314    /// See [`ParallelWin`] for the options.
315    pub fn parallel(&self) -> Var<ParallelWin> {
316        WINDOWS_SV.read().parallel.clone()
317    }
318
319    /// Requests a new window.
320    ///
321    /// The `new_window` future runs in an [`UiTask`] inside the new [`WINDOW`] context.
322    ///
323    /// Returns a response variable that will update once when the window is opened, note that while the [`WINDOW`] is
324    /// available in the `new_window` argument already, the window is only available in this service after
325    /// the returned variable updates. Also note that the window might not be fully [loaded] yet.
326    ///
327    /// An update cycle is processed between the end of `new_window` and the window init, this means that you
328    /// can use the context [`WINDOW`] to set variables that will be read on init with the new value.
329    ///
330    /// [loaded]: Self::is_loaded
331    /// [`UiTask`]: zng_task::UiTask
332    /// [`WINDOW`]: zng_app::window::WINDOW
333    pub fn open<F: Future<Output = WindowRoot> + Send + 'static>(
334        &self,
335        new_window: impl IntoFuture<IntoFuture = F>,
336    ) -> ResponseVar<WindowId> {
337        WINDOWS_SV
338            .write()
339            .open_impl(WindowId::new_unique(), UiTask::new(None, new_window), None)
340    }
341
342    /// Requests a new window with pre-defined ID.
343    ///
344    /// # Panics
345    ///
346    /// If the `window_id` is already assigned to an open or opening window.
347    pub fn open_id<F: Future<Output = WindowRoot> + Send + 'static>(
348        &self,
349        window_id: impl Into<WindowId>,
350        new_window: impl IntoFuture<IntoFuture = F>,
351    ) -> ResponseVar<WindowId> {
352        let window_id = window_id.into();
353        self.assert_id_unused(window_id);
354        WINDOWS_SV.write().open_impl(window_id, UiTask::new(None, new_window), None)
355    }
356
357    /// Requests a new headless window.
358    ///
359    /// Headless windows don't show on screen, but if `with_renderer` is `true` they will still render frames.
360    ///
361    /// Note that in a headless app the [`open`] method also creates headless windows, this method
362    /// creates headless windows even in a headed app.
363    ///
364    /// [`open`]: WINDOWS::open
365    pub fn open_headless<F: Future<Output = WindowRoot> + Send + 'static>(
366        &self,
367        new_window: impl IntoFuture<IntoFuture = F>,
368        with_renderer: bool,
369    ) -> ResponseVar<WindowId> {
370        WINDOWS_SV.write().open_impl(
371            WindowId::new_unique(),
372            UiTask::new(None, new_window),
373            Some(if with_renderer {
374                WindowMode::HeadlessWithRenderer
375            } else {
376                WindowMode::Headless
377            }),
378        )
379    }
380
381    /// Requests a new headless window with pre-defined ID.
382    ///
383    /// # Panics
384    ///
385    /// If the `window_id` is already assigned to an open or opening window.
386    pub fn open_headless_id<F: Future<Output = WindowRoot> + Send + 'static>(
387        &self,
388        window_id: impl Into<WindowId>,
389        new_window: impl IntoFuture<IntoFuture = F>,
390        with_renderer: bool,
391    ) -> ResponseVar<WindowId> {
392        let window_id = window_id.into();
393        self.assert_id_unused(window_id);
394        WINDOWS_SV.write().open_impl(
395            window_id,
396            UiTask::new(None, new_window),
397            Some(if with_renderer {
398                WindowMode::HeadlessWithRenderer
399            } else {
400                WindowMode::Headless
401            }),
402        )
403    }
404
405    #[track_caller]
406    fn assert_id_unused(&self, id: WindowId) {
407        let w = WINDOWS_SV.read();
408        if w.windows_info.contains_key(&id) || w.open_loading.contains_key(&id) {
409            panic!("window id `{id:?}` is already in use")
410        }
411    }
412
413    /// Gets a handle that stops the window from loading while the handle is alive.
414    ///
415    /// A window is only opened in the view-process after it is loaded, without any loading handles the window is considered loaded
416    /// after the first layout pass. Nodes in the window can request a loading handle to delay the view opening to after all async resources
417    /// it requires are loaded.
418    ///
419    /// Note that a window is only loaded after all handles are dropped or expired, you should set a reasonable `deadline`,  
420    /// it is best to partially render a window after a short time than not show anything.
421    ///
422    /// Returns `None` if the window has already loaded or is not found.
423    pub fn loading_handle(&self, window_id: impl Into<WindowId>, deadline: impl Into<Deadline>) -> Option<WindowLoadingHandle> {
424        WINDOWS_SV.write().loading_handle_impl(window_id.into(), deadline.into())
425    }
426
427    /// Starts closing a window, the operation can be canceled by listeners of
428    /// [`WINDOW_CLOSE_REQUESTED_EVENT`]. If the window has children they are closed together.
429    ///
430    /// Returns a response var that will update once with the result of the operation.
431    ///
432    /// Returns an error if the `window_id` is not one of the open windows or is only an open request.
433    pub fn close(&self, window_id: impl Into<WindowId>) -> Result<ResponseVar<CloseWindowResult>, WindowNotFoundError> {
434        self.close_together([window_id.into()])
435    }
436
437    /// Starts closing multiple windows together, the operation can be canceled by listeners of
438    /// [`WINDOW_CLOSE_REQUESTED_EVENT`]. If canceled none of the windows are closed. Children of each window
439    /// are also selected the close together.
440    ///
441    /// Returns a response var that will update once with the result of the operation. Returns
442    /// [`Cancel`] if `windows` is empty.
443    ///
444    /// Returns an error if any of the IDs is not one of the open windows or is only an open request.
445    ///
446    /// [`Cancel`]: CloseWindowResult::Cancel
447    pub fn close_together(
448        &self,
449        windows: impl IntoIterator<Item = WindowId>,
450    ) -> Result<ResponseVar<CloseWindowResult>, WindowNotFoundError> {
451        WINDOWS_SV.write().close_together(windows)
452    }
453
454    /// Starts closing all open windows together, the operation can be canceled by listeners of
455    /// [`WINDOW_CLOSE_REQUESTED_EVENT`]. If canceled none of the windows are closed.
456    ///
457    /// Returns a response var that will update once with the result of the operation. Returns
458    /// [`Cancel`] if no window is open.
459    ///
460    /// [`Cancel`]: CloseWindowResult::Cancel
461    pub fn close_all(&self) -> ResponseVar<CloseWindowResult> {
462        let set: Vec<_> = WINDOWS_SV.read().windows_info.keys().copied().collect();
463        self.close_together(set).unwrap()
464    }
465
466    /// Get the window [mode].
467    ///
468    /// This value indicates if the window is headless or not.
469    ///
470    /// Returns an error if the `window_id` is not one of the open windows or is only an open request.
471    ///
472    /// [mode]: WindowMode
473    pub fn mode(&self, window_id: impl Into<WindowId>) -> Result<WindowMode, WindowNotFoundError> {
474        let window_id = window_id.into();
475        WINDOWS_SV
476            .read()
477            .windows_info
478            .get(&window_id)
479            .map(|w| w.mode)
480            .ok_or(WindowNotFoundError::new(window_id))
481    }
482
483    /// Returns a shared reference to the latest widget tree info for the window.
484    ///
485    /// Returns an error if the `window_id` is not one of the open windows or is only an open request.
486    pub fn widget_tree(&self, window_id: impl Into<WindowId>) -> Result<WidgetInfoTree, WindowNotFoundError> {
487        let window_id = window_id.into();
488        WINDOWS_SV
489            .read()
490            .windows_info
491            .get(&window_id)
492            .map(|w| w.widget_tree.clone())
493            .ok_or(WindowNotFoundError::new(window_id))
494    }
495
496    /// Search for the widget info in all windows.
497    pub fn widget_info(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetInfo> {
498        let widget_id = widget_id.into();
499        WINDOWS_SV.read().windows_info.values().find_map(|w| w.widget_tree.get(widget_id))
500    }
501
502    /// Generate an image from the current rendered frame of the window.
503    ///
504    /// The image is not loaded at the moment of return, it will update when it is loaded.
505    ///
506    /// If the window is not found the error is reported in the [image error].
507    ///
508    /// [image error]: zng_ext_image::Img::error
509    #[cfg(feature = "image")]
510    pub fn frame_image(&self, window_id: impl Into<WindowId>, mask: Option<ImageMaskMode>) -> ImageVar {
511        let window_id = window_id.into();
512        if let Some((win, wgt)) = self.nest_parent(window_id) {
513            if let Ok(tree) = self.widget_tree(win)
514                && let Some(wgt) = tree.get(wgt)
515            {
516                return WINDOWS_SV
517                    .write()
518                    .frame_image_impl(win, |vr| vr.frame_image_rect(wgt.inner_bounds(), mask));
519            }
520            tracing::error!("did not find nest parent {win:?}//.../{wgt:?}, will capture parent window frame")
521        }
522        WINDOWS_SV.write().frame_image_impl(window_id, move |vr| vr.frame_image(mask))
523    }
524
525    /// Generate an image from a rectangular selection of the current rendered frame of the window.
526    ///
527    /// The image is not loaded at the moment of return, it will update when it is loaded.
528    ///
529    /// If the window is not found the error is reported in the image error.
530    ///
531    /// [image error]: zng_ext_image::Img::error
532    #[cfg(feature = "image")]
533    pub fn frame_image_rect(&self, window_id: impl Into<WindowId>, mut rect: PxRect, mask: Option<ImageMaskMode>) -> ImageVar {
534        let mut window_id = window_id.into();
535        if let Some((win, wgt)) = self.nest_parent(window_id) {
536            if let Ok(tree) = self.widget_tree(win)
537                && let Some(wgt) = tree.get(wgt)
538            {
539                window_id = win;
540                let bounds = wgt.inner_bounds();
541                rect.origin += bounds.origin.to_vector();
542                rect = rect.intersection(&bounds).unwrap_or_default();
543            }
544            if window_id != win {
545                tracing::error!("did not find nest parent {win:?}//.../{wgt:?}, will capture parent window frame")
546            }
547        }
548        WINDOWS_SV.write().frame_image_impl(window_id, |vr| vr.frame_image_rect(rect, mask))
549    }
550
551    /// Returns a shared reference the variables that control the window.
552    ///
553    /// Returns an error if the `window_id` is not one of the open windows or is only an open request.
554    pub fn vars(&self, window_id: impl Into<WindowId>) -> Result<WindowVars, WindowNotFoundError> {
555        let window_id = window_id.into();
556        WINDOWS_SV
557            .read()
558            .windows_info
559            .get(&window_id)
560            .map(|w| w.vars.clone())
561            .ok_or(WindowNotFoundError::new(window_id))
562    }
563
564    /// Gets if the window is focused in the operating system.
565    ///
566    /// Returns an error if the `window_id` is not one of the open windows, returns `false` if the `window_id` is
567    /// one of the open requests.
568    pub fn is_focused(&self, window_id: impl Into<WindowId>) -> Result<bool, WindowNotFoundError> {
569        let window_id = window_id.into();
570        let w = WINDOWS_SV.read();
571        if let Some(w) = w.windows_info.get(&window_id) {
572            Ok(w.is_focused)
573        } else if w.open_loading.contains_key(&window_id) {
574            Ok(false)
575        } else {
576            Err(WindowNotFoundError::new(window_id))
577        }
578    }
579
580    /// Returns shared references to the widget trees of each open window.
581    pub fn widget_trees(&self) -> Vec<WidgetInfoTree> {
582        WINDOWS_SV.read().windows_info.values().map(|w| w.widget_tree.clone()).collect()
583    }
584
585    /// Gets the id of the window that is focused in the operating system.
586    pub fn focused_window_id(&self) -> Option<WindowId> {
587        WINDOWS_SV.read().windows_info.values().find(|w| w.is_focused).map(|w| w.id)
588    }
589
590    /// Returns a shared reference to the focused window's info.
591    pub fn focused_info(&self) -> Option<WidgetInfoTree> {
592        WINDOWS_SV
593            .read()
594            .windows_info
595            .values()
596            .find(|w| w.is_focused)
597            .map(|w| w.widget_tree.clone())
598    }
599
600    /// Returns `true` if the window [`open`] task has completed.
601    ///
602    /// Note that the window may not be fully [loaded] yet, or actually open in the the view-process.
603    ///
604    /// [`open`]: WINDOWS::open
605    /// [loaded]: WINDOWS::is_loaded
606    pub fn is_open(&self, window_id: impl Into<WindowId>) -> bool {
607        WINDOWS_SV.read().windows_info.contains_key(&window_id.into())
608    }
609
610    /// Returns `true` if the `window_id` is associated with a pending window open request or open task.
611    ///
612    /// Window open requests start polling after each update.
613    pub fn is_opening(&self, window_id: impl Into<WindowId>) -> bool {
614        let window_id = window_id.into();
615        let sv = WINDOWS_SV.read();
616        sv.open_loading.contains_key(&window_id)
617    }
618
619    /// Returns `true` if the window is not open or has pending loading handles.
620    pub fn is_loading(&self, window_id: impl Into<WindowId>) -> bool {
621        let window_id = window_id.into();
622        let sv = WINDOWS_SV.read();
623        sv.open_loading.contains_key(&window_id) || sv.windows_info.get(&window_id).map(|i| !i.is_loaded).unwrap_or(false)
624    }
625
626    /// Returns `true` if the window is open and has no pending loading handles.
627    ///
628    /// See [`loading_handle`] for more details.
629    ///
630    /// [`loading_handle`]: WINDOWS::loading_handle
631    pub fn is_loaded(&self, window_id: impl Into<WindowId>) -> bool {
632        let window_id = window_id.into();
633        WINDOWS_SV.read().windows_info.get(&window_id).map(|i| i.is_loaded).unwrap_or(false)
634    }
635
636    /// Wait until the window is loaded or closed.
637    ///
638    /// If `wait_event` is `true` also awaits for the [`WINDOW_LOAD_EVENT`] to finish notifying.
639    ///
640    /// Returns `true` if the window iis open and has no pending loading handles.
641    pub fn wait_loaded(&self, window_id: impl Into<WindowId>, wait_event: bool) -> impl Future<Output = bool> + Send + Sync + 'static {
642        Self::wait_loaded_impl(window_id.into(), wait_event)
643    }
644    async fn wait_loaded_impl(window_id: WindowId, wait_event: bool) -> bool {
645        if Self.is_loaded(window_id) {
646            if wait_event {
647                // unlikely, but it can just have loaded and the event is ongoing.
648                zng_task::yield_now().await;
649            }
650            return true;
651        }
652
653        // start receiving before loading check otherwise could load after check and before receiver creation.
654        let recv = WINDOW_LOAD_EVENT.receiver();
655        while Self.is_loading(window_id) {
656            while let Ok(msg) = zng_task::with_deadline(recv.recv_async(), 1.secs()).await {
657                if let Ok(args) = msg
658                    && args.window_id == window_id
659                {
660                    if wait_event {
661                        zng_task::yield_now().await;
662                    }
663                    return true;
664                }
665            }
666            // deadline, rare case window closes before load
667        }
668
669        if Self.is_loaded(window_id) {
670            if wait_event {
671                zng_task::yield_now().await;
672            }
673            return true;
674        }
675        false
676    }
677
678    /// Request operating system focus for the window.
679    ///
680    /// The window will be made active and steal keyboard focus from the current focused window.
681    ///
682    /// Prefer using the `FOCUS` service and advanced `FocusRequest` configs instead of using this method directly, they integrate
683    /// with the in app widget focus and internally still use this method.
684    ///
685    /// If the `window_id` is only associated with an open request it is modified to focus the window on open.
686    /// If more than one focus request is made in the same update cycle only the last request is processed.
687    pub fn focus(&self, window_id: impl Into<WindowId>) -> Result<(), WindowNotFoundError> {
688        let window_id = window_id.into();
689        if !self.is_focused(window_id)? {
690            let mut w = WINDOWS_SV.write();
691            w.focus_request = Some(window_id);
692            UPDATES.update(None);
693        }
694        Ok(())
695    }
696
697    /// Focus a window if it is open or opening, otherwise opens it focused.
698    pub fn focus_or_open(
699        &self,
700        window_id: impl Into<WindowId>,
701        open: impl Future<Output = WindowRoot> + Send + 'static,
702    ) -> Option<ResponseVar<WindowId>> {
703        let window_id = window_id.into();
704        if self.focus(window_id).is_ok() {
705            None
706        } else {
707            let r = self.open_id(window_id, async move {
708                let w = open.await;
709                // keep the request as close to the actual open as possible
710                WINDOWS.focus(WINDOW.id()).unwrap();
711                w
712            });
713            Some(r)
714        }
715    }
716
717    /// Move the window to the front of the operating system Z stack.
718    ///
719    /// Note that the window is not focused, the [`focus`] operation also moves the window to the front.
720    ///
721    /// [`always_on_top`]: WindowVars::always_on_top
722    /// [`focus`]: Self::focus
723    pub fn bring_to_top(&self, window_id: impl Into<WindowId>) -> Result<(), WindowNotFoundError> {
724        let window_id = window_id.into();
725        let mut w = WINDOWS_SV.write();
726        if w.windows_info.contains_key(&window_id) {
727            w.bring_to_top_requests.push(window_id);
728            UPDATES.update(None);
729            Ok(())
730        } else {
731            Err(WindowNotFoundError::new(window_id))
732        }
733    }
734
735    /// Register the closure `extender` to be called with the root of every new window starting on the next update.
736    ///
737    /// The closure returns the new root node that will be passed to any other root extender until
738    /// the actual final root node is created. The closure is called in the [`WINDOW`] context of the new window,
739    /// so it can be used to modify the window context too.
740    ///
741    /// This is an advanced API that enables app wide features, like themes, to inject context in every new window. The
742    /// extender is called in the context of the window, after the window creation future has completed.
743    ///
744    /// Note that the *root* node passed to the extender is the child node of the `WindowRoot` widget, not the widget itself.
745    /// The extended root will be wrapped in the root widget node, that is, the final root widget will be
746    /// `root(extender_nodes(CONTEXT(EVENT(..))))`, so extension nodes should operate as `CONTEXT` properties.
747    ///
748    /// Note that for themes the `zng-wgt-window` crate provides a `register_style_fn` API that is built over this
749    /// method and more oriented for theming.
750    pub fn register_root_extender(&self, extender: impl FnMut(WindowRootExtenderArgs) -> UiNode + Send + 'static) {
751        WINDOWS_SV.write().root_extenders.get_mut().push(Box::new(extender))
752    }
753
754    /// Variable that tracks the OS window manager configuration for the window chrome.
755    ///
756    /// The chrome (also known as window decorations) defines the title bar, window buttons and window border. Some
757    /// window managers don't provide a native chrome, you can use this config with the [`WindowVars::chrome`] setting
758    /// in a [`register_root_extender`] to provide a custom fallback chrome.
759    ///
760    /// [`register_root_extender`]: Self::register_root_extender
761    pub fn system_chrome(&self) -> Var<ChromeConfig> {
762        WINDOWS_SV.read().system_chrome.read_only()
763    }
764
765    /// Register the closure `handler` to be called for every new window starting on the next update.
766    ///
767    /// The closure can use the args to inspect the new window context and optionally convert the request to a [`NestedWindowNode`].
768    /// Nested windows can be manipulated using the `WINDOWS` API just like other windows, but are layout and rendered inside another window.
769    ///
770    /// This is primarily an adapter for mobile platforms that only support one real window, it accelerates cross platform support from
771    /// projects originally desktop only.
772    ///
773    /// Note that this API is not recommended for implementing features such as *window docking* or
774    /// *tabbing*, for that you probably need to model *tabs* as objects that can outlive their host windows and use [`ArcNode`]
775    /// to transfer the content between host windows.
776    ///
777    /// [`NestedWindowNode`]: crate::NestedWindowNode
778    /// [`ArcNode`]: zng_app::widget::node::ArcNode
779    pub fn register_open_nested_handler(&self, handler: impl FnMut(&mut crate::OpenNestedHandlerArgs) + Send + 'static) {
780        WINDOWS_SV.write().open_nested_handlers.get_mut().push(Box::new(handler))
781    }
782
783    /// Gets the parent actual window and widget that hosts `maybe_nested` if it is open and nested.
784    pub fn nest_parent(&self, maybe_nested: impl Into<WindowId>) -> Option<(WindowId, WidgetId)> {
785        let vars = self.vars(maybe_nested.into()).ok()?;
786        let nest = vars.nest_parent().get()?;
787        let parent = vars.parent().get()?;
788        Some((parent, nest))
789    }
790
791    /// Add a view-process extension payload to the window request for the view-process.
792    ///
793    /// This will only work if called on the first [`UiNode::init`] and at most the first [`UiNode::layout`] of the window.
794    ///
795    /// The payload is dropped after it is send, this method must be called again on [`VIEW_PROCESS_INITED_EVENT`]
796    /// to reinitialize the extensions after view-process respawn.
797    ///
798    /// [`UiNode::init`]: zng_app::widget::node::UiNode::init
799    /// [`UiNode::layout`]: zng_app::widget::node::UiNode::layout
800    /// [`VIEW_PROCESS_INITED_EVENT`]: zng_app::view_process::VIEW_PROCESS_INITED_EVENT
801    pub fn view_extensions_init(
802        &self,
803        window_id: impl Into<WindowId>,
804        extension_id: ApiExtensionId,
805        request: ApiExtensionPayload,
806    ) -> Result<(), WindowNotFoundError> {
807        let window_id = window_id.into();
808        match WINDOWS_SV.write().windows_info.get_mut(&window_id) {
809            Some(i) => {
810                i.extensions.push((extension_id, request));
811                Ok(())
812            }
813            None => Err(WindowNotFoundError::new(window_id)),
814        }
815    }
816
817    pub(super) fn system_colors_config(&self) -> ColorsConfig {
818        WINDOWS_SV.read().latest_colors_cfg
819    }
820
821    pub(super) fn take_view_extensions_init(&self, id: WindowId) -> Vec<(ApiExtensionId, ApiExtensionPayload)> {
822        std::mem::take(&mut WINDOWS_SV.write().windows_info.get_mut(&id).unwrap().extensions)
823    }
824
825    /// Call a view-process headed window extension with custom encoded payload.
826    ///
827    /// Note that unlike most service methods this calls happens immediately.
828    pub fn view_window_extension_raw(
829        &self,
830        window_id: impl Into<WindowId>,
831        extension_id: ApiExtensionId,
832        request: ApiExtensionPayload,
833    ) -> Result<ApiExtensionPayload, ViewExtensionError> {
834        let window_id = window_id.into();
835        let sv = WINDOWS_SV.read();
836        match WINDOWS_SV.read().windows_info.get(&window_id) {
837            Some(i) => match &i.view {
838                Some(r) => match r {
839                    ViewWindowOrHeadless::Window(r) => {
840                        let r = r.clone();
841                        drop(sv);
842                        r.window_extension_raw(extension_id, request)
843                            .map_err(|_| ViewExtensionError::Disconnected)
844                    }
845                    ViewWindowOrHeadless::Headless(_) => Err(ViewExtensionError::WindowNotHeaded(window_id)),
846                },
847                None => Err(ViewExtensionError::NotOpenInViewProcess(window_id)),
848            },
849            None => Err(ViewExtensionError::WindowNotFound(WindowNotFoundError::new(window_id))),
850        }
851    }
852
853    /// Call a headed window extension with serialized payload.
854    ///
855    /// Note that unlike most service methods this call happens immediately.
856    pub fn view_window_extension<I, O>(
857        &self,
858        window_id: impl Into<WindowId>,
859        extension_id: ApiExtensionId,
860        request: &I,
861    ) -> Result<O, ViewExtensionError>
862    where
863        I: serde::Serialize,
864        O: serde::de::DeserializeOwned,
865    {
866        let window_id = window_id.into();
867        let sv = WINDOWS_SV.read();
868        match sv.windows_info.get(&window_id) {
869            Some(i) => match &i.view {
870                Some(r) => match r {
871                    ViewWindowOrHeadless::Window(r) => {
872                        let r = r.clone();
873                        drop(sv);
874                        let r = r
875                            .window_extension(extension_id, request)
876                            .map_err(|_| ViewExtensionError::Disconnected)?;
877                        r.map_err(ViewExtensionError::Api)
878                    }
879                    ViewWindowOrHeadless::Headless(_) => Err(ViewExtensionError::WindowNotHeaded(window_id)),
880                },
881                None => Err(ViewExtensionError::NotOpenInViewProcess(window_id)),
882            },
883            None => Err(ViewExtensionError::WindowNotFound(WindowNotFoundError::new(window_id))),
884        }
885    }
886
887    /// Call a view-process render extension with custom encoded payload for the renderer associated with the window.
888    ///
889    /// Note that unlike most service methods this call happens immediately.
890    pub fn view_render_extension_raw(
891        &self,
892        window_id: impl Into<WindowId>,
893        extension_id: ApiExtensionId,
894        request: ApiExtensionPayload,
895    ) -> Result<ApiExtensionPayload, ViewExtensionError> {
896        let window_id = window_id.into();
897        let sv = WINDOWS_SV.read();
898        match WINDOWS_SV.read().windows_info.get(&window_id) {
899            Some(i) => match &i.view {
900                Some(r) => {
901                    let r = r.renderer();
902                    drop(sv);
903                    r.render_extension_raw(extension_id, request)
904                        .map_err(|_| ViewExtensionError::Disconnected)
905                }
906                None => Err(ViewExtensionError::NotOpenInViewProcess(window_id)),
907            },
908            None => Err(ViewExtensionError::WindowNotFound(WindowNotFoundError::new(window_id))),
909        }
910    }
911
912    /// Call a render extension with serialized payload for the renderer associated with the window.
913    ///
914    /// Note that unlike most service methods this call happens immediately.
915    pub fn view_render_extension<I, O>(
916        &self,
917        window_id: impl Into<WindowId>,
918        extension_id: ApiExtensionId,
919        request: &I,
920    ) -> Result<O, ViewExtensionError>
921    where
922        I: serde::Serialize,
923        O: serde::de::DeserializeOwned,
924    {
925        let window_id = window_id.into();
926        let sv = WINDOWS_SV.read();
927        match sv.windows_info.get(&window_id) {
928            Some(i) => match &i.view {
929                Some(r) => {
930                    let r = r.renderer();
931                    drop(sv);
932                    let r = r
933                        .render_extension(extension_id, request)
934                        .map_err(|_| ViewExtensionError::Disconnected)?;
935                    r.map_err(ViewExtensionError::Api)
936                }
937                None => Err(ViewExtensionError::NotOpenInViewProcess(window_id)),
938            },
939            None => Err(ViewExtensionError::WindowNotFound(WindowNotFoundError::new(window_id))),
940        }
941    }
942
943    /// Update the reference to view window the renderer associated with the window.
944    pub(super) fn set_view(&self, id: WindowId, view: ViewWindowOrHeadless) {
945        if let Some(info) = WINDOWS_SV.write().windows_info.get_mut(&id) {
946            info.view = Some(view);
947        }
948    }
949
950    /// Update widget info tree associated with the window.
951    pub(super) fn set_widget_tree(&self, info_tree: WidgetInfoTree) {
952        if let Some(info) = WINDOWS_SV.write().windows_info.get_mut(&info_tree.window_id()) {
953            info.widget_tree = info_tree;
954        }
955    }
956
957    /// Change window state to loaded if there are no load handles active.
958    ///
959    /// Returns `true` if loaded.
960    pub(super) fn try_load(&self, window_id: WindowId) -> bool {
961        if let Some(info) = WINDOWS_SV.write().windows_info.get_mut(&window_id) {
962            info.is_loaded = info.loading_handle.try_load(window_id);
963
964            if info.is_loaded && !info.vars.0.is_loaded.get() {
965                info.vars.0.is_loaded.set(true);
966                WINDOW_LOAD_EVENT.notify(WindowOpenArgs::now(info.id));
967            }
968
969            info.is_loaded
970        } else {
971            unreachable!()
972        }
973    }
974
975    pub(super) fn on_pre_event(update: &EventUpdate) {
976        if let Some(args) = RAW_WINDOW_FOCUS_EVENT.on(update) {
977            let mut wns = WINDOWS_SV.write();
978
979            let mut prev = None;
980            let mut new = None;
981
982            if let Some(prev_focus) = args.prev_focus
983                && let Some(window) = wns.windows_info.get_mut(&prev_focus)
984            {
985                if window.is_focused {
986                    window.is_focused = false;
987                    prev = Some(prev_focus);
988                } else if args.new_focus.is_none()
989                    && let Some(focused) = wns.windows_info.values_mut().find(|w| w.is_focused)
990                    && focused.vars.nest_parent().get().is_some()
991                    && focused.vars.parent().get() == Some(prev_focus)
992                {
993                    // focus is in nested window of the system window that lost app focus.
994                    focused.is_focused = false;
995                    prev = Some(focused.id);
996                }
997            }
998            if let Some(new_focus) = args.new_focus {
999                if prev.is_none()
1000                    && let Some((&id, window)) = wns.windows_info.iter_mut().find(|w| w.1.is_focused)
1001                    && new_focus != id
1002                {
1003                    window.is_focused = false;
1004                    prev = Some(id);
1005                }
1006
1007                if let Some(window) = wns.windows_info.get_mut(&new_focus)
1008                    && !window.is_focused
1009                {
1010                    window.is_focused = true;
1011                    window.vars.focus_indicator().set(None);
1012                    new = Some(new_focus);
1013                }
1014            }
1015
1016            if prev.is_some() || new.is_some() {
1017                let args = WindowFocusChangedArgs::new(args.timestamp, args.propagation().clone(), prev, new, false);
1018                WINDOW_FOCUS_CHANGED_EVENT.notify(args);
1019            }
1020        } else if let Some(args) = RAW_WINDOW_CLOSE_REQUESTED_EVENT.on(update) {
1021            let _ = WINDOWS.close(args.window_id);
1022        } else if let Some(args) = RAW_WINDOW_CLOSE_EVENT.on(update) {
1023            if WINDOWS_SV.read().windows.contains_key(&args.window_id) {
1024                tracing::error!("view-process closed window without request");
1025                let mut windows = IdSet::default();
1026                windows.insert(args.window_id);
1027                let args = WindowCloseArgs::new(args.timestamp, args.propagation().clone(), windows);
1028                WINDOW_CLOSE_EVENT.notify(args);
1029            }
1030        } else if let Some(args) = RAW_COLORS_CONFIG_CHANGED_EVENT.on(update) {
1031            WINDOWS_SV.write().latest_colors_cfg = args.config;
1032        } else if let Some(args) = RAW_CHROME_CONFIG_CHANGED_EVENT.on(update) {
1033            WINDOWS_SV.read().system_chrome.set(args.config);
1034        } else if VIEW_PROCESS_INITED_EVENT.has(update) {
1035            // we skipped request fulfillment until this event.
1036            UPDATES.update(None);
1037        } else {
1038            #[cfg(feature = "image")]
1039            if let Some(args) = RAW_IMAGE_LOADED_EVENT.on(update).or_else(|| RAW_IMAGE_LOAD_ERROR_EVENT.on(update)) {
1040                // update ready frame images.
1041                let mut sv = WINDOWS_SV.write();
1042                sv.frame_images.retain(|i| {
1043                    if let Some(i) = i.upgrade() {
1044                        if Some(&args.image) == i.get().view() {
1045                            i.update();
1046                            false
1047                        } else {
1048                            true
1049                        }
1050                    } else {
1051                        false
1052                    }
1053                });
1054            }
1055        }
1056
1057        Self::with_detached_windows(|windows, parallel| {
1058            if windows.len() > 1 && parallel.contains(ParallelWin::EVENT) {
1059                windows.par_iter_mut().with_ctx().for_each(|(_, window)| {
1060                    window.pre_event(update);
1061                });
1062            } else {
1063                for (_, window) in windows.iter_mut() {
1064                    window.pre_event(update);
1065                }
1066            }
1067        })
1068    }
1069
1070    pub(super) fn on_ui_event(update: &mut EventUpdate) {
1071        if update.delivery_list_mut().has_pending_search() {
1072            update
1073                .delivery_list_mut()
1074                .fulfill_search(WINDOWS_SV.read().windows_info.values().map(|w| &w.widget_tree));
1075        }
1076        Self::with_detached_windows(|windows, parallel| {
1077            if windows.len() > 1 && parallel.contains(ParallelWin::EVENT) {
1078                windows.par_iter_mut().with_ctx().for_each(|(_, window)| {
1079                    window.ui_event(update);
1080                });
1081            } else {
1082                for (_, window) in windows.iter_mut() {
1083                    window.ui_event(update);
1084                }
1085            }
1086        });
1087    }
1088
1089    pub(super) fn on_event(update: &mut EventUpdate) {
1090        if let Some(args) = WINDOW_CLOSE_REQUESTED_EVENT.on(update) {
1091            let key = args.windows.iter().next().unwrap();
1092            let mut sv = WINDOWS_SV.write();
1093            if let Some(rsp) = sv.close_responders.remove(key) {
1094                if !args.propagation().is_stopped() {
1095                    // close requested by us and not canceled.
1096                    WINDOW_CLOSE_EVENT.notify(WindowCloseArgs::now(args.windows.clone()));
1097                    for r in rsp {
1098                        r.respond(CloseWindowResult::Closed);
1099                    }
1100                } else {
1101                    for r in rsp {
1102                        r.respond(CloseWindowResult::Cancel);
1103                    }
1104                    // already cancelled exit request
1105                    sv.exit_on_close = false;
1106                }
1107            }
1108        } else if let Some(args) = WINDOW_CLOSE_EVENT.on(update) {
1109            // finish close, this notifies `UiNode::deinit` and drops the window
1110            // causing the ViewWindow to drop and close.
1111
1112            for w in args.windows.iter() {
1113                let w = WINDOWS_SV.write().windows.remove(w);
1114                if let Some(w) = w {
1115                    let id = w.ctx.id();
1116                    w.close();
1117
1118                    let info = WINDOWS_SV.write().windows_info.remove(&id).unwrap();
1119
1120                    info.vars.0.is_open.set(false);
1121
1122                    if info.is_focused {
1123                        let args = WindowFocusChangedArgs::now(Some(info.id), None, true);
1124                        WINDOW_FOCUS_CHANGED_EVENT.notify(args)
1125                    }
1126                }
1127            }
1128
1129            let is_headless_app = zng_app::APP.window_mode().is_headless();
1130            let mut wns = WINDOWS_SV.write();
1131
1132            // if windows closed because of app exit request
1133            // OR
1134            // if set to exit on last headed window close in a headed app,
1135            // AND there is no more open headed window OR request for opening a headed window.
1136            if mem::take(&mut wns.exit_on_close)
1137                || (wns.exit_on_last_close.get()
1138                    && !is_headless_app
1139                    && !wns.windows.values().any(|w| matches!(w.ctx.mode(), WindowMode::Headed))
1140                    && !wns
1141                        .open_requests
1142                        .iter()
1143                        .any(|w| matches!(w.force_headless, None | Some(WindowMode::Headed)))
1144                    && !wns.open_tasks.iter().any(|t| matches!(t.mode, WindowMode::Headed)))
1145            {
1146                // fulfill `exit_on_close` or `exit_on_last_close`
1147                APP.exit();
1148            }
1149        } else if let Some(args) = EXIT_REQUESTED_EVENT.on(update)
1150            && !args.propagation().is_stopped()
1151        {
1152            let mut windows = WINDOWS_SV.write();
1153            if !windows.windows_info.is_empty() {
1154                args.propagation().stop();
1155                windows.exit_on_close = true;
1156                drop(windows);
1157                WINDOWS.close_all();
1158            }
1159        }
1160    }
1161
1162    pub(super) fn on_ui_update(update_widgets: &mut WidgetUpdates) {
1163        if update_widgets.delivery_list_mut().has_pending_search() {
1164            update_widgets
1165                .delivery_list_mut()
1166                .fulfill_search(WINDOWS_SV.read().windows_info.values().map(|w| &w.widget_tree));
1167        }
1168
1169        Self::with_detached_windows(|windows, parallel| {
1170            if windows.len() > 1 && parallel.contains(ParallelWin::UPDATE) {
1171                windows.par_iter_mut().with_ctx().for_each(|(_, window)| {
1172                    window.update(update_widgets);
1173                });
1174            } else {
1175                for (_, window) in windows.iter_mut() {
1176                    window.update(update_widgets);
1177                }
1178            }
1179        });
1180    }
1181
1182    pub(super) fn on_update() {
1183        Self::fulfill_requests();
1184    }
1185
1186    fn fulfill_requests() {
1187        if VIEW_PROCESS.is_available() && !VIEW_PROCESS.is_connected() {
1188            // wait ViewProcessInitedEvent
1189            return;
1190        }
1191
1192        let ((open, mut open_tasks, close, focus, bring_to_top, view_tasks), colors_cfg) = {
1193            let mut wns = WINDOWS_SV.write();
1194            (wns.take_requests(), wns.latest_colors_cfg)
1195        };
1196
1197        let window_mode = zng_app::APP.window_mode();
1198
1199        // fulfill open requests.
1200        for r in open {
1201            let window_mode = match (window_mode, r.force_headless) {
1202                (WindowMode::Headed | WindowMode::HeadlessWithRenderer, Some(mode)) => {
1203                    debug_assert!(!matches!(mode, WindowMode::Headed));
1204                    mode
1205                }
1206                (mode, _) => mode,
1207            };
1208
1209            let colors_cfg = match window_mode {
1210                WindowMode::Headed => colors_cfg,
1211                WindowMode::Headless | WindowMode::HeadlessWithRenderer => ColorsConfig::default(),
1212            };
1213
1214            let task = AppWindowTask::new(r.id, window_mode, colors_cfg, r.new.into_inner(), r.responder);
1215            open_tasks.push(task);
1216        }
1217
1218        // update open tasks.
1219        let mut any_ready = false;
1220        for task in &mut open_tasks {
1221            let ready = task.update();
1222            any_ready |= ready;
1223        }
1224        if any_ready {
1225            for mut task in open_tasks {
1226                if task.is_ready() {
1227                    let window_id = task.ctx.id();
1228
1229                    let mut wns = WINDOWS_SV.write();
1230                    let loading = wns.open_loading.remove(&window_id).unwrap();
1231                    let mut root_extenders = mem::take(&mut wns.root_extenders);
1232                    let mut open_nested_handlers = mem::take(&mut wns.open_nested_handlers);
1233                    drop(wns);
1234                    let (window, info, responder) =
1235                        task.finish(loading, &mut root_extenders.get_mut()[..], &mut open_nested_handlers.get_mut()[..]);
1236
1237                    let mut wns = WINDOWS_SV.write();
1238                    root_extenders.get_mut().append(wns.root_extenders.get_mut());
1239                    open_nested_handlers.get_mut().append(wns.open_nested_handlers.get_mut());
1240                    wns.root_extenders = root_extenders;
1241                    wns.open_nested_handlers = open_nested_handlers;
1242
1243                    if wns.windows.insert(window_id, window).is_some() {
1244                        // id conflict resolved on request.
1245                        unreachable!();
1246                    }
1247                    wns.windows_info.insert(info.id, info);
1248
1249                    responder.respond(window_id);
1250                    // WINDOW_OPEN_EVENT.notify happens after init, so that handlers
1251                    // on the window itself can subscribe to the event.
1252                } else {
1253                    let mut wns = WINDOWS_SV.write();
1254                    wns.open_tasks.push(task);
1255                }
1256            }
1257        } else {
1258            let mut wns = WINDOWS_SV.write();
1259            debug_assert!(wns.open_tasks.is_empty());
1260            wns.open_tasks = open_tasks;
1261        }
1262
1263        // notify close requests, the request is fulfilled or canceled
1264        // in the `event` handler.
1265
1266        {
1267            let mut wns = WINDOWS_SV.write();
1268            let wns = &mut *wns;
1269
1270            let mut close_wns = IdSet::default();
1271
1272            for r in close {
1273                for w in r.windows {
1274                    if let Some(info) = wns.windows_info.get(&w)
1275                        && close_wns.insert(w)
1276                    {
1277                        wns.close_responders
1278                            .entry(w)
1279                            .or_insert_with(Default::default)
1280                            .push(r.responder.clone());
1281
1282                        info.vars.0.children.with(|c| {
1283                            for &c in c.iter() {
1284                                if wns.windows_info.contains_key(&c) && close_wns.insert(c) {
1285                                    wns.close_responders
1286                                        .entry(c)
1287                                        .or_insert_with(Default::default)
1288                                        .push(r.responder.clone());
1289                                }
1290                            }
1291                        });
1292                    }
1293                }
1294            }
1295            if !close_wns.is_empty() {
1296                let args = WindowCloseRequestedArgs::now(close_wns);
1297                WINDOW_CLOSE_REQUESTED_EVENT.notify(args);
1298            }
1299        }
1300
1301        // fulfill focus request
1302        if let Some(w_id) = focus {
1303            Self::with_detached_windows(|windows, _| {
1304                if let Some(w) = windows.get_mut(&w_id) {
1305                    w.focus();
1306                }
1307            });
1308        }
1309
1310        for w_id in bring_to_top {
1311            Self::with_detached_windows(|windows, _| {
1312                if let Some(w) = windows.get_mut(&w_id) {
1313                    w.bring_to_top();
1314                }
1315            });
1316        }
1317
1318        for view_task in view_tasks {
1319            let task = view_task.task.into_inner();
1320            Self::with_detached_windows(|windows, _| {
1321                if let Some(w) = windows.get_mut(&view_task.window_id) {
1322                    w.view_task(task);
1323                } else {
1324                    task(None);
1325                }
1326            })
1327        }
1328    }
1329
1330    pub(super) fn on_info(info_widgets: &mut InfoUpdates) {
1331        if info_widgets.delivery_list_mut().has_pending_search() {
1332            info_widgets
1333                .delivery_list_mut()
1334                .fulfill_search(WINDOWS_SV.read().windows_info.values().map(|w| &w.widget_tree));
1335        }
1336
1337        let info_widgets_arc = Arc::new(mem::take(info_widgets));
1338
1339        Self::with_detached_windows(|windows, parallel| {
1340            if windows.len() > 1 && parallel.contains(ParallelWin::LAYOUT) {
1341                windows.par_iter_mut().with_ctx().for_each(|(_, window)| {
1342                    window.info(info_widgets_arc.clone());
1343                })
1344            } else {
1345                for (_, window) in windows.iter_mut() {
1346                    window.info(info_widgets_arc.clone());
1347                }
1348            }
1349        });
1350
1351        match Arc::try_unwrap(info_widgets_arc) {
1352            Ok(w) => {
1353                *info_widgets = w;
1354            }
1355            Err(_) => {
1356                tracing::error!("info_widgets not released by window")
1357            }
1358        }
1359    }
1360
1361    pub(super) fn on_layout(layout_widgets: &mut LayoutUpdates) {
1362        if layout_widgets.delivery_list_mut().has_pending_search() {
1363            layout_widgets
1364                .delivery_list_mut()
1365                .fulfill_search(WINDOWS_SV.read().windows_info.values().map(|w| &w.widget_tree));
1366        }
1367
1368        let layout_widgets_arc = Arc::new(mem::take(layout_widgets));
1369
1370        Self::with_detached_windows(|windows, parallel| {
1371            if windows.len() > 1 && parallel.contains(ParallelWin::LAYOUT) {
1372                windows.par_iter_mut().with_ctx().for_each(|(_, window)| {
1373                    window.layout(layout_widgets_arc.clone());
1374                })
1375            } else {
1376                for (_, window) in windows.iter_mut() {
1377                    window.layout(layout_widgets_arc.clone());
1378                }
1379            }
1380        });
1381
1382        match Arc::try_unwrap(layout_widgets_arc) {
1383            Ok(w) => {
1384                *layout_widgets = w;
1385            }
1386            Err(_) => {
1387                // expected in nested windows
1388                tracing::debug!("layout_widgets not released by window")
1389            }
1390        }
1391    }
1392
1393    pub(super) fn on_render(render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
1394        for list in [&mut *render_widgets, &mut *render_update_widgets] {
1395            if list.delivery_list_mut().has_pending_search() {
1396                list.delivery_list_mut()
1397                    .fulfill_search(WINDOWS_SV.read().windows_info.values().map(|w| &w.widget_tree));
1398            }
1399        }
1400
1401        let render_widgets_arc = Arc::new(mem::take(render_widgets));
1402        let render_update_widgets_arc = Arc::new(mem::take(render_update_widgets));
1403
1404        Self::with_detached_windows(|windows, parallel| {
1405            if windows.len() > 1 && parallel.contains(ParallelWin::RENDER) {
1406                windows.par_iter_mut().with_ctx().for_each(|(_, window)| {
1407                    window.render(render_widgets_arc.clone(), render_update_widgets_arc.clone());
1408                });
1409            } else {
1410                for (_, window) in windows.iter_mut() {
1411                    window.render(render_widgets_arc.clone(), render_update_widgets_arc.clone());
1412                }
1413            }
1414        });
1415
1416        match Arc::try_unwrap(render_widgets_arc) {
1417            Ok(w) => {
1418                *render_widgets = w;
1419            }
1420            Err(_) => {
1421                // expected in nested windows
1422                tracing::debug!("render_widgets not released by window")
1423            }
1424        }
1425        match Arc::try_unwrap(render_update_widgets_arc) {
1426            Ok(w) => {
1427                *render_update_widgets = w;
1428            }
1429            Err(_) => {
1430                // expected in nested windows
1431                tracing::debug!("render_update_widgets not released by window")
1432            }
1433        }
1434    }
1435
1436    /// Takes ownership of [`Windows::windows`] for the duration of the call to `f`.
1437    ///
1438    /// The windows map is empty for the duration of `f` and should not be used, this is for
1439    /// mutating the window content while still allowing it to query the `Windows::windows_info`.
1440    fn with_detached_windows(f: impl FnOnce(&mut IdMap<WindowId, AppWindow>, ParallelWin)) {
1441        let (mut windows, parallel) = {
1442            let mut w = WINDOWS_SV.write();
1443            (mem::take(&mut w.windows), w.parallel.get())
1444        };
1445        f(&mut windows, parallel);
1446        let mut wns = WINDOWS_SV.write();
1447        debug_assert!(wns.windows.is_empty());
1448        wns.windows = windows;
1449    }
1450}
1451
1452/// Native dialogs.
1453impl WINDOWS {
1454    /// Show a native message dialog for the window.
1455    ///
1456    /// The dialog can be modal in the view-process, in the app-process it is always async, the
1457    /// response var will update once when the user responds to the dialog.
1458    ///
1459    /// Consider using the `DIALOG` service instead of the method directly.
1460    pub fn native_message_dialog(
1461        &self,
1462        window_id: WindowId,
1463        dialog: zng_view_api::dialog::MsgDialog,
1464    ) -> ResponseVar<zng_view_api::dialog::MsgDialogResponse> {
1465        let (responder, rsp) = response_var();
1466        WINDOWS_SV.write().view_window_task(window_id, move |win| match win {
1467            Some(win) => {
1468                if let Err(e) = win.message_dialog(dialog, responder.clone()) {
1469                    responder.respond(zng_view_api::dialog::MsgDialogResponse::Error(formatx!("{e}")))
1470                }
1471            }
1472            None => responder.respond(zng_view_api::dialog::MsgDialogResponse::Error(Txt::from_static(
1473                "native window not found",
1474            ))),
1475        });
1476        rsp
1477    }
1478
1479    /// Show a native file dialog for the window.
1480    ///
1481    /// The dialog can be modal in the view-process, in the app-process it is always async, the
1482    /// response var will update once when the user responds to the dialog.
1483    ///
1484    /// Consider using the `DIALOG` service instead of the method directly.
1485    pub fn native_file_dialog(
1486        &self,
1487        window_id: WindowId,
1488        dialog: zng_view_api::dialog::FileDialog,
1489    ) -> ResponseVar<zng_view_api::dialog::FileDialogResponse> {
1490        let (responder, rsp) = response_var();
1491        WINDOWS_SV.write().view_window_task(window_id, move |win| match win {
1492            Some(win) => {
1493                if let Err(e) = win.file_dialog(dialog, responder.clone()) {
1494                    responder.respond(zng_view_api::dialog::FileDialogResponse::Error(formatx!("{e}")))
1495                }
1496            }
1497            None => responder.respond(zng_view_api::dialog::FileDialogResponse::Error(Txt::from_static(
1498                "native window not found",
1499            ))),
1500        });
1501        rsp
1502    }
1503}
1504
1505/// Raw drag&drop API.
1506#[allow(non_camel_case_types)]
1507pub struct WINDOWS_DRAG_DROP;
1508impl WINDOWS_DRAG_DROP {
1509    /// Request a start of drag&drop from the window.
1510    pub fn start_drag_drop(
1511        &self,
1512        window_id: WindowId,
1513        data: Vec<DragDropData>,
1514        allowed_effects: DragDropEffect,
1515    ) -> Result<DragDropId, DragDropError> {
1516        match WINDOWS_SV.write().windows.get_mut(&window_id) {
1517            Some(w) => w.start_drag_drop(data, allowed_effects),
1518            None => Err(DragDropError::CannotStart(WindowNotFoundError::new(window_id).to_txt())),
1519        }
1520    }
1521
1522    /// Notify the drag source of what effect was applied for a received drag&drop.
1523    pub fn drag_dropped(&self, window_id: WindowId, drop_id: DragDropId, applied: DragDropEffect) -> Result<(), WindowNotFoundError> {
1524        match WINDOWS_SV.write().windows.get_mut(&window_id) {
1525            Some(w) => {
1526                w.drag_dropped(drop_id, applied);
1527                Ok(())
1528            }
1529            None => Err(WindowNotFoundError::new(window_id)),
1530        }
1531    }
1532}
1533
1534/// Window data visible in [`Windows`], detached so we can make the window visible inside the window content.
1535struct AppWindowInfo {
1536    id: WindowId,
1537    mode: WindowMode,
1538    view: Option<ViewWindowOrHeadless>,
1539    extensions: Vec<(ApiExtensionId, ApiExtensionPayload)>,
1540    vars: WindowVars,
1541
1542    widget_tree: WidgetInfoTree,
1543    // focus tracked by the raw focus events.
1544    is_focused: bool,
1545
1546    loading_handle: WindowLoading,
1547    is_loaded: bool,
1548}
1549impl AppWindowInfo {
1550    pub fn new(id: WindowId, root_id: WidgetId, mode: WindowMode, vars: WindowVars, loading_handle: WindowLoading) -> Self {
1551        Self {
1552            id,
1553            mode,
1554            view: None,
1555            extensions: vec![],
1556            vars,
1557            widget_tree: WidgetInfoTree::wgt(id, root_id),
1558            is_focused: false,
1559            loading_handle,
1560            is_loaded: false,
1561        }
1562    }
1563}
1564struct OpenWindowRequest {
1565    id: WindowId,
1566    new: Mutex<UiTask<WindowRoot>>, // never locked, makes `OpenWindowRequest: Sync`
1567    force_headless: Option<WindowMode>,
1568    responder: ResponderVar<WindowId>,
1569}
1570struct CloseWindowRequest {
1571    responder: ResponderVar<CloseWindowResult>,
1572    windows: IdSet<WindowId>,
1573}
1574
1575struct AppWindowTask {
1576    ctx: WindowCtx,
1577    mode: WindowMode,
1578    task: Mutex<UiTask<WindowRoot>>, // never locked, used to make `AppWindowTask: Sync`
1579    responder: ResponderVar<WindowId>,
1580}
1581impl AppWindowTask {
1582    fn new(id: WindowId, mode: WindowMode, colors_cfg: ColorsConfig, new: UiTask<WindowRoot>, responder: ResponderVar<WindowId>) -> Self {
1583        let primary_scale_factor = match mode {
1584            WindowMode::Headed => MONITORS
1585                .primary_monitor()
1586                .get()
1587                .map(|m| m.scale_factor().get())
1588                .unwrap_or_else(|| 1.fct()),
1589            WindowMode::Headless | WindowMode::HeadlessWithRenderer => 1.fct(),
1590        };
1591
1592        let mut ctx = WindowCtx::new(id, mode);
1593
1594        let vars = WindowVars::new(WINDOWS_SV.read().default_render_mode.get(), primary_scale_factor, colors_cfg);
1595        ctx.with_state(|s| s.borrow_mut().set(*WINDOW_VARS_ID, vars.clone()));
1596
1597        Self {
1598            ctx,
1599            mode,
1600            responder,
1601            task: Mutex::new(new),
1602        }
1603    }
1604
1605    fn is_ready(&mut self) -> bool {
1606        self.task.get_mut().is_ready()
1607    }
1608
1609    fn update(&mut self) -> bool {
1610        WINDOW.with_context(&mut self.ctx, || {
1611            self.task.get_mut().update();
1612        });
1613        self.task.get_mut().is_ready()
1614    }
1615
1616    fn finish(
1617        self,
1618        loading: WindowLoading,
1619        extenders: &mut [Box<dyn FnMut(WindowRootExtenderArgs) -> UiNode + Send>],
1620        open_nested_handlers: &mut [Box<dyn FnMut(&mut crate::OpenNestedHandlerArgs) + Send>],
1621    ) -> (AppWindow, AppWindowInfo, ResponderVar<WindowId>) {
1622        let mut window = self.task.into_inner().into_result().unwrap_or_else(|_| panic!());
1623        let mut ctx = self.ctx;
1624
1625        WINDOW.with_context(&mut ctx, || {
1626            for ext in extenders.iter_mut().rev() {
1627                let root = mem::replace(&mut window.child, UiNode::nil());
1628                window.child = ext(WindowRootExtenderArgs { root });
1629            }
1630            let child = mem::replace(&mut window.child, UiNode::nil());
1631            let vars = WINDOW.vars();
1632            let child = with_context_var(child, ACCENT_COLOR_VAR, vars.actual_accent_color());
1633            let child = with_context_var(child, COLOR_SCHEME_VAR, vars.actual_color_scheme());
1634            let child = with_context_var(child, PARALLEL_VAR, vars.parallel());
1635            window.child = child;
1636        });
1637
1638        let mode = self.mode;
1639        let id = ctx.id();
1640
1641        ctx.set_widget_tree(WidgetInfoTree::wgt(id, window.id));
1642
1643        let vars = ctx.with_state(|s| s.borrow().get_clone(*WINDOW_VARS_ID)).unwrap();
1644
1645        if window.kiosk {
1646            vars.chrome().set(false);
1647            vars.visible().set(true);
1648            if !vars.state().get().is_fullscreen() {
1649                vars.state().set(WindowState::Exclusive);
1650            }
1651        }
1652
1653        let commands = WindowCommands::new(id);
1654
1655        let root_id = window.id;
1656
1657        let mut args = crate::OpenNestedHandlerArgs::new(ctx, vars.clone(), commands, window);
1658        for h in open_nested_handlers.iter_mut().rev() {
1659            h(&mut args);
1660            if args.has_nested() {
1661                break;
1662            }
1663        }
1664
1665        let (ctx, ctrl) = match args.take_normal() {
1666            Ok((ctx, vars, commands, window)) => (ctx, WindowCtrl::new(&vars, commands, mode, window)),
1667            Err((ctx, node)) => (ctx, WindowCtrl::new_nested(node)),
1668        };
1669
1670        let window = AppWindow {
1671            ctrl: Mutex::new(ctrl),
1672            ctx,
1673        };
1674        let info = AppWindowInfo::new(id, root_id, mode, vars, loading);
1675
1676        (window, info, self.responder)
1677    }
1678}
1679
1680struct ViewWindowTask {
1681    window_id: WindowId,
1682    task: Mutex<Box<dyn FnOnce(Option<&view_process::ViewWindow>) + Send>>, // never locked, for :Async only
1683}
1684
1685/// Window context owner.
1686struct AppWindow {
1687    ctrl: Mutex<WindowCtrl>, // never locked, makes `AppWindow: Sync`.
1688    ctx: WindowCtx,
1689}
1690impl AppWindow {
1691    fn ctrl_in_ctx<R>(&mut self, action: impl FnOnce(&mut WindowCtrl) -> R) -> R {
1692        WINDOW.with_context(&mut self.ctx, || action(self.ctrl.get_mut()))
1693    }
1694
1695    pub fn pre_event(&mut self, update: &EventUpdate) {
1696        self.ctrl_in_ctx(|ctrl| ctrl.pre_event(update))
1697    }
1698
1699    pub fn ui_event(&mut self, update: &EventUpdate) {
1700        self.ctrl_in_ctx(|ctrl| ctrl.ui_event(update))
1701    }
1702
1703    pub fn update(&mut self, update_widgets: &WidgetUpdates) {
1704        self.ctrl_in_ctx(|ctrl| ctrl.update(update_widgets));
1705    }
1706
1707    pub fn info(&mut self, info_widgets: Arc<InfoUpdates>) {
1708        let info_update = self.ctrl_in_ctx(|ctrl| ctrl.info(info_widgets));
1709        if let Some(new) = info_update {
1710            self.ctx.set_widget_tree(new);
1711        }
1712    }
1713
1714    pub fn layout(&mut self, layout_widgets: Arc<LayoutUpdates>) {
1715        self.ctrl_in_ctx(|ctrl| ctrl.layout(layout_widgets));
1716    }
1717
1718    pub fn render(&mut self, render_widgets: Arc<RenderUpdates>, render_update_widgets: Arc<RenderUpdates>) {
1719        self.ctrl_in_ctx(|ctrl| ctrl.render(render_widgets, render_update_widgets));
1720    }
1721
1722    pub fn focus(&mut self) {
1723        self.ctrl_in_ctx(|ctrl| ctrl.focus());
1724    }
1725
1726    pub fn start_drag_drop(&mut self, data: Vec<DragDropData>, allowed_effects: DragDropEffect) -> Result<DragDropId, DragDropError> {
1727        self.ctrl_in_ctx(|ctx| ctx.start_drag_drop(data, allowed_effects))
1728    }
1729
1730    pub fn drag_dropped(&mut self, drop_id: DragDropId, applied: DragDropEffect) {
1731        self.ctrl_in_ctx(|ctx| ctx.drag_dropped(drop_id, applied))
1732    }
1733
1734    pub fn bring_to_top(&mut self) {
1735        self.ctrl_in_ctx(|ctrl| ctrl.bring_to_top());
1736    }
1737
1738    pub fn close(mut self) {
1739        self.ctrl_in_ctx(|ctrl| ctrl.close());
1740    }
1741
1742    fn view_task(&mut self, task: Box<dyn FnOnce(Option<&view_process::ViewWindow>) + Send>) {
1743        self.ctrl_in_ctx(|ctrl| ctrl.view_task(task));
1744    }
1745}
1746
1747struct WindowLoading {
1748    handles: Vec<std::sync::Weak<crate::WindowLoadingHandleData>>,
1749    timer: Option<DeadlineHandle>,
1750}
1751impl WindowLoading {
1752    pub fn new() -> Self {
1753        WindowLoading {
1754            handles: vec![],
1755            timer: None,
1756        }
1757    }
1758
1759    /// Returns `true` if the window can load.
1760    pub fn try_load(&mut self, window_id: WindowId) -> bool {
1761        let mut deadline = Deadline::MAX;
1762        self.handles.retain(|h| match h.upgrade() {
1763            Some(h) => {
1764                if h.deadline.has_elapsed() {
1765                    false
1766                } else {
1767                    deadline = deadline.min(h.deadline);
1768                    true
1769                }
1770            }
1771            None => false,
1772        });
1773
1774        if self.handles.is_empty() {
1775            self.timer = None;
1776            true
1777        } else {
1778            if let Some(t) = &self.timer
1779                && t.deadline() != deadline
1780            {
1781                self.timer = None;
1782            }
1783            if self.timer.is_none() {
1784                let t = TIMERS.on_deadline(
1785                    deadline,
1786                    hn_once!(|_| {
1787                        UPDATES.update_window(window_id).layout_window(window_id).render_window(window_id);
1788                    }),
1789                );
1790                self.timer = Some(t);
1791            }
1792
1793            false
1794        }
1795    }
1796
1797    pub fn new_handle(&mut self, update: AppEventSender, deadline: Deadline) -> WindowLoadingHandle {
1798        let h = Arc::new(crate::WindowLoadingHandleData { update, deadline });
1799        self.handles.push(Arc::downgrade(&h));
1800        WindowLoadingHandle(h)
1801    }
1802}
1803
1804/// Extensions methods for [`WINDOW`] contexts of windows open by [`WINDOWS`].
1805///
1806/// [`WINDOW`]: zng_app::window::WINDOW
1807#[expect(non_camel_case_types)]
1808pub trait WINDOW_Ext {
1809    /// Clone a reference to the variables that get and set window properties.
1810    fn vars(&self) -> super::WindowVars {
1811        WindowVars::req()
1812    }
1813
1814    /// Returns `true` if the window open task has completed.
1815    ///
1816    /// Note that the window may not yet be [loaded], or actually open in the view-process.
1817    ///
1818    /// [loaded]: WINDOW_Ext::is_loaded
1819    fn is_open(&self) -> bool {
1820        WINDOWS.is_open(WINDOW.id())
1821    }
1822
1823    /// Returns `true` if the window is open and has no pending loading handles.
1824    ///
1825    /// See [`WINDOWS::is_loaded`] for more details.
1826    fn is_loaded(&self) -> bool {
1827        WINDOWS.is_loaded(WINDOW.id())
1828    }
1829
1830    /// Enable accessibility info.
1831    ///
1832    /// If access is not already enabled, enables it in the app-process only.
1833    fn enable_access(&self) {
1834        let vars = WINDOW.vars();
1835        let access_enabled = &vars.0.access_enabled;
1836        if access_enabled.get().is_disabled() {
1837            access_enabled.modify(|e| **e |= zng_app::widget::info::access::AccessEnabled::APP);
1838        }
1839    }
1840
1841    /// Gets a handle that stops the window from loading while the handle is alive.
1842    ///
1843    /// A window is only opened in the view-process after it is loaded, without any loading handles the window is considered loaded
1844    /// after the first layout pass. Nodes in the window can request a loading handle to delay the view opening to after all async resources
1845    /// it requires are loaded.
1846    ///
1847    /// Note that a window is only loaded after all handles are dropped or expired, you should set a reasonable `deadline`,  
1848    /// it is best to partially render a window after a short time than not show anything.
1849    ///
1850    /// Returns `None` if the window has already loaded.
1851    fn loading_handle(&self, deadline: impl Into<Deadline>) -> Option<WindowLoadingHandle> {
1852        WINDOWS.loading_handle(WINDOW.id(), deadline)
1853    }
1854
1855    /// Generate an image from the current rendered frame of the window.
1856    ///
1857    /// The image is not loaded at the moment of return, it will update when it is loaded.
1858    #[cfg(feature = "image")]
1859    fn frame_image(&self, mask: Option<ImageMaskMode>) -> ImageVar {
1860        WINDOWS.frame_image(WINDOW.id(), mask)
1861    }
1862
1863    /// Generate an image from a selection of the current rendered frame of the window.
1864    ///
1865    /// The image is not loaded at the moment of return, it will update when it is loaded.
1866    ///
1867    /// If the window is not found the error is reported in the image error.
1868    #[cfg(feature = "image")]
1869    fn frame_image_rect(&self, rect: PxRect, mask: Option<ImageMaskMode>) -> ImageVar {
1870        WINDOWS.frame_image_rect(WINDOW.id(), rect, mask)
1871    }
1872
1873    /// Move the window to the front of the operating system Z stack.
1874    ///
1875    /// See [`WINDOWS.bring_to_top`] for more details.
1876    ///
1877    /// [`WINDOWS.bring_to_top`]: WINDOWS::bring_to_top
1878    fn bring_to_top(&self) {
1879        WINDOWS.bring_to_top(WINDOW.id()).ok();
1880    }
1881
1882    /// Starts closing the window, the operation can be canceled by listeners of
1883    /// [`WINDOW_CLOSE_REQUESTED_EVENT`]. If the window has children they are closed together.
1884    ///
1885    /// Returns a response var that will update once with the result of the operation.
1886    ///
1887    /// See [`WINDOWS.close`] for more details.
1888    ///
1889    /// [`WINDOWS.close`]: WINDOWS::close
1890    fn close(&self) -> ResponseVar<CloseWindowResult> {
1891        WINDOWS.close(WINDOW.id()).unwrap()
1892    }
1893}
1894impl WINDOW_Ext for WINDOW {}
1895
1896/// Arguments for [`WINDOWS.register_root_extender`].
1897///
1898/// [`WINDOWS.register_root_extender`]: WINDOWS::register_root_extender
1899#[non_exhaustive]
1900pub struct WindowRootExtenderArgs {
1901    /// The window root content, extender must wrap this node with extension nodes or return
1902    /// it for no-op.
1903    pub root: UiNode,
1904}
1905#[cfg(feature = "image")]
1906impl ImageRenderWindowRoot for WindowRoot {
1907    fn into_any(self: Box<Self>) -> Box<dyn Any> {
1908        self
1909    }
1910}
1911
1912#[doc(hidden)]
1913#[cfg(feature = "image")]
1914impl ImageRenderWindowsService for WINDOWS {
1915    fn new_window_root(&self, node: UiNode, render_mode: RenderMode, scale_factor: Option<Factor>) -> Box<dyn ImageRenderWindowRoot> {
1916        Box::new(WindowRoot::new_container(
1917            WidgetId::new_unique(),
1918            StartPosition::Default,
1919            false,
1920            true,
1921            Some(render_mode),
1922            scale_factor.map(HeadlessMonitor::new_scale).unwrap_or_default(),
1923            false,
1924            node,
1925        ))
1926    }
1927
1928    fn enable_frame_capture_in_window_context(&self, mask: Option<ImageMaskMode>) {
1929        let mode = if let Some(mask) = mask {
1930            FrameCaptureMode::AllMask(mask)
1931        } else {
1932            FrameCaptureMode::All
1933        };
1934        WINDOW.vars().frame_capture_mode().set(mode);
1935    }
1936
1937    fn set_parent_in_window_context(&self, parent_id: WindowId) {
1938        let vars = WINDOW.vars();
1939        vars.parent().set(parent_id);
1940    }
1941
1942    fn open_headless_window(&self, new_window_root: Box<dyn FnOnce() -> Box<dyn ImageRenderWindowRoot> + Send>) {
1943        WINDOWS.open_headless(
1944            async move {
1945                let w = *new_window_root()
1946                    .into_any()
1947                    .downcast::<WindowRoot>()
1948                    .expect("expected `WindowRoot` in image render window");
1949                let vars = WINDOW.vars();
1950                vars.auto_size().set(true);
1951                vars.min_size().set((1.px(), 1.px()));
1952                w
1953            },
1954            true,
1955        );
1956    }
1957
1958    fn on_frame_image_ready(&self, update: &EventUpdate) -> Option<(WindowId, Img)> {
1959        if let Some(args) = FRAME_IMAGE_READY_EVENT.on(update)
1960            && let Some(img) = &args.frame_image
1961        {
1962            return Some((args.window_id, img.clone()));
1963        }
1964        None
1965    }
1966
1967    fn close_window(&self, window_id: WindowId) {
1968        let _ = WINDOWS.close(window_id);
1969    }
1970
1971    fn clone_boxed(&self) -> Box<dyn ImageRenderWindowsService> {
1972        Box::new(WINDOWS)
1973    }
1974}
1975
1976/// Window focused widget hook.
1977#[expect(non_camel_case_types)]
1978pub struct WINDOW_FOCUS;
1979impl WINDOW_FOCUS {
1980    /// Setup a var that is controlled by the focus service and tracks the focused widget.
1981    ///
1982    /// This must be called by the focus implementation only.
1983    pub fn hook_focus_service(&self, focused: Var<Option<InteractionPath>>) {
1984        *FOCUS_SV.write() = focused;
1985    }
1986
1987    pub(crate) fn focused(&self) -> Var<Option<InteractionPath>> {
1988        FOCUS_SV.get()
1989    }
1990}