zng_ext_window/
service.rs

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