zng_ext_window/
control.rs

1//! This module implements Management of window content and synchronization of WindowVars and View-Process.
2
3use std::{mem, sync::Arc};
4
5use parking_lot::Mutex;
6use zng_app::{
7    access::{ACCESS_DEINITED_EVENT, ACCESS_INITED_EVENT},
8    event::{AnyEventArgs, CommandHandle},
9    hn_once,
10    render::{FrameBuilder, FrameUpdate},
11    static_id,
12    timer::TIMERS,
13    update::{EventUpdate, InfoUpdates, LayoutUpdates, RenderUpdates, UPDATES, WidgetUpdates},
14    view_process::{
15        VIEW_PROCESS, VIEW_PROCESS_INITED_EVENT, ViewHeadless, ViewRenderer, ViewWindow,
16        raw_events::{
17            RAW_COLORS_CONFIG_CHANGED_EVENT, RAW_HEADLESS_OPEN_EVENT, RAW_IME_EVENT, RAW_WINDOW_CHANGED_EVENT, RAW_WINDOW_FOCUS_EVENT,
18            RAW_WINDOW_OPEN_EVENT, RAW_WINDOW_OR_HEADLESS_OPEN_ERROR_EVENT, RawWindowFocusArgs,
19        },
20    },
21    widget::{
22        VarLayout, WIDGET, WidgetCtx, WidgetId, WidgetUpdateMode,
23        info::{WidgetInfoBuilder, WidgetInfoTree, WidgetLayout, WidgetMeasure, WidgetPath, access::AccessEnabled},
24        node::{UiNode, UiNodeImpl, WidgetUiNodeImpl},
25    },
26    window::{WINDOW, WindowCtx, WindowId, WindowMode},
27};
28use zng_app_context::LocalContext;
29use zng_clone_move::clmv;
30use zng_color::{LightDark, Rgba, colors};
31use zng_layout::{
32    context::{DIRECTION_VAR, LAYOUT, LayoutMetrics, LayoutPassId},
33    unit::{
34        Dip, DipPoint, DipRect, DipSize, DipToPx, Factor, FactorUnits, Layout1d, Layout2d, Length, Px, PxConstraints, PxDensity, PxPoint,
35        PxRect, PxSize, PxToDip, PxVector, TimeUnits,
36    },
37};
38use zng_state_map::StateId;
39use zng_task::channel::ChannelError;
40use zng_var::{Var, VarHandles};
41use zng_view_api::{
42    DragDropId, FocusResult, Ime,
43    config::{ColorScheme, FontAntiAliasing},
44    drag_drop::{DragDropData, DragDropEffect, DragDropError},
45    window::{
46        EventCause, FrameCapture, FrameId, FrameRequest, FrameUpdateRequest, FrameWaitId, HeadlessRequest, RenderMode, WindowRequest,
47        WindowState, WindowStateAll,
48    },
49};
50use zng_wgt::prelude::WidgetInfo;
51
52use crate::{
53    AutoSize, HeadlessMonitor, MONITORS, MONITORS_CHANGED_EVENT, MonitorInfo, StartPosition, WINDOW_CHANGED_EVENT, WINDOW_Ext,
54    WINDOW_FOCUS, WINDOWS, WINDOWS_DRAG_DROP, WidgetInfoImeArea, WindowChangedArgs, WindowRoot, WindowVars,
55    cmd::{MINIMIZE_CMD, RESTORE_CMD, WindowCommands},
56};
57
58#[cfg(feature = "image")]
59use zng_app::{Deadline, view_process::raw_events::RAW_FRAME_RENDERED_EVENT};
60
61#[cfg(feature = "image")]
62use zng_ext_image::{IMAGES, ImageRenderArgs, ImageSource, ImageVar, Img};
63
64#[cfg(feature = "image")]
65use crate::{FRAME_IMAGE_READY_EVENT, FrameCaptureMode, FrameImageReadyArgs};
66
67#[cfg(feature = "image")]
68use zng_var::VarHandle;
69
70#[cfg(feature = "image")]
71use crate::WindowIcon;
72
73#[cfg(feature = "image")]
74struct ImageResources {
75    icon_var: Option<ImageVar>,
76    cursor_var: Option<ImageVar>,
77    icon_binding: VarHandle,
78    cursor_binding: VarHandle,
79    deadline: Deadline,
80}
81#[cfg(feature = "image")]
82impl Default for ImageResources {
83    fn default() -> Self {
84        Self {
85            icon_var: None,
86            cursor_var: None,
87            icon_binding: VarHandle::dummy(),
88            cursor_binding: VarHandle::dummy(),
89            deadline: Deadline::timeout(1.secs()),
90        }
91    }
92}
93
94struct ImeInfo {
95    target: WidgetPath,
96    has_preview: bool,
97    area: DipRect,
98}
99
100/// Implementer of `App <-> View` sync in a headed window.
101struct HeadedCtrl {
102    window: Option<ViewWindow>,
103    waiting_view: bool,
104    delayed_view_updates: Vec<Box<dyn FnOnce(&ViewWindow) + Send>>,
105    vars: WindowVars,
106    respawned: bool,
107
108    content: ContentCtrl,
109
110    // init config.
111    start_position: StartPosition,
112    start_focused: bool,
113    kiosk: Option<WindowState>, // Some(enforced_fullscreen)
114    transparent: bool,
115    render_mode: Option<RenderMode>,
116
117    // current state.
118    state: Option<WindowStateAll>, // None if not inited.
119    monitor: Option<MonitorInfo>,
120    resize_wait_id: Option<FrameWaitId>,
121    #[cfg(feature = "image")]
122    img_res: ImageResources,
123    actual_state: Option<WindowState>, // for WindowChangedEvent
124    parent_color_scheme: Option<Var<ColorScheme>>,
125    parent_accent_color: Option<Var<LightDark>>,
126    actual_parent: Option<WindowId>,
127    root_font_size: Dip,
128    render_access_update: Option<WidgetInfoTree>, // previous info tree
129    ime_info: Option<ImeInfo>,
130    cancel_ime_handle: CommandHandle,
131    open_title_menu_handle: CommandHandle,
132    drag_move_handle: CommandHandle,
133}
134impl HeadedCtrl {
135    pub fn new(vars: &WindowVars, commands: WindowCommands, content: WindowRoot) -> Self {
136        Self {
137            window: None,
138            waiting_view: false,
139            delayed_view_updates: vec![],
140
141            start_position: content.start_position,
142            start_focused: content.start_focused,
143            kiosk: if content.kiosk { Some(WindowState::Fullscreen) } else { None },
144            transparent: content.transparent,
145            render_mode: content.render_mode,
146
147            content: ContentCtrl::new(vars.clone(), commands, content),
148            vars: vars.clone(),
149            respawned: false,
150
151            state: None,
152            monitor: None,
153            resize_wait_id: None,
154            #[cfg(feature = "image")]
155            img_res: ImageResources::default(),
156            parent_color_scheme: None,
157            parent_accent_color: None,
158            actual_parent: None,
159            actual_state: None,
160            root_font_size: Dip::from_px(Length::pt_to_px(11.0, 1.fct()), 1.fct()),
161            render_access_update: None,
162            ime_info: None,
163            cancel_ime_handle: CommandHandle::dummy(),
164            open_title_menu_handle: CommandHandle::dummy(),
165            drag_move_handle: CommandHandle::dummy(),
166        }
167    }
168
169    fn update_gen(&mut self, update: impl FnOnce(&ViewWindow) + Send + 'static) {
170        if let Some(view) = &self.window {
171            // view is ready, just update.
172            update(view);
173        } else if self.waiting_view {
174            // update after view requested, but still not ready. Will apply when the view is received
175            // or be discarded if the view-process respawns.
176            self.delayed_view_updates.push(Box::new(update));
177        } else {
178            // respawning or view-process not inited, will recreate entire window.
179        }
180    }
181
182    pub fn update(&mut self, update_widgets: &WidgetUpdates) {
183        if self.window.is_none() && !self.waiting_view {
184            // we request a view on the first layout.
185            UPDATES.layout_window(WINDOW.id());
186
187            if let Some(enforced_fullscreen) = self.kiosk {
188                // enforce kiosk in pre-init.
189
190                if !self.vars.state().get().is_fullscreen() {
191                    self.vars.state().set(enforced_fullscreen);
192                }
193            }
194        }
195
196        if let Some(query) = self.vars.monitor().get_new() {
197            if self.monitor.is_none() {
198                let monitor = query.select_fallback();
199                let scale_factor = monitor.scale_factor().get();
200                self.vars.0.scale_factor.set(scale_factor);
201                self.monitor = Some(monitor);
202                UPDATES.layout_window(WINDOW.id());
203            } else if let Some(new) = query.select() {
204                let current = self.vars.0.actual_monitor.get();
205                if Some(new.id()) != current {
206                    let scale_factor = new.scale_factor().get();
207                    self.vars.0.scale_factor.set(scale_factor);
208                    self.vars.0.actual_monitor.set(new.id());
209                    self.monitor = Some(new);
210                    UPDATES.layout_window(WINDOW.id());
211                }
212            }
213        }
214        if let Some(prev_state) = self.state.clone() {
215            let mut new_state = prev_state.clone();
216
217            if self.vars.chrome().is_new() || WINDOWS.system_chrome().is_new() {
218                let mut chrome = self.vars.chrome().get();
219
220                if self.kiosk.is_some() && !chrome {
221                    tracing::error!("window in `kiosk` mode can not show chrome");
222                    chrome = false;
223                }
224
225                new_state.chrome_visible = chrome && !WINDOWS.system_chrome().get().needs_custom();
226            }
227
228            if let Some(mut req_state) = self.vars.state().get_new() {
229                if let Some(enforced_fullscreen) = &mut self.kiosk {
230                    if !req_state.is_fullscreen() {
231                        tracing::error!("window in `kiosk` mode can only be fullscreen");
232
233                        req_state = *enforced_fullscreen;
234                    } else {
235                        *enforced_fullscreen = req_state;
236                    }
237                }
238
239                new_state.set_state(req_state);
240                self.vars.0.restore_state.set(new_state.restore_state);
241            }
242
243            if (self.vars.min_size().is_new() || self.vars.max_size().is_new())
244                && let Some(m) = &self.monitor
245            {
246                let scale_factor = m.scale_factor().get();
247                let screen_density = m.density().get();
248                let screen_size = m.size().get();
249                let (min_size, max_size) = self.content.outer_layout(scale_factor, screen_density, screen_size, || {
250                    let min_size = self.vars.min_size().layout_dft(default_min_size(scale_factor));
251                    let max_size = self.vars.max_size().layout_dft(screen_size);
252
253                    (min_size.to_dip(scale_factor), max_size.to_dip(scale_factor))
254                });
255
256                let size = new_state.restore_rect.size;
257
258                new_state.restore_rect.size = size.min(max_size).max(min_size);
259                new_state.min_size = min_size;
260                new_state.max_size = max_size;
261            }
262
263            if let Some(auto) = self.vars.auto_size().get_new()
264                && auto != AutoSize::DISABLED
265            {
266                UPDATES.layout_window(WINDOW.id());
267            }
268
269            if self.vars.size().is_new() {
270                let auto_size = self.vars.auto_size().get();
271
272                if auto_size != AutoSize::CONTENT
273                    && let Some(m) = &self.monitor
274                {
275                    let scale_factor = m.scale_factor().get();
276                    let screen_density = m.density().get();
277                    let screen_size = m.size().get();
278                    let size = self.content.outer_layout(scale_factor, screen_density, screen_size, || {
279                        self.vars.size().layout_dft(default_size(scale_factor)).to_dip(scale_factor)
280                    });
281
282                    let size = size.min(new_state.max_size).max(new_state.min_size);
283
284                    if !auto_size.contains(AutoSize::CONTENT_WIDTH) {
285                        new_state.restore_rect.size.width = size.width;
286                    }
287                    if !auto_size.contains(AutoSize::CONTENT_HEIGHT) {
288                        new_state.restore_rect.size.height = size.height;
289                    }
290                }
291            }
292
293            if let Some(pos) = self.vars.position().get_new()
294                && let Some(m) = &self.monitor
295            {
296                let scale_factor = m.scale_factor().get();
297                let screen_density = m.density().get();
298                let screen_size = m.size().get();
299                let pos = self.content.outer_layout(scale_factor, screen_density, screen_size, || {
300                    pos.layout_dft(PxPoint::new(Px(50), Px(50)))
301                });
302                new_state.restore_rect.origin = pos.to_dip(scale_factor);
303            }
304
305            if let Some(mut visible) = self.vars.visible().get_new() {
306                if !visible && self.kiosk.is_some() {
307                    tracing::error!("window in `kiosk` mode can not be hidden");
308                    visible = true;
309                }
310
311                self.update_gen(move |view| {
312                    let _: Ignore = view.set_visible(visible);
313                });
314            }
315
316            if let Some(movable) = self.vars.movable().get_new() {
317                self.update_gen(move |view| {
318                    let _: Ignore = view.set_movable(movable);
319                });
320            }
321
322            if let Some(resizable) = self.vars.resizable().get_new() {
323                self.update_gen(move |view| {
324                    let _: Ignore = view.set_resizable(resizable);
325                });
326            }
327
328            if let Some(buttons) = self.vars.enabled_buttons().get_new() {
329                self.update_gen(move |view| {
330                    let _: Ignore = view.set_enabled_buttons(buttons);
331                })
332            }
333
334            if let Some(reason) = self.vars.system_shutdown_warn().get_new() {
335                self.update_gen(move |view| {
336                    let _: Ignore = view.set_system_shutdown_warn(reason);
337                })
338            }
339
340            if prev_state != new_state {
341                self.update_gen(move |view| {
342                    let _: Ignore = view.set_state(new_state);
343                })
344            }
345        }
346
347        if let Some(font_size) = self.vars.font_size().get_new()
348            && let Some(m) = &self.monitor
349        {
350            let scale_factor = m.scale_factor().get();
351            let screen_density = m.density().get();
352            let screen_size = m.size().get();
353            let mut font_size_px = self.content.outer_layout(scale_factor, screen_density, screen_size, || {
354                font_size.layout_dft_x(Length::pt_to_px(11.0, scale_factor))
355            });
356            if font_size_px < Px(0) {
357                tracing::error!("invalid font size {font_size:?} => {font_size_px:?}");
358                font_size_px = Length::pt_to_px(11.0, scale_factor);
359            }
360            let font_size_dip = font_size_px.to_dip(scale_factor);
361
362            if font_size_dip != self.root_font_size {
363                self.root_font_size = font_size_dip;
364                UPDATES.layout_window(WINDOW.id());
365            }
366        }
367
368        #[cfg(feature = "image")]
369        let mut img_res_loading = vec![];
370
371        // icon:
372        #[cfg(feature = "image")]
373        let mut send_icon = false;
374        #[cfg(feature = "image")]
375        if let Some(ico) = self.vars.icon().get_new() {
376            self.img_res.icon_var = match ico {
377                WindowIcon::Default => None,
378                WindowIcon::Image(ImageSource::Render(ico, _)) => {
379                    Some(IMAGES.cache(ImageSource::Render(ico.clone(), Some(ImageRenderArgs::new(WINDOW.id())))))
380                }
381                WindowIcon::Image(source) => Some(IMAGES.cache(source)),
382            };
383
384            if let Some(ico) = &self.img_res.icon_var {
385                self.img_res.icon_binding = ico.bind_map(&self.vars.0.actual_icon, |img| Some(img.clone()));
386
387                if ico.get().is_loading() && self.window.is_none() && !self.waiting_view {
388                    img_res_loading.push(ico.clone());
389                }
390            } else {
391                self.vars.0.actual_icon.set(None);
392                self.img_res.icon_binding = VarHandle::dummy();
393            }
394
395            send_icon = true;
396        } else if self.img_res.icon_var.as_ref().map(|ico| ico.is_new()).unwrap_or(false) {
397            send_icon = true;
398        }
399        #[cfg(feature = "image")]
400        if send_icon {
401            let icon = self.img_res.icon_var.as_ref().and_then(|ico| ico.get().view().cloned());
402            self.update_gen(move |view| {
403                let _: Ignore = view.set_icon(icon.as_ref());
404            });
405        }
406
407        // cursor (image):
408        if let Some(cursor) = self.vars.cursor().get_new() {
409            match cursor {
410                crate::CursorSource::Icon(ico) => {
411                    #[cfg(feature = "image")]
412                    {
413                        self.img_res.cursor_var = None;
414                    }
415                    self.update_gen(move |view| {
416                        let _: Ignore = view.set_cursor(Some(ico));
417                        #[cfg(feature = "image")]
418                        let _: Ignore = view.set_cursor_image(None, PxPoint::zero());
419                    });
420                }
421                #[cfg(feature = "image")]
422                crate::CursorSource::Img(img) => {
423                    self.img_res.cursor_var = Some(match img.source {
424                        ImageSource::Render(cur, _) => {
425                            IMAGES.cache(ImageSource::Render(cur.clone(), Some(ImageRenderArgs::new(WINDOW.id()))))
426                        }
427                        source => IMAGES.cache(source),
428                    });
429
430                    self.update_gen(move |view| {
431                        let _: Ignore = view.set_cursor(Some(img.fallback));
432                        let _: Ignore = view.set_cursor_image(None, PxPoint::zero());
433                    });
434                }
435                crate::CursorSource::Hidden => {
436                    #[cfg(feature = "image")]
437                    {
438                        self.img_res.cursor_var = None;
439                    }
440                    self.update_gen(move |view| {
441                        let _: Ignore = view.set_cursor(None);
442                        #[cfg(feature = "image")]
443                        let _: Ignore = view.set_cursor_image(None, PxPoint::zero());
444                    });
445                }
446            }
447
448            #[cfg(feature = "image")]
449            if let Some(cur) = &self.img_res.cursor_var {
450                let hotspot = self.vars.cursor().with(|i| i.hotspot().cloned().unwrap_or_default());
451
452                let cursor_img_to_actual = move |img: &Img| -> Option<(Img, PxPoint)> {
453                    let hotspot = if img.is_loaded() {
454                        let mut metrics = LayoutMetrics::new(1.fct(), img.size(), Px(16));
455                        if let Some(density) = img.density() {
456                            metrics = metrics.with_screen_density(density.width);
457                        }
458
459                        LAYOUT.with_context(metrics, || hotspot.layout())
460                    } else {
461                        PxPoint::zero()
462                    };
463
464                    Some((img.clone(), hotspot))
465                };
466                self.vars.0.actual_cursor_img.set_from_map(cur, cursor_img_to_actual.clone());
467                self.img_res.cursor_binding = cur.bind_map(&self.vars.0.actual_cursor_img, cursor_img_to_actual);
468
469                if cur.get().is_loading() && self.window.is_none() && !self.waiting_view {
470                    img_res_loading.push(cur.clone());
471                }
472            } else {
473                self.vars.0.actual_cursor_img.set(None);
474                self.img_res.cursor_binding = VarHandle::dummy();
475            }
476        }
477        #[cfg(feature = "image")]
478        if let Some(img_hotspot) = self.vars.0.actual_cursor_img.get_new() {
479            self.update_gen(move |view| match img_hotspot {
480                Some((img, hotspot)) => {
481                    let _: Ignore = view.set_cursor_image(img.view(), hotspot);
482                }
483                None => {
484                    let _: Ignore = view.set_cursor_image(None, PxPoint::zero());
485                }
486            })
487        }
488
489        // setup init wait for images
490        #[cfg(feature = "image")]
491        if !img_res_loading.is_empty() {
492            if self.img_res.deadline.has_elapsed() {
493                UPDATES.layout_window(WINDOW.id());
494            } else {
495                let window_id = WINDOW.id();
496                TIMERS
497                    .on_deadline(
498                        self.img_res.deadline,
499                        hn_once!(|_| {
500                            if img_res_loading.iter().any(|i| i.get().is_loading()) {
501                                // window maybe still waiting.
502                                UPDATES.layout_window(window_id);
503                            }
504                        }),
505                    )
506                    .perm();
507            }
508        }
509
510        if let Some(title) = self.vars.title().get_new() {
511            self.update_gen(move |view| {
512                let _: Ignore = view.set_title(title);
513            });
514        }
515
516        if let Some(mode) = self.vars.video_mode().get_new() {
517            self.update_gen(move |view| {
518                let _: Ignore = view.set_video_mode(mode);
519            });
520        }
521
522        if let Some(visible) = self.vars.taskbar_visible().get_new() {
523            self.update_gen(move |view| {
524                let _: Ignore = view.set_taskbar_visible(visible);
525            });
526        }
527
528        if let Some(top) = self.vars.always_on_top().get_new() {
529            self.update_gen(move |view| {
530                let _: Ignore = view.set_always_on_top(top);
531            });
532        }
533
534        #[cfg(feature = "image")]
535        if let Some(mode) = self.vars.frame_capture_mode().get_new() {
536            self.update_gen(move |view| {
537                let _: Ignore = view.set_capture_mode(matches!(mode, FrameCaptureMode::All));
538            });
539        }
540
541        if let Some(m) = &self.monitor {
542            if let Some(fct) = m.scale_factor().get_new() {
543                self.vars.0.scale_factor.set(fct);
544            }
545            if m.scale_factor().is_new() || m.size().is_new() || m.density().is_new() {
546                UPDATES.layout_window(WINDOW.id());
547            }
548        }
549
550        if let Some(indicator) = self.vars.focus_indicator().get_new() {
551            if WINDOWS.is_focused(WINDOW.id()).unwrap_or(false) {
552                self.vars.focus_indicator().set(None);
553            } else if let Some(view) = &self.window {
554                let _ = view.set_focus_indicator(indicator);
555                // will be set to `None` once the window is focused.
556            }
557            // else indicator is send with init.
558        }
559
560        let mut update_colors = false;
561
562        if update_parent(&mut self.actual_parent, &self.vars) {
563            self.parent_color_scheme = self
564                .actual_parent
565                .and_then(|id| WINDOWS.vars(id).ok().map(|v| v.actual_color_scheme()));
566            self.parent_accent_color = self
567                .actual_parent
568                .and_then(|id| WINDOWS.vars(id).ok().map(|v| v.actual_accent_color()));
569            update_colors = true;
570        }
571
572        if update_colors || self.vars.color_scheme().is_new() || self.parent_color_scheme.as_ref().map(|t| t.is_new()).unwrap_or(false) {
573            let scheme = self
574                .vars
575                .color_scheme()
576                .get()
577                .or_else(|| self.parent_color_scheme.as_ref().map(|t| t.get()))
578                .unwrap_or_else(|| WINDOWS.system_colors_config().scheme);
579            self.vars.0.actual_color_scheme.set(scheme);
580        }
581        if update_colors || self.vars.accent_color().is_new() || self.parent_accent_color.as_ref().map(|t| t.is_new()).unwrap_or(false) {
582            let accent = self
583                .vars
584                .accent_color()
585                .get()
586                .or_else(|| self.parent_accent_color.as_ref().map(|t| t.get()))
587                .unwrap_or_else(|| WINDOWS.system_colors_config().accent.into());
588            self.vars.0.actual_accent_color.set(accent);
589        }
590
591        if self.vars.0.access_enabled.is_new() {
592            UPDATES.update_info_window(WINDOW.id());
593        } else if self.vars.0.access_enabled.get() == AccessEnabled::VIEW && WINDOW_FOCUS.focused().is_new() {
594            self.update_access_focused();
595        }
596
597        if super::IME_EVENT.has_subscribers() && WINDOW_FOCUS.focused().is_new() {
598            self.update_ime();
599        }
600
601        self.content.update(update_widgets);
602    }
603
604    pub fn pre_event(&mut self, update: &EventUpdate) {
605        if let Some(args) = RAW_WINDOW_CHANGED_EVENT.on(update) {
606            if args.window_id == WINDOW.id() {
607                let mut state_change = None;
608                let mut pos_change = None;
609                let mut size_change = None;
610
611                if let Some(monitor) = args.monitor
612                    && self.vars.0.actual_monitor.get().map(|m| m != monitor).unwrap_or(true)
613                {
614                    self.vars.0.actual_monitor.set(Some(monitor));
615                    self.monitor = MONITORS.monitor(monitor);
616                    if let Some(m) = &self.monitor {
617                        let fct = m.scale_factor().get();
618                        self.vars.0.scale_factor.set(fct);
619                    }
620                    UPDATES.layout_window(WINDOW.id());
621                }
622
623                if let Some(state) = args.state.clone() {
624                    self.vars.state().set(state.state);
625                    self.vars.0.restore_rect.set(state.restore_rect);
626                    self.vars.0.restore_state.set(state.restore_state);
627
628                    let new_state = state.state;
629                    if self.actual_state != Some(new_state) {
630                        let prev_state = self.actual_state.unwrap_or(WindowState::Normal);
631                        state_change = Some((prev_state, new_state));
632                        self.actual_state = Some(new_state);
633
634                        match (prev_state, new_state) {
635                            (_, WindowState::Minimized) => {
636                                // minimized, minimize children.
637                                self.vars.0.children.with(|c| {
638                                    for &c in c.iter() {
639                                        MINIMIZE_CMD.scoped(c).notify();
640                                    }
641                                });
642                            }
643                            (WindowState::Minimized, _) => {
644                                // restored, restore children.
645                                self.vars.0.children.with(|c| {
646                                    for &c in c.iter() {
647                                        RESTORE_CMD.scoped(c).notify();
648                                    }
649                                });
650
651                                // we skip layout & render when minimized.
652                                let w_id = WINDOW.id();
653                                UPDATES.layout_window(w_id).render_window(w_id);
654                            }
655                            _ => {}
656                        }
657                    }
658
659                    self.state = Some(state);
660                }
661
662                if let Some((global_pos, pos)) = args.position
663                    && (self.vars.0.actual_position.get() != pos || self.vars.0.global_position.get() != global_pos)
664                {
665                    self.vars.0.actual_position.set(pos);
666                    self.vars.0.global_position.set(global_pos);
667                    pos_change = Some((global_pos, pos));
668                }
669
670                if let Some(size) = args.size
671                    && self.vars.0.actual_size.get() != size
672                {
673                    self.vars.0.actual_size.set(size);
674                    size_change = Some(size);
675
676                    UPDATES.layout_window(WINDOW.id());
677
678                    if args.cause == EventCause::System {
679                        // resize by system (user)
680                        self.vars.auto_size().set(AutoSize::DISABLED);
681                    }
682                }
683
684                if let Some(padding) = args.safe_padding {
685                    self.vars.0.safe_padding.set(padding);
686                }
687
688                if let Some(id) = args.frame_wait_id {
689                    self.resize_wait_id = Some(id);
690
691                    UPDATES.render_update_window(WINDOW.id());
692                }
693
694                if state_change.is_some() || pos_change.is_some() || size_change.is_some() {
695                    let args = WindowChangedArgs::new(
696                        args.timestamp,
697                        args.propagation().clone(),
698                        args.window_id,
699                        state_change,
700                        pos_change,
701                        size_change,
702                        args.cause,
703                    );
704                    WINDOW_CHANGED_EVENT.notify(args);
705                }
706            } else if self.actual_state.unwrap_or(WindowState::Normal) == WindowState::Minimized
707                && args.state.as_ref().map(|s| s.state != WindowState::Minimized).unwrap_or(false)
708                && self.vars.0.children.with(|c| c.contains(&args.window_id))
709            {
710                // child restored.
711                RESTORE_CMD.scoped(WINDOW.id()).notify();
712            }
713        } else if let Some(args) = RAW_WINDOW_FOCUS_EVENT.on(update) {
714            if args.new_focus == Some(WINDOW.id()) {
715                self.vars.0.children.with(|c| {
716                    for &c in c.iter() {
717                        let _ = WINDOWS.bring_to_top(c);
718                    }
719                });
720            } else if let Some(new_focus) = args.new_focus {
721                self.vars.0.children.with(|c| {
722                    if c.contains(&new_focus) {
723                        let _ = WINDOWS.bring_to_top(WINDOW.id());
724
725                        for c in c.iter() {
726                            if *c != new_focus {
727                                let _ = WINDOWS.bring_to_top(WINDOW.id());
728                            }
729                        }
730
731                        let _ = WINDOWS.bring_to_top(new_focus);
732                    }
733                });
734            }
735        } else if let Some(args) = MONITORS_CHANGED_EVENT.on(update) {
736            if let Some(m) = &self.monitor
737                && args.removed.contains(&m.id())
738            {
739                self.monitor = None;
740                self.vars.0.actual_monitor.set(None);
741            }
742            self.vars.monitor().update();
743        } else if let Some(args) = RAW_WINDOW_OPEN_EVENT.on(update) {
744            if args.window_id == WINDOW.id() {
745                self.waiting_view = false;
746
747                WINDOWS.set_view(args.window_id, args.window.clone().into());
748
749                self.window = Some(args.window.clone());
750                self.cancel_ime_handle = super::cmd::CANCEL_IME_CMD.scoped(WINDOW.id()).subscribe(true);
751                self.open_title_menu_handle = super::cmd::OPEN_TITLE_BAR_CONTEXT_MENU_CMD.scoped(WINDOW.id()).subscribe(true);
752                self.drag_move_handle = super::cmd::DRAG_MOVE_RESIZE_CMD.scoped(WINDOW.id()).subscribe(true);
753
754                self.vars.0.render_mode.set(args.data.render_mode);
755                self.vars.state().set(args.data.state.state);
756                self.actual_state = Some(args.data.state.state);
757                self.vars.0.restore_state.set(args.data.state.restore_state);
758                self.vars.0.restore_rect.set(args.data.state.restore_rect);
759                self.vars.0.global_position.set(args.data.position.0);
760                self.vars.0.actual_position.set(args.data.position.1);
761                self.vars.0.actual_size.set(args.data.size);
762                self.vars.0.safe_padding.set(args.data.safe_padding);
763                self.vars.0.actual_monitor.set(args.data.monitor);
764                self.vars.0.scale_factor.set(args.data.scale_factor);
765
766                self.state = Some(args.data.state.clone());
767
768                let scheme = self
769                    .vars
770                    .color_scheme()
771                    .get()
772                    .or_else(|| self.parent_color_scheme.as_ref().map(|t| t.get()))
773                    .unwrap_or_else(|| WINDOWS.system_colors_config().scheme);
774                self.vars.0.actual_color_scheme.set(scheme);
775                let accent = self
776                    .vars
777                    .accent_color()
778                    .get()
779                    .or_else(|| self.parent_accent_color.as_ref().map(|t| t.get()))
780                    .unwrap_or_else(|| WINDOWS.system_colors_config().accent.into());
781                self.vars.0.actual_accent_color.set(accent);
782
783                UPDATES.layout_window(args.window_id).render_window(args.window_id);
784
785                for update in mem::take(&mut self.delayed_view_updates) {
786                    update(&args.window);
787                }
788            }
789        } else if let Some(args) = RAW_COLORS_CONFIG_CHANGED_EVENT.on(update) {
790            let scheme = self
791                .vars
792                .color_scheme()
793                .get()
794                .or_else(|| self.parent_color_scheme.as_ref().map(|t| t.get()))
795                .unwrap_or(args.config.scheme);
796            self.vars.0.actual_color_scheme.set(scheme);
797            let color = self
798                .vars
799                .accent_color()
800                .get()
801                .or_else(|| self.parent_accent_color.as_ref().map(|t| t.get()))
802                .unwrap_or_else(|| args.config.accent.into());
803            self.vars.0.actual_accent_color.set(color);
804        } else if let Some(args) = RAW_WINDOW_OR_HEADLESS_OPEN_ERROR_EVENT.on(update) {
805            let w_id = WINDOW.id();
806            if args.window_id == w_id && self.window.is_none() && self.waiting_view {
807                tracing::error!("view-process failed to open a window, {}", args.error);
808
809                // was waiting view and failed, treat like a respawn.
810
811                self.waiting_view = false;
812                self.delayed_view_updates = vec![];
813                self.respawned = true;
814
815                UPDATES.layout_window(w_id).render_window(w_id);
816            }
817        } else if let Some(args) = RAW_IME_EVENT.on(update) {
818            let w_id = WINDOW.id();
819            if args.window_id == w_id {
820                match &args.ime {
821                    Ime::Preview(s, c) => {
822                        if let Some(info) = &mut self.ime_info {
823                            info.has_preview = !s.is_empty();
824                            let args = super::ImeArgs::now(info.target.clone(), s.clone(), *c);
825                            super::IME_EVENT.notify(args);
826                        }
827                    }
828                    Ime::Commit(s) => {
829                        if let Some(info) = &mut self.ime_info {
830                            info.has_preview = false;
831                            let args = super::ImeArgs::now(info.target.clone(), s.clone(), None);
832                            super::IME_EVENT.notify(args);
833                        }
834                    }
835                }
836            }
837        } else if let Some(args) = ACCESS_INITED_EVENT.on(update) {
838            if args.window_id == WINDOW.id() {
839                tracing::info!("accessibility info enabled in view for {:?}", args.window_id);
840                self.vars.0.access_enabled.set(AccessEnabled::VIEW);
841            }
842        } else if let Some(args) = ACCESS_DEINITED_EVENT.on(update) {
843            if args.window_id == WINDOW.id() {
844                tracing::info!("accessibility info disabled in view for {:?}", args.window_id);
845                self.vars.0.access_enabled.modify(|a| {
846                    if a.is_enabled() {
847                        **a = AccessEnabled::APP;
848                    }
849                });
850            }
851        } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update)
852            && self
853                .window
854                .as_ref()
855                .map(|w| w.renderer().generation() != Ok(args.generation))
856                .unwrap_or(args.is_respawn)
857        {
858            debug_assert!(args.is_respawn);
859
860            self.window = None;
861            self.cancel_ime_handle = CommandHandle::dummy();
862            self.open_title_menu_handle = CommandHandle::dummy();
863            self.drag_move_handle = CommandHandle::dummy();
864            self.waiting_view = false;
865            self.delayed_view_updates = vec![];
866            self.respawned = true;
867
868            let w_id = WINDOW.id();
869            UPDATES.layout_window(w_id).render_window(w_id);
870        }
871
872        self.content.pre_event(update);
873
874        if self.ime_info.is_some() && super::cmd::CANCEL_IME_CMD.scoped(WINDOW.id()).has(update) {
875            let prev = self.ime_info.take().unwrap();
876            if prev.has_preview {
877                let args = super::ImeArgs::now(prev.target, "", (0, 0));
878                super::IME_EVENT.notify(args);
879            }
880            if let Some(w) = &self.window {
881                let _ = w.set_ime_area(None);
882            }
883        } else if let Some(args) = super::cmd::DRAG_MOVE_RESIZE_CMD.scoped(WINDOW.id()).on(update) {
884            let r = args.handle_enabled(&self.drag_move_handle, |args| args.param::<crate::cmd::ResizeDirection>().copied());
885            if let Some(r) = r {
886                self.view_task(Box::new(move |w| {
887                    let _ = if let Some(r) = r {
888                        w.unwrap().drag_resize(r)
889                    } else {
890                        w.unwrap().drag_move()
891                    };
892                }));
893            }
894        } else if let Some(args) = super::cmd::OPEN_TITLE_BAR_CONTEXT_MENU_CMD.scoped(WINDOW.id()).on(update) {
895            let pos = args.handle_enabled(&self.open_title_menu_handle, |args| {
896                if let Some(p) = args.param::<DipPoint>() {
897                    *p
898                } else if let Some(p) = args.param::<PxPoint>() {
899                    p.to_dip(self.vars.scale_factor().get())
900                } else {
901                    DipPoint::splat(Dip::new(24))
902                }
903            });
904            if let Some(pos) = pos {
905                self.view_task(Box::new(move |w| {
906                    let _ = w.unwrap().open_title_bar_context_menu(pos);
907                }));
908            }
909        };
910    }
911
912    pub fn ui_event(&mut self, update: &EventUpdate) {
913        self.content.ui_event(update);
914    }
915
916    #[must_use]
917    pub fn info(&mut self, info_widgets: Arc<InfoUpdates>) -> Option<WidgetInfoTree> {
918        let prev_tree = WINDOW.info();
919        let info = self.content.info(info_widgets);
920        if let Some(info) = &info
921            && self.window.is_some()
922        {
923            // updated widget info and has view-process window
924            if info.access_enabled() == AccessEnabled::VIEW && self.render_access_update.is_none() {
925                // view window requires access info, next frame
926                self.render_access_update = Some(prev_tree);
927                UPDATES.render_window(WINDOW.id());
928            } else if self.ime_info.is_some() {
929                UPDATES.render_window(WINDOW.id());
930            }
931        }
932
933        info
934    }
935
936    fn accessible_focused(&self, info: &WidgetInfoTree) -> Option<WidgetId> {
937        if WINDOWS.is_focused(info.window_id()).unwrap_or(false) {
938            WINDOW_FOCUS.focused().with(|p| {
939                if let Some(p) = p
940                    && p.window_id() == info.window_id()
941                    && let Some(wgt) = info.get(p.widget_id())
942                    && let Some(wgt) = wgt.access()
943                    && wgt.is_accessible()
944                {
945                    // is focused accessible widget inside window
946                    return Some(wgt.info().id());
947                }
948                None
949            })
950        } else {
951            None
952        }
953    }
954
955    fn update_access_focused(&mut self) {
956        if self.render_access_update.is_some() {
957            // will update next frame
958            return;
959        }
960        if let Some(view) = &self.window {
961            let info = WINDOW.info();
962            if info.access_enabled().is_enabled() {
963                let _ = view.access_update(zng_view_api::access::AccessTreeUpdate::new(
964                    vec![],
965                    None,
966                    self.accessible_focused(&info).unwrap_or(info.root().id()).into(),
967                ));
968            }
969        }
970    }
971
972    fn update_ime(&mut self) {
973        WINDOW_FOCUS.focused().with(|f| {
974            let mut ime_path = None;
975            if let Some(f) = f
976                && f.interactivity().is_enabled()
977                && f.window_id() == WINDOW.id()
978                && super::IME_EVENT.is_subscriber(f.widget_id())
979            {
980                ime_path = Some(f.as_path().clone());
981            }
982
983            if ime_path.as_ref() == self.ime_info.as_ref().map(|p| &p.target) {
984                return;
985            }
986
987            if let Some(p) = ime_path {
988                let info = WINDOW.info();
989                if let Some(w) = info.get(p.widget_id()) {
990                    if let Some(prev) = self.ime_info.take()
991                        && prev.has_preview
992                    {
993                        // clear
994                        let args = super::ImeArgs::now(prev.target, "", (0, 0));
995                        super::IME_EVENT.notify(args);
996                    }
997
998                    self.ime_info = Some(ImeInfo {
999                        target: p.clone(),
1000                        has_preview: false,
1001                        area: DipRect::zero(),
1002                    });
1003
1004                    if let Some(win) = &self.window {
1005                        let area = w.ime_area().to_dip(info.scale_factor());
1006                        self.ime_info.as_mut().unwrap().area = area;
1007
1008                        // set to `None` to force a refresh, some IME (MS Emoji) behave like
1009                        // they are in the same widget still if only the position changes
1010                        let _ = win.set_ime_area(None);
1011                        let _ = win.set_ime_area(Some(area));
1012                    }
1013                    return;
1014                }
1015            }
1016
1017            if let Some(prev) = self.ime_info.take() {
1018                if let Some(w) = &self.window {
1019                    let _ = w.set_ime_area(None);
1020                }
1021
1022                if prev.has_preview {
1023                    // clear
1024                    let args = super::ImeArgs::now(prev.target, "", (0, 0));
1025                    super::IME_EVENT.notify(args);
1026                }
1027            }
1028        });
1029    }
1030
1031    pub fn layout(&mut self, layout_widgets: Arc<LayoutUpdates>) {
1032        if !layout_widgets.delivery_list().enter_window(WINDOW.id()) {
1033            return;
1034        }
1035
1036        if self.window.is_some() {
1037            if matches!(self.state.as_ref().map(|s| s.state), Some(WindowState::Minimized)) {
1038                return;
1039            }
1040            self.layout_update(layout_widgets);
1041        } else if self.respawned && !self.waiting_view {
1042            self.layout_respawn();
1043        } else if !self.waiting_view {
1044            self.layout_init();
1045        }
1046    }
1047
1048    /// First layout, opens the window.
1049    fn layout_init(&mut self) {
1050        // await images load up to 1s.
1051        #[cfg(feature = "image")]
1052        if self.img_res.deadline.has_elapsed() {
1053            if let Some(icon) = &self.img_res.icon_var
1054                && icon.get().is_loading()
1055            {
1056                return;
1057            }
1058            if let Some(cursor) = &self.img_res.cursor_var
1059                && cursor.get().is_loading()
1060            {
1061                return;
1062            }
1063        }
1064        // update window "load" state, `is_loaded` and the `WindowLoadEvent` happen here.
1065        if !WINDOWS.try_load(WINDOW.id()) {
1066            // block on loading handles.
1067            return;
1068        }
1069
1070        self.monitor = Some(self.vars.monitor().get().select_fallback());
1071        let m = self.monitor.as_ref().unwrap();
1072        self.vars.0.scale_factor.set(m.scale_factor().get());
1073
1074        let scale_factor = m.scale_factor().get();
1075        let screen_density = m.density().get();
1076        let screen_rect = m.px_rect();
1077
1078        // Layout min, max and size in the monitor space.
1079        let (min_size, max_size, mut size, root_font_size) =
1080            self.content.outer_layout(scale_factor, screen_density, screen_rect.size, || {
1081                let min_size = self.vars.min_size().layout_dft(default_min_size(scale_factor));
1082                let max_size = self.vars.max_size().layout_dft(screen_rect.size);
1083                let size = self.vars.size().layout_dft(default_size(scale_factor));
1084
1085                let font_size = self.vars.font_size().get();
1086                let mut root_font_size = font_size.layout_dft_x(Length::pt_to_px(11.0, scale_factor));
1087                if root_font_size < Px(0) {
1088                    tracing::error!("invalid font size {font_size:?} => {root_font_size:?}");
1089                    root_font_size = Length::pt_to_px(11.0, scale_factor);
1090                }
1091
1092                (min_size, max_size, size.min(max_size).max(min_size), root_font_size)
1093            });
1094
1095        self.root_font_size = root_font_size.to_dip(scale_factor);
1096
1097        let state = self.vars.state().get();
1098        if state == WindowState::Normal && self.vars.auto_size().get() != AutoSize::DISABLED {
1099            // layout content to get auto-size size.
1100            size = self.content.layout(
1101                Arc::default(),
1102                scale_factor,
1103                screen_density,
1104                min_size,
1105                max_size,
1106                size,
1107                root_font_size,
1108                false,
1109            );
1110        }
1111
1112        // Layout initial position in the monitor space.
1113        let mut system_pos = false;
1114        let position = match self.start_position {
1115            StartPosition::Default => {
1116                let pos = self.vars.position().get();
1117                if pos.x.is_default() || pos.y.is_default() {
1118                    system_pos = true;
1119                    screen_rect.origin + PxVector::splat(Px(40))
1120                } else {
1121                    self.content.outer_layout(scale_factor, screen_density, screen_rect.size, || {
1122                        pos.layout() + screen_rect.origin.to_vector()
1123                    })
1124                }
1125            }
1126            StartPosition::CenterMonitor => {
1127                PxPoint::new(
1128                    (screen_rect.size.width - size.width) / Px(2),
1129                    (screen_rect.size.height - size.height) / Px(2),
1130                ) + screen_rect.origin.to_vector()
1131            }
1132            StartPosition::CenterParent => {
1133                // center monitor if no parent
1134                let mut parent_rect = screen_rect;
1135
1136                if let Some(parent) = self.vars.parent().get()
1137                    && let Ok(w) = WINDOWS.vars(parent)
1138                {
1139                    let factor = w.scale_factor().get();
1140                    let pos = w.actual_position().get().to_px(factor);
1141                    let size = w.actual_size().get().to_px(factor);
1142
1143                    parent_rect = PxRect::new(pos, size);
1144                }
1145
1146                PxPoint::new(
1147                    (parent_rect.size.width - size.width) / Px(2),
1148                    (parent_rect.size.height - size.height) / Px(2),
1149                ) + parent_rect.origin.to_vector()
1150            }
1151        };
1152
1153        // send view window request:
1154
1155        let m_position = (position - screen_rect.origin.to_vector()).to_dip(scale_factor);
1156        let size = size.to_dip(scale_factor);
1157
1158        let state = WindowStateAll::new(
1159            state,
1160            position,
1161            DipRect::new(m_position, size),
1162            WindowState::Normal,
1163            min_size.to_dip(scale_factor),
1164            max_size.to_dip(scale_factor),
1165            self.vars.chrome().get() && !WINDOWS.system_chrome().get().needs_custom(),
1166        );
1167
1168        let window_id = WINDOW.id();
1169
1170        let request = WindowRequest::new(
1171            zng_view_api::window::WindowId::from_raw(window_id.get()),
1172            self.vars.title().get(),
1173            state.clone(),
1174            self.kiosk.is_some(),
1175            system_pos,
1176            self.vars.video_mode().get(),
1177            self.vars.visible().get(),
1178            self.vars.taskbar_visible().get(),
1179            self.vars.always_on_top().get(),
1180            self.vars.movable().get(),
1181            self.vars.resizable().get(),
1182            {
1183                #[cfg(feature = "image")]
1184                {
1185                    self.img_res
1186                        .icon_var
1187                        .as_ref()
1188                        .and_then(|ico| ico.get().view().map(|ico| ico.id()))
1189                        .flatten()
1190                }
1191                #[cfg(not(feature = "image"))]
1192                None
1193            },
1194            self.vars.cursor().with(|c| c.icon()),
1195            {
1196                #[cfg(feature = "image")]
1197                {
1198                    self.vars
1199                        .actual_cursor_img()
1200                        .get()
1201                        .and_then(|(i, h)| i.view().and_then(|i| i.id()).map(|i| (i, h)))
1202                }
1203                #[cfg(not(feature = "image"))]
1204                None
1205            },
1206            self.transparent,
1207            {
1208                #[cfg(feature = "image")]
1209                {
1210                    matches!(self.vars.frame_capture_mode().get(), FrameCaptureMode::All)
1211                }
1212
1213                #[cfg(not(feature = "image"))]
1214                false
1215            },
1216            self.render_mode.unwrap_or_else(|| WINDOWS.default_render_mode().get()),
1217            self.vars.focus_indicator().get(),
1218            self.start_focused,
1219            self.ime_info.as_ref().and_then(|a| {
1220                let area = WINDOW.info().get(a.target.widget_id())?.ime_area().to_dip(scale_factor);
1221                Some(area)
1222            }),
1223            self.vars.enabled_buttons().get(),
1224            self.vars.system_shutdown_warn().get(),
1225            WINDOWS.take_view_extensions_init(window_id),
1226        );
1227
1228        self.state = Some(state);
1229        if VIEW_PROCESS.open_window(request).is_err() {
1230            tracing::warn!("respawn on layout init");
1231            // layout_respawn will request the window
1232        }
1233        self.waiting_view = true;
1234    }
1235
1236    /// Layout for already open window.
1237    fn layout_update(&mut self, layout_widgets: Arc<LayoutUpdates>) {
1238        let m = self.monitor.as_ref().unwrap();
1239        let scale_factor = m.scale_factor().get();
1240        let screen_density = m.density().get();
1241
1242        let mut state = self.state.clone().unwrap();
1243
1244        let current_size = self.vars.0.actual_size.get().to_px(scale_factor);
1245        let mut size = current_size;
1246        let min_size = state.min_size.to_px(scale_factor);
1247        let max_size = state.max_size.to_px(scale_factor);
1248        let root_font_size = self.root_font_size.to_px(scale_factor);
1249
1250        let skip_auto_size = !matches!(state.state, WindowState::Normal);
1251
1252        if !skip_auto_size {
1253            let auto_size = self.vars.auto_size().get();
1254
1255            if auto_size.contains(AutoSize::CONTENT_WIDTH) {
1256                size.width = max_size.width;
1257            }
1258            if auto_size.contains(AutoSize::CONTENT_HEIGHT) {
1259                size.height = max_size.height;
1260            }
1261        }
1262
1263        let size = self.content.layout(
1264            layout_widgets,
1265            scale_factor,
1266            screen_density,
1267            min_size,
1268            max_size,
1269            size,
1270            root_font_size,
1271            skip_auto_size,
1272        );
1273
1274        if size != current_size {
1275            assert!(!skip_auto_size);
1276
1277            let auto_size_origin = self.vars.auto_size_origin().get();
1278            let auto_size_origin = |size| {
1279                let metrics = LayoutMetrics::new(scale_factor, size, root_font_size)
1280                    .with_screen_density(screen_density)
1281                    .with_direction(DIRECTION_VAR.get());
1282                LAYOUT.with_context(metrics, || auto_size_origin.layout().to_dip(scale_factor))
1283            };
1284            let prev_origin = auto_size_origin(current_size);
1285            let new_origin = auto_size_origin(size);
1286
1287            let size = size.to_dip(scale_factor);
1288
1289            state.restore_rect.size = size;
1290            state.restore_rect.origin += prev_origin - new_origin;
1291
1292            if let Some(view) = &self.window {
1293                let _: Ignore = view.set_state(state);
1294            } else {
1295                debug_assert!(self.respawned);
1296                self.state = Some(state);
1297            }
1298        }
1299    }
1300
1301    /// First layout after respawn, opens the window but used previous sizes.
1302    fn layout_respawn(&mut self) {
1303        if self.monitor.is_none() {
1304            self.monitor = Some(self.vars.monitor().get().select_fallback());
1305            let m = self.monitor.as_ref().unwrap();
1306            self.vars.0.scale_factor.set(m.scale_factor().get());
1307        }
1308
1309        self.layout_update(Arc::default());
1310
1311        let window_id = WINDOW.id();
1312
1313        let request = WindowRequest::new(
1314            zng_view_api::window::WindowId::from_raw(window_id.get()),
1315            self.vars.title().get(),
1316            self.state.clone().unwrap(),
1317            self.kiosk.is_some(),
1318            false,
1319            self.vars.video_mode().get(),
1320            self.vars.visible().get(),
1321            self.vars.taskbar_visible().get(),
1322            self.vars.always_on_top().get(),
1323            self.vars.movable().get(),
1324            self.vars.resizable().get(),
1325            {
1326                #[cfg(feature = "image")]
1327                {
1328                    self.img_res
1329                        .icon_var
1330                        .as_ref()
1331                        .and_then(|ico| ico.get().view().map(|ico| ico.id()))
1332                        .flatten()
1333                }
1334                #[cfg(not(feature = "image"))]
1335                None
1336            },
1337            self.vars.cursor().with(|c| c.icon()),
1338            {
1339                #[cfg(feature = "image")]
1340                {
1341                    self.vars
1342                        .actual_cursor_img()
1343                        .get()
1344                        .and_then(|(i, h)| i.view().and_then(|i| i.id()).map(|i| (i, h)))
1345                }
1346                #[cfg(not(feature = "image"))]
1347                None
1348            },
1349            self.transparent,
1350            {
1351                #[cfg(feature = "image")]
1352                {
1353                    matches!(self.vars.frame_capture_mode().get(), FrameCaptureMode::All)
1354                }
1355                #[cfg(not(feature = "image"))]
1356                false
1357            },
1358            self.render_mode.unwrap_or_else(|| WINDOWS.default_render_mode().get()),
1359            self.vars.focus_indicator().get(),
1360            WINDOWS.is_focused(WINDOW.id()).unwrap_or(false),
1361            self.ime_info.as_ref().and_then(|a| {
1362                let info = WINDOW.info();
1363                let area = info.get(a.target.widget_id())?.ime_area().to_dip(info.scale_factor());
1364                Some(area)
1365            }),
1366            self.vars.enabled_buttons().get(),
1367            self.vars.system_shutdown_warn().get(),
1368            WINDOWS.take_view_extensions_init(window_id),
1369        );
1370
1371        if let Ok(()) = VIEW_PROCESS.open_window(request) {
1372            self.waiting_view = true
1373        }
1374    }
1375
1376    pub fn render(&mut self, render_widgets: Arc<RenderUpdates>, render_update_widgets: Arc<RenderUpdates>) {
1377        let w_id = WINDOW.id();
1378        if !render_widgets.delivery_list().enter_window(w_id) && !render_update_widgets.delivery_list().enter_window(w_id) {
1379            return;
1380        }
1381
1382        if let Some(view) = &self.window {
1383            if matches!(self.state.as_ref().map(|s| s.state), Some(WindowState::Minimized)) {
1384                return;
1385            }
1386
1387            let scale_factor = self.monitor.as_ref().unwrap().scale_factor().get();
1388            self.content.render(
1389                Some(view.renderer()),
1390                scale_factor,
1391                self.resize_wait_id.take(),
1392                render_widgets,
1393                render_update_widgets,
1394            );
1395
1396            if let Some(prev_tree) = self.render_access_update.take() {
1397                let info = WINDOW.info();
1398                // info was rebuild before this frame
1399                if let Some(mut update) = info.to_access_updates(&prev_tree) {
1400                    // updated access info
1401                    update.focused = self.accessible_focused(&info).unwrap_or_else(|| info.root().id()).into();
1402                    let _ = view.access_update(update);
1403                }
1404            } else {
1405                let info = WINDOW.info();
1406                if info.access_enabled() == AccessEnabled::VIEW
1407                    && let Some(mut update) = info.to_access_updates_bounds()
1408                {
1409                    // updated transforms or visibility access info
1410                    update.focused = self.accessible_focused(&info).unwrap_or_else(|| info.root().id()).into();
1411                    let _ = view.access_update(update);
1412                }
1413            }
1414
1415            if let Some(ime) = &mut self.ime_info
1416                && let Some(w) = &self.window
1417            {
1418                let info = WINDOW.info();
1419                if let Some(wgt) = info.get(ime.target.widget_id()) {
1420                    let area = wgt.ime_area().to_dip(scale_factor);
1421                    if ime.area != area {
1422                        ime.area = area;
1423                        let _ = w.set_ime_area(Some(area));
1424                    }
1425                }
1426            }
1427        }
1428    }
1429
1430    pub fn focus(&mut self) {
1431        self.update_gen(|view| {
1432            let r = view.focus();
1433            if let Ok(FocusResult::AlreadyFocused) = r {
1434                let prev = WINDOWS.focused_window_id();
1435                let new = Some(WINDOW.id());
1436                if prev != new {
1437                    // probably prev is a nested window
1438                    RAW_WINDOW_FOCUS_EVENT.notify(RawWindowFocusArgs::now(prev, new));
1439                }
1440            }
1441        });
1442    }
1443
1444    pub fn start_drag_drop(&mut self, data: Vec<DragDropData>, allowed_effects: DragDropEffect) -> Result<DragDropId, DragDropError> {
1445        if let Some(view) = &self.window
1446            && let Ok(r) = view.start_drag_drop(data, allowed_effects)
1447        {
1448            return r;
1449        }
1450        Err(DragDropError::CannotStart("view not available".into()))
1451    }
1452
1453    pub fn drag_dropped(&mut self, drop_id: DragDropId, applied: DragDropEffect) {
1454        if let Some(view) = &self.window {
1455            let _ = view.drag_dropped(drop_id, applied);
1456        }
1457    }
1458
1459    pub fn bring_to_top(&mut self) {
1460        self.update_gen(|view| {
1461            let _ = view.bring_to_top();
1462        });
1463    }
1464
1465    pub fn close(&mut self) {
1466        self.content.close();
1467        self.window = None;
1468        self.cancel_ime_handle = CommandHandle::dummy();
1469        self.cancel_ime_handle = CommandHandle::dummy();
1470    }
1471
1472    fn view_task(&mut self, task: Box<dyn FnOnce(Option<&ViewWindow>) + Send>) {
1473        if let Some(view) = &self.window {
1474            task(Some(view));
1475        } else if self.waiting_view {
1476            self.delayed_view_updates.push(Box::new(move |v| task(Some(v))));
1477        } else {
1478            task(None);
1479        }
1480    }
1481}
1482
1483/// Respond to `parent_var` updates, returns `true` if the `parent` value has changed.
1484fn update_parent(parent: &mut Option<WindowId>, vars: &WindowVars) -> bool {
1485    let parent_var = vars.parent();
1486    if let Some(parent_id) = parent_var.get_new() {
1487        if parent_id == *parent {
1488            return false;
1489        }
1490
1491        match parent_id {
1492            Some(mut parent_id) => {
1493                if parent_id == WINDOW.id() {
1494                    tracing::error!("cannot set `{:?}` as it's own parent", parent_id);
1495                    parent_var.set(*parent);
1496                    return false;
1497                }
1498                if !vars.0.children.with(|c| c.is_empty()) {
1499                    tracing::error!("cannot set parent for `{:?}` because it already has children", WINDOW.id());
1500                    parent_var.set(*parent);
1501                    return false;
1502                }
1503
1504                if let Ok(parent_vars) = WINDOWS.vars(parent_id) {
1505                    // redirect to parent's parent.
1506                    if let Some(grand) = parent_vars.parent().get() {
1507                        tracing::debug!("using `{grand:?}` as parent, because it is the parent of requested `{parent_id:?}`");
1508                        parent_var.set(grand);
1509
1510                        parent_id = grand;
1511                        if Some(parent_id) == *parent {
1512                            return false;
1513                        }
1514                    }
1515
1516                    // remove previous
1517                    if let Some(parent_id) = parent.take()
1518                        && let Ok(parent_vars) = WINDOWS.vars(parent_id)
1519                    {
1520                        let id = WINDOW.id();
1521                        parent_vars.0.children.modify(move |c| {
1522                            c.remove(&id);
1523                        });
1524                    }
1525
1526                    // insert new
1527                    *parent = Some(parent_id);
1528                    let id = WINDOW.id();
1529                    parent_vars.0.children.modify(move |c| {
1530                        c.insert(id);
1531                    });
1532
1533                    true
1534                } else {
1535                    tracing::error!("cannot use `{:?}` as a parent because it does not exist", parent_id);
1536                    parent_var.set(*parent);
1537                    false
1538                }
1539            }
1540            None => {
1541                if let Some(parent_id) = parent.take() {
1542                    if let Ok(parent_vars) = WINDOWS.vars(parent_id) {
1543                        let id = WINDOW.id();
1544                        parent_vars.0.children.modify(move |c| {
1545                            c.remove(&id);
1546                        });
1547                    }
1548                    true
1549                } else {
1550                    false
1551                }
1552            }
1553        }
1554    } else {
1555        false
1556    }
1557}
1558
1559/// Implementer of `App <-> View` sync in a headless window.
1560struct HeadlessWithRendererCtrl {
1561    surface: Option<ViewHeadless>,
1562    waiting_view: bool,
1563    delayed_view_updates: Vec<Box<dyn FnOnce(&ViewHeadless) + Send>>,
1564    vars: WindowVars,
1565    content: ContentCtrl,
1566
1567    // init config.
1568    render_mode: Option<RenderMode>,
1569    headless_monitor: HeadlessMonitor,
1570    headless_simulator: HeadlessSimulator,
1571
1572    // current state.
1573    size: DipSize,
1574
1575    actual_parent: Option<WindowId>,
1576    /// actual_color_scheme and scale_factor binding.
1577    var_bindings: VarHandles,
1578}
1579impl HeadlessWithRendererCtrl {
1580    pub fn new(vars: &WindowVars, commands: WindowCommands, content: WindowRoot) -> Self {
1581        Self {
1582            surface: None,
1583            waiting_view: false,
1584            delayed_view_updates: vec![],
1585            vars: vars.clone(),
1586
1587            render_mode: content.render_mode,
1588            headless_monitor: content.headless_monitor,
1589            headless_simulator: HeadlessSimulator::new(),
1590
1591            content: ContentCtrl::new(vars.clone(), commands, content),
1592
1593            actual_parent: None,
1594            size: DipSize::zero(),
1595            var_bindings: VarHandles::dummy(),
1596        }
1597    }
1598
1599    pub fn update(&mut self, update_widgets: &WidgetUpdates) {
1600        if self.vars.0.access_enabled.is_new() {
1601            UPDATES.update_info_window(WINDOW.id());
1602        }
1603
1604        if self.surface.is_some() {
1605            if self.vars.size().is_new()
1606                || self.vars.min_size().is_new()
1607                || self.vars.max_size().is_new()
1608                || self.vars.auto_size().is_new()
1609                || self.vars.font_size().is_new()
1610            {
1611                UPDATES.layout_window(WINDOW.id());
1612            }
1613        } else {
1614            // we init on the first layout.
1615            UPDATES.layout_window(WINDOW.id());
1616        }
1617
1618        if update_parent(&mut self.actual_parent, &self.vars) || self.var_bindings.is_dummy() {
1619            self.var_bindings = update_headless_vars(self.headless_monitor.scale_factor, &self.vars);
1620        }
1621
1622        self.content.update(update_widgets);
1623    }
1624
1625    #[must_use]
1626    pub fn info(&mut self, info_widgets: Arc<InfoUpdates>) -> Option<WidgetInfoTree> {
1627        self.content.info(info_widgets)
1628    }
1629
1630    pub fn pre_event(&mut self, update: &EventUpdate) {
1631        if let Some(args) = RAW_HEADLESS_OPEN_EVENT.on(update) {
1632            if args.window_id == WINDOW.id() {
1633                self.waiting_view = false;
1634
1635                WINDOWS.set_view(args.window_id, args.surface.clone().into());
1636
1637                self.surface = Some(args.surface.clone());
1638                self.vars.0.render_mode.set(args.data.render_mode);
1639
1640                UPDATES.render_window(args.window_id);
1641
1642                for update in mem::take(&mut self.delayed_view_updates) {
1643                    update(&args.surface);
1644                }
1645            }
1646        } else if let Some(args) = RAW_WINDOW_OR_HEADLESS_OPEN_ERROR_EVENT.on(update) {
1647            if args.window_id == WINDOW.id() && self.surface.is_none() && self.waiting_view {
1648                tracing::error!("view-process failed to open a headless surface, {}", args.error);
1649
1650                // was waiting view and failed, treat like a respawn.
1651
1652                self.waiting_view = false;
1653                self.delayed_view_updates = vec![];
1654
1655                UPDATES.layout_window(args.window_id).render_window(args.window_id);
1656            }
1657        } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update)
1658            && let Some(view) = &self.surface
1659            && view.renderer().generation() != Ok(args.generation)
1660        {
1661            debug_assert!(args.is_respawn);
1662
1663            self.surface = None;
1664
1665            let w_id = WINDOW.id();
1666            UPDATES.layout_window(w_id).render_window(w_id);
1667        }
1668
1669        self.content.pre_event(update);
1670
1671        self.headless_simulator.pre_event(update);
1672    }
1673
1674    pub fn ui_event(&mut self, update: &EventUpdate) {
1675        self.content.ui_event(update);
1676    }
1677
1678    pub fn layout(&mut self, layout_widgets: Arc<LayoutUpdates>) {
1679        if !layout_widgets.delivery_list().enter_window(WINDOW.id()) {
1680            return;
1681        }
1682
1683        let scale_factor = self.vars.0.scale_factor.get();
1684        let screen_density = self.headless_monitor.density;
1685        let screen_size = self.headless_monitor.size.to_px(scale_factor);
1686
1687        let (min_size, max_size, size, root_font_size) = self.content.outer_layout(scale_factor, screen_density, screen_size, || {
1688            let min_size = self.vars.min_size().layout_dft(default_min_size(scale_factor));
1689            let max_size = self.vars.max_size().layout_dft(screen_size);
1690            let size = self.vars.size().layout_dft(default_size(scale_factor));
1691            let root_font_size = self.vars.font_size().layout_dft_x(Length::pt_to_px(11.0, scale_factor));
1692
1693            (min_size, max_size, size.min(max_size).max(min_size), root_font_size)
1694        });
1695
1696        let size = self.content.layout(
1697            layout_widgets,
1698            scale_factor,
1699            screen_density,
1700            min_size,
1701            max_size,
1702            size,
1703            root_font_size,
1704            false,
1705        );
1706        let size = size.to_dip(scale_factor);
1707
1708        if let Some(view) = &self.surface {
1709            // already has surface, maybe resize:
1710            if self.size != size {
1711                self.size = size;
1712                let _: Ignore = view.set_size(size, scale_factor);
1713            }
1714        } else if !self.waiting_view {
1715            // (re)spawn the view surface:
1716
1717            if !WINDOWS.try_load(WINDOW.id()) {
1718                return;
1719            }
1720
1721            let render_mode = self.render_mode.unwrap_or_else(|| WINDOWS.default_render_mode().get());
1722
1723            let window_id = WINDOW.id();
1724
1725            let r = VIEW_PROCESS.open_headless(HeadlessRequest::new(
1726                zng_view_api::window::WindowId::from_raw(window_id.get()),
1727                scale_factor,
1728                size,
1729                render_mode,
1730                WINDOWS.take_view_extensions_init(window_id),
1731            ));
1732
1733            if let Ok(()) = r {
1734                self.waiting_view = true
1735            }
1736        }
1737
1738        self.headless_simulator.layout();
1739    }
1740
1741    pub fn render(&mut self, render_widgets: Arc<RenderUpdates>, render_update_widgets: Arc<RenderUpdates>) {
1742        let w_id = WINDOW.id();
1743        if !render_widgets.delivery_list().enter_window(w_id) && !render_update_widgets.delivery_list().enter_window(w_id) {
1744            return;
1745        }
1746
1747        if let Some(view) = &self.surface {
1748            let fct = self.vars.0.scale_factor.get();
1749            self.content
1750                .render(Some(view.renderer()), fct, None, render_widgets, render_update_widgets);
1751        }
1752    }
1753
1754    pub fn focus(&mut self) {
1755        self.headless_simulator.focus();
1756    }
1757
1758    pub fn bring_to_top(&mut self) {
1759        self.headless_simulator.bring_to_top();
1760    }
1761
1762    pub fn close(&mut self) {
1763        self.content.close();
1764        self.surface = None;
1765    }
1766
1767    pub fn start_drag_drop(&mut self, data: Vec<DragDropData>, allowed_effects: DragDropEffect) -> Result<DragDropId, DragDropError> {
1768        let _ = (data, allowed_effects);
1769        Err(DragDropError::CannotStart("cannot start drag&drop from headless window".into()))
1770    }
1771
1772    pub fn drag_dropped(&mut self, drop_id: DragDropId, applied: DragDropEffect) {
1773        let _ = (drop_id, applied);
1774    }
1775
1776    fn view_task(&mut self, task: Box<dyn FnOnce(Option<&ViewWindow>) + Send>) {
1777        task(None)
1778    }
1779}
1780
1781fn update_headless_vars(m_factor: Option<Factor>, h_vars: &WindowVars) -> VarHandles {
1782    let mut handles = VarHandles::dummy();
1783
1784    if let Some(f) = m_factor {
1785        h_vars.0.scale_factor.set(f);
1786    }
1787
1788    if let Some(parent_vars) = h_vars.parent().get().and_then(|id| WINDOWS.vars(id).ok()) {
1789        // bind parent factor
1790        if m_factor.is_none() {
1791            h_vars.0.scale_factor.set_from(&parent_vars.0.scale_factor);
1792            handles.push(parent_vars.0.scale_factor.bind(&h_vars.0.scale_factor));
1793        }
1794
1795        // merge bind color scheme.
1796        let user = h_vars.color_scheme();
1797        let parent = &parent_vars.0.actual_color_scheme;
1798        let actual = &h_vars.0.actual_color_scheme;
1799
1800        handles.push(user.hook(clmv!(parent, actual, |args| {
1801            let value = *args.value();
1802            let scheme = value.unwrap_or_else(|| parent.get());
1803            actual.set(scheme);
1804            true
1805        })));
1806
1807        handles.push(parent.hook(clmv!(user, actual, |args| {
1808            let scheme = user.get().unwrap_or_else(|| *args.value());
1809            actual.set(scheme);
1810            true
1811        })));
1812
1813        actual.modify(clmv!(user, parent, |a| {
1814            let value = user.get().unwrap_or_else(|| parent.get());
1815            a.set(value);
1816        }));
1817    } else {
1818        // set-bind color scheme
1819        let from = h_vars.color_scheme();
1820        let to = &h_vars.0.actual_color_scheme;
1821
1822        to.set_from_map(&from, |&s| s.unwrap_or_default());
1823        handles.push(from.bind_map(to, |&s| s.unwrap_or_default()));
1824    }
1825
1826    handles
1827}
1828
1829/// implementer of `App` only content management.
1830struct HeadlessCtrl {
1831    vars: WindowVars,
1832    content: ContentCtrl,
1833
1834    headless_monitor: HeadlessMonitor,
1835    headless_simulator: HeadlessSimulator,
1836
1837    actual_parent: Option<WindowId>,
1838    /// actual_color_scheme and scale_factor binding.
1839    var_bindings: VarHandles,
1840}
1841impl HeadlessCtrl {
1842    pub fn new(vars: &WindowVars, commands: WindowCommands, content: WindowRoot) -> Self {
1843        Self {
1844            vars: vars.clone(),
1845            headless_monitor: content.headless_monitor,
1846            content: ContentCtrl::new(vars.clone(), commands, content),
1847            headless_simulator: HeadlessSimulator::new(),
1848            actual_parent: None,
1849            var_bindings: VarHandles::dummy(),
1850        }
1851    }
1852
1853    pub fn update(&mut self, update_widgets: &WidgetUpdates) {
1854        if self.vars.0.access_enabled.is_new() {
1855            UPDATES.update_info_window(WINDOW.id());
1856        }
1857
1858        if self.vars.size().is_new() || self.vars.min_size().is_new() || self.vars.max_size().is_new() || self.vars.auto_size().is_new() {
1859            UPDATES.layout_window(WINDOW.id());
1860        }
1861
1862        if matches!(self.content.init_state, InitState::Init) {
1863            let w_id = WINDOW.id();
1864            UPDATES.layout_window(w_id).render_window(w_id);
1865        }
1866
1867        if update_parent(&mut self.actual_parent, &self.vars) || self.var_bindings.is_dummy() {
1868            self.var_bindings = update_headless_vars(self.headless_monitor.scale_factor, &self.vars);
1869        }
1870
1871        self.content.update(update_widgets);
1872    }
1873
1874    #[must_use]
1875    pub fn info(&mut self, info_widgets: Arc<InfoUpdates>) -> Option<WidgetInfoTree> {
1876        self.content.info(info_widgets)
1877    }
1878
1879    pub fn pre_event(&mut self, update: &EventUpdate) {
1880        self.content.pre_event(update);
1881        self.headless_simulator.pre_event(update);
1882    }
1883
1884    pub fn ui_event(&mut self, update: &EventUpdate) {
1885        self.content.ui_event(update);
1886    }
1887
1888    pub fn layout(&mut self, layout_widgets: Arc<LayoutUpdates>) {
1889        let w_id = WINDOW.id();
1890        if !layout_widgets.delivery_list().enter_window(w_id) {
1891            return;
1892        }
1893
1894        if !WINDOWS.try_load(w_id) {
1895            return;
1896        }
1897
1898        let scale_factor = self.vars.0.scale_factor.get();
1899        let screen_density = self.headless_monitor.density;
1900        let screen_size = self.headless_monitor.size.to_px(scale_factor);
1901
1902        let (min_size, max_size, size, root_font_size) = self.content.outer_layout(scale_factor, screen_density, screen_size, || {
1903            let min_size = self.vars.min_size().layout_dft(default_min_size(scale_factor));
1904            let max_size = self.vars.max_size().layout_dft(screen_size);
1905            let size = self.vars.size().layout_dft(default_size(scale_factor));
1906            let root_font_size = self.vars.font_size().layout_dft_x(Length::pt_to_px(11.0, scale_factor));
1907
1908            (min_size, max_size, size.min(max_size).max(min_size), root_font_size)
1909        });
1910
1911        let _surface_size = self.content.layout(
1912            layout_widgets,
1913            scale_factor,
1914            screen_density,
1915            min_size,
1916            max_size,
1917            size,
1918            root_font_size,
1919            false,
1920        );
1921
1922        self.headless_simulator.layout();
1923    }
1924
1925    pub fn render(&mut self, render_widgets: Arc<RenderUpdates>, render_update_widgets: Arc<RenderUpdates>) {
1926        let w_id = WINDOW.id();
1927        if !render_widgets.delivery_list().enter_window(w_id) && !render_update_widgets.delivery_list().enter_window(w_id) {
1928            return;
1929        }
1930
1931        // layout and render cannot happen yet
1932        if !WINDOWS.try_load(w_id) {
1933            return;
1934        }
1935
1936        let fct = self.vars.0.scale_factor.get();
1937        self.content.render(None, fct, None, render_widgets, render_update_widgets);
1938    }
1939
1940    pub fn focus(&mut self) {
1941        self.headless_simulator.focus();
1942    }
1943
1944    pub fn bring_to_top(&mut self) {
1945        self.headless_simulator.bring_to_top();
1946    }
1947
1948    pub fn close(&mut self) {
1949        self.content.close();
1950    }
1951
1952    pub fn start_drag_drop(&mut self, data: Vec<DragDropData>, allowed_effects: DragDropEffect) -> Result<DragDropId, DragDropError> {
1953        let _ = (data, allowed_effects);
1954        Err(DragDropError::CannotStart("cannot start drag&drop from headless window".into()))
1955    }
1956
1957    pub fn drag_dropped(&mut self, drop_id: DragDropId, applied: DragDropEffect) {
1958        let _ = (drop_id, applied);
1959    }
1960
1961    fn view_task(&mut self, task: Box<dyn FnOnce(Option<&ViewWindow>) + Send>) {
1962        task(None);
1963    }
1964}
1965
1966/// Implementer of headless apps simulation of headed events for tests.
1967struct HeadlessSimulator {
1968    is_enabled: Option<bool>,
1969    is_open: bool,
1970}
1971impl HeadlessSimulator {
1972    fn new() -> Self {
1973        HeadlessSimulator {
1974            is_enabled: None,
1975            is_open: false,
1976        }
1977    }
1978
1979    fn enabled(&mut self) -> bool {
1980        *self.is_enabled.get_or_insert_with(|| zng_app::APP.window_mode().is_headless())
1981    }
1982
1983    pub fn pre_event(&mut self, update: &EventUpdate) {
1984        if self.enabled() && self.is_open && VIEW_PROCESS_INITED_EVENT.on(update).map(|a| a.is_respawn).unwrap_or(false) {
1985            self.is_open = false;
1986        }
1987    }
1988
1989    pub fn layout(&mut self) {
1990        if self.enabled() && !self.is_open {
1991            self.is_open = true;
1992            self.focus();
1993        }
1994    }
1995
1996    pub fn focus(&mut self) {
1997        let args = RawWindowFocusArgs::now(WINDOWS.focused_window_id(), Some(WINDOW.id()));
1998        RAW_WINDOW_FOCUS_EVENT.notify(args);
1999    }
2000
2001    pub fn bring_to_top(&mut self) {
2002        // we don't have "bring-to-top" event.
2003    }
2004}
2005
2006#[derive(Clone, Copy)]
2007enum InitState {
2008    /// We let one update cycle happen before init
2009    /// to let the constructor closure setup vars
2010    /// that are read on init.
2011    SkipOne,
2012    Init,
2013    Inited,
2014}
2015
2016/// Implementer of window UI node tree initialization and management.
2017struct ContentCtrl {
2018    vars: WindowVars,
2019    commands: WindowCommands,
2020
2021    root_ctx: WidgetCtx,
2022    root: UiNode,
2023    layout_pass: LayoutPassId,
2024
2025    init_state: InitState,
2026    frame_id: FrameId,
2027    clear_color: Rgba,
2028}
2029impl ContentCtrl {
2030    pub fn new(vars: WindowVars, commands: WindowCommands, window: WindowRoot) -> Self {
2031        Self {
2032            vars,
2033            commands,
2034
2035            root_ctx: WidgetCtx::new(window.id),
2036            root: window.child,
2037
2038            layout_pass: LayoutPassId::new(),
2039
2040            init_state: InitState::SkipOne,
2041            frame_id: FrameId::INVALID,
2042            clear_color: colors::BLACK,
2043        }
2044    }
2045
2046    pub fn update(&mut self, update_widgets: &WidgetUpdates) {
2047        match self.init_state {
2048            InitState::Inited => {
2049                self.commands.update(&self.vars);
2050
2051                update_widgets.with_window(|| {
2052                    if self.root_ctx.take_reinit() {
2053                        // like WidgetBase, pending reinit cancels update
2054                        WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
2055                            self.root.deinit();
2056                            self.root.init();
2057                        });
2058                        let _ = self.root_ctx.take_reinit(); // ignore after init
2059                    } else {
2060                        // no pending reinit, can update
2061                        WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
2062                            update_widgets.with_widget(|| {
2063                                self.root.update(update_widgets);
2064                            });
2065                        });
2066
2067                        // update requested reinit
2068                        if self.root_ctx.take_reinit() {
2069                            WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
2070                                self.root.deinit();
2071                                self.root.init();
2072                            });
2073                        }
2074                    }
2075                });
2076            }
2077
2078            InitState::SkipOne => {
2079                UPDATES.update(None);
2080                self.init_state = InitState::Init;
2081            }
2082            InitState::Init => {
2083                self.commands.init(&self.vars);
2084                WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
2085                    self.root.init();
2086                    // requests info, layout and render just in case `root` is a blank.
2087                    WIDGET.update_info().layout().render();
2088
2089                    super::WINDOW_OPEN_EVENT.notify(super::WindowOpenArgs::now(WINDOW.id()));
2090                });
2091                self.init_state = InitState::Inited;
2092                self.root_ctx.take_reinit(); // ignore reinit request (same as WidgetBase).
2093            }
2094        }
2095    }
2096
2097    #[must_use]
2098    pub fn info(&mut self, info_widgets: Arc<InfoUpdates>) -> Option<WidgetInfoTree> {
2099        let win_id = WINDOW.id();
2100        if info_widgets.delivery_list().enter_window(win_id) && matches!(self.init_state, InitState::Inited) {
2101            let mut info = WidgetInfoBuilder::new(
2102                info_widgets,
2103                win_id,
2104                self.vars.0.access_enabled.get(),
2105                self.root_ctx.id(),
2106                self.root_ctx.bounds(),
2107                self.root_ctx.border(),
2108                self.vars.0.scale_factor.get(),
2109            );
2110
2111            WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
2112                self.root.info(&mut info);
2113            });
2114
2115            let info = info.finalize(Some(WINDOW.info()), true);
2116
2117            WINDOWS.set_widget_tree(info.clone());
2118
2119            if self.root_ctx.is_pending_reinit() {
2120                WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || WIDGET.update());
2121            }
2122
2123            Some(info)
2124        } else {
2125            None
2126        }
2127    }
2128
2129    pub fn pre_event(&mut self, update: &EventUpdate) {
2130        #[cfg(feature = "image")]
2131        if let Some(args) = RAW_FRAME_RENDERED_EVENT.on(update) {
2132            if args.window_id == WINDOW.id() {
2133                let image = args.frame_image.as_ref().cloned().map(Img::new);
2134
2135                let args = FrameImageReadyArgs::new(args.timestamp, args.propagation().clone(), args.window_id, args.frame_id, image);
2136                FRAME_IMAGE_READY_EVENT.notify(args);
2137            }
2138            return;
2139        }
2140
2141        self.commands.event(&self.vars, update);
2142    }
2143
2144    pub fn ui_event(&mut self, update: &EventUpdate) {
2145        update.with_window(|| {
2146            if !matches!(self.init_state, InitState::Inited) {
2147                tracing::error!("cannot deliver `{:?}`, window `{}` is not inited", update.event(), WINDOW.id());
2148                return;
2149            }
2150
2151            if self.root_ctx.take_reinit() {
2152                WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
2153                    self.root.deinit();
2154                    self.root.init();
2155                });
2156                let _ = self.root_ctx.take_reinit(); // ignore after init
2157            }
2158
2159            WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
2160                update.with_widget(|| {
2161                    self.root.event(update);
2162                })
2163            });
2164
2165            if self.root_ctx.take_reinit() {
2166                WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
2167                    self.root.deinit();
2168                    self.root.init();
2169                });
2170            }
2171        });
2172    }
2173
2174    pub fn close(&mut self) {
2175        WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
2176            self.root.deinit();
2177        });
2178
2179        self.vars.0.is_open.set(false);
2180        self.root_ctx.deinit(false);
2181    }
2182
2183    /// Run an `action` in the context of a monitor screen that is parent of this content.
2184    pub fn outer_layout<R>(
2185        &mut self,
2186        scale_factor: Factor,
2187        screen_density: PxDensity,
2188        screen_size: PxSize,
2189        action: impl FnOnce() -> R,
2190    ) -> R {
2191        let metrics = LayoutMetrics::new(scale_factor, screen_size, Length::pt_to_px(11.0, scale_factor))
2192            .with_screen_density(screen_density)
2193            .with_direction(DIRECTION_VAR.get());
2194        LAYOUT.with_context(metrics, action)
2195    }
2196
2197    /// Layout content if there was a pending request, returns `Some(final_size)`.
2198    #[expect(clippy::too_many_arguments)]
2199    pub fn layout(
2200        &mut self,
2201        layout_widgets: Arc<LayoutUpdates>,
2202        scale_factor: Factor,
2203        screen_density: PxDensity,
2204        min_size: PxSize,
2205        max_size: PxSize,
2206        size: PxSize,
2207        root_font_size: Px,
2208        skip_auto_size: bool,
2209    ) -> PxSize {
2210        if !matches!(self.init_state, InitState::Inited) {
2211            return PxSize::zero();
2212        }
2213
2214        let _s = tracing::trace_span!("window.on_layout", window = %WINDOW.id().sequential()).entered();
2215
2216        let auto_size = self.vars.auto_size().get();
2217
2218        self.layout_pass = self.layout_pass.next();
2219
2220        let final_size = WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
2221            let metrics = LayoutMetrics::new(scale_factor, size, root_font_size)
2222                .with_screen_density(screen_density)
2223                .with_direction(DIRECTION_VAR.get());
2224            LAYOUT.with_root_context(self.layout_pass, metrics, || {
2225                let mut root_cons = LAYOUT.constraints();
2226                if !skip_auto_size {
2227                    if auto_size.contains(AutoSize::CONTENT_WIDTH) {
2228                        root_cons.x = PxConstraints::new_range(min_size.width, max_size.width);
2229                    }
2230                    if auto_size.contains(AutoSize::CONTENT_HEIGHT) {
2231                        root_cons.y = PxConstraints::new_range(min_size.height, max_size.height);
2232                    }
2233                }
2234                let desired_size = LAYOUT.with_constraints(root_cons, || {
2235                    WidgetLayout::with_root_widget(layout_widgets, |wl| self.root.layout(wl))
2236                });
2237
2238                let mut final_size = size;
2239                if !skip_auto_size {
2240                    if auto_size.contains(AutoSize::CONTENT_WIDTH) {
2241                        final_size.width = desired_size.width.max(min_size.width).min(max_size.width);
2242                    }
2243                    if auto_size.contains(AutoSize::CONTENT_HEIGHT) {
2244                        final_size.height = desired_size.height.max(min_size.height).min(max_size.height);
2245                    }
2246                }
2247
2248                final_size
2249            })
2250        });
2251
2252        if self.root_ctx.is_pending_reinit() {
2253            WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || WIDGET.update());
2254        }
2255
2256        final_size
2257    }
2258
2259    pub fn render(
2260        &mut self,
2261        renderer: Option<ViewRenderer>,
2262        scale_factor: Factor,
2263        wait_id: Option<FrameWaitId>,
2264        render_widgets: Arc<RenderUpdates>,
2265        render_update_widgets: Arc<RenderUpdates>,
2266    ) {
2267        if !matches!(self.init_state, InitState::Inited) {
2268            return;
2269        }
2270
2271        let w_id = WINDOW.id();
2272        if render_widgets.delivery_list().enter_window(w_id) {
2273            // RENDER FULL FRAME
2274            let _s = tracing::trace_span!("window.on_render", window = %WINDOW.id().sequential()).entered();
2275
2276            self.frame_id = self.frame_id.next();
2277
2278            let mut frame = FrameBuilder::new(
2279                render_widgets,
2280                render_update_widgets,
2281                self.frame_id,
2282                self.root_ctx.id(),
2283                &self.root_ctx.bounds(),
2284                &WINDOW.info(),
2285                renderer.clone(),
2286                scale_factor,
2287                FontAntiAliasing::Default,
2288            );
2289
2290            let frame = WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
2291                self.root.render(&mut frame);
2292                frame.finalize(&WINDOW.info())
2293            });
2294
2295            if self.root_ctx.is_pending_reinit() {
2296                WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || WIDGET.update());
2297            }
2298
2299            self.clear_color = frame.clear_color;
2300
2301            #[cfg(feature = "image")]
2302            let capture = self.take_frame_capture();
2303            #[cfg(not(feature = "image"))]
2304            let capture = FrameCapture::None;
2305
2306            if let Some(renderer) = renderer {
2307                let _: Ignore = renderer.render(FrameRequest::new(
2308                    self.frame_id,
2309                    self.clear_color,
2310                    frame.display_list,
2311                    capture,
2312                    wait_id,
2313                ));
2314            } else {
2315                // simulate frame in headless
2316                #[cfg(feature = "image")]
2317                FRAME_IMAGE_READY_EVENT.notify(FrameImageReadyArgs::now(WINDOW.id(), self.frame_id, None));
2318            }
2319        } else if render_update_widgets.delivery_list().enter_window(w_id) {
2320            // RENDER UPDATE
2321            let _s = tracing::trace_span!("window.on_render_update", window = %WINDOW.id().sequential()).entered();
2322
2323            self.frame_id = self.frame_id.next_update();
2324
2325            let mut update = FrameUpdate::new(
2326                render_update_widgets,
2327                self.frame_id,
2328                self.root_ctx.id(),
2329                self.root_ctx.bounds(),
2330                self.clear_color,
2331            );
2332
2333            let update = WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
2334                self.root.render_update(&mut update);
2335                update.finalize(&WINDOW.info())
2336            });
2337
2338            if let Some(c) = update.clear_color {
2339                self.clear_color = c;
2340            }
2341
2342            if self.root_ctx.is_pending_reinit() {
2343                WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || WIDGET.update());
2344            }
2345
2346            #[cfg(feature = "image")]
2347            let capture = self.take_frame_capture();
2348            #[cfg(not(feature = "image"))]
2349            let capture = FrameCapture::None;
2350
2351            if let Some(renderer) = renderer {
2352                let _: Ignore = renderer.render_update(FrameUpdateRequest::new(
2353                    self.frame_id,
2354                    update.transforms,
2355                    update.floats,
2356                    update.colors,
2357                    update.clear_color,
2358                    capture,
2359                    wait_id,
2360                    update.extensions,
2361                ));
2362            } else {
2363                // simulate frame in headless
2364                #[cfg(feature = "image")]
2365                FRAME_IMAGE_READY_EVENT.notify(FrameImageReadyArgs::now(WINDOW.id(), self.frame_id, None));
2366            }
2367        }
2368    }
2369    #[cfg(feature = "image")]
2370    fn take_frame_capture(&self) -> FrameCapture {
2371        match self.vars.frame_capture_mode().get() {
2372            FrameCaptureMode::Sporadic => FrameCapture::None,
2373            FrameCaptureMode::Next => {
2374                self.vars.frame_capture_mode().set(FrameCaptureMode::Sporadic);
2375                FrameCapture::Full
2376            }
2377            FrameCaptureMode::All => FrameCapture::Full,
2378            FrameCaptureMode::NextMask(m) => {
2379                self.vars.frame_capture_mode().set(FrameCaptureMode::Sporadic);
2380                FrameCapture::Mask(m)
2381            }
2382            FrameCaptureMode::AllMask(m) => FrameCapture::Mask(m),
2383        }
2384    }
2385}
2386
2387/// Management of window content and synchronization of WindowVars and View-Process.
2388pub(super) struct WindowCtrl(WindowCtrlMode);
2389#[allow(clippy::large_enum_variant)] // headed control is the largest, but also the most common
2390enum WindowCtrlMode {
2391    Headed(HeadedCtrl),
2392    Headless(HeadlessCtrl),
2393    HeadlessWithRenderer(HeadlessWithRendererCtrl),
2394    Nested(NestedCtrl),
2395}
2396impl WindowCtrl {
2397    pub fn new(vars: &WindowVars, commands: WindowCommands, mode: WindowMode, content: WindowRoot) -> Self {
2398        WindowCtrl(match mode {
2399            WindowMode::Headed => WindowCtrlMode::Headed(HeadedCtrl::new(vars, commands, content)),
2400            WindowMode::Headless => WindowCtrlMode::Headless(HeadlessCtrl::new(vars, commands, content)),
2401            WindowMode::HeadlessWithRenderer => {
2402                WindowCtrlMode::HeadlessWithRenderer(HeadlessWithRendererCtrl::new(vars, commands, content))
2403            }
2404        })
2405    }
2406
2407    pub fn new_nested(c: Arc<Mutex<NestedContentCtrl>>) -> Self {
2408        WindowCtrl(WindowCtrlMode::Nested(NestedCtrl::new(c)))
2409    }
2410
2411    pub fn update(&mut self, update_widgets: &WidgetUpdates) {
2412        match &mut self.0 {
2413            WindowCtrlMode::Headed(c) => c.update(update_widgets),
2414            WindowCtrlMode::Headless(c) => c.update(update_widgets),
2415            WindowCtrlMode::HeadlessWithRenderer(c) => c.update(update_widgets),
2416            WindowCtrlMode::Nested(c) => c.update(update_widgets),
2417        }
2418    }
2419
2420    #[must_use]
2421    pub fn info(&mut self, info_widgets: Arc<InfoUpdates>) -> Option<WidgetInfoTree> {
2422        match &mut self.0 {
2423            WindowCtrlMode::Headed(c) => c.info(info_widgets),
2424            WindowCtrlMode::Headless(c) => c.info(info_widgets),
2425            WindowCtrlMode::HeadlessWithRenderer(c) => c.info(info_widgets),
2426            WindowCtrlMode::Nested(c) => c.info(info_widgets),
2427        }
2428    }
2429
2430    pub fn pre_event(&mut self, update: &EventUpdate) {
2431        match &mut self.0 {
2432            WindowCtrlMode::Headed(c) => c.pre_event(update),
2433            WindowCtrlMode::Headless(c) => c.pre_event(update),
2434            WindowCtrlMode::HeadlessWithRenderer(c) => c.pre_event(update),
2435            WindowCtrlMode::Nested(c) => c.pre_event(update),
2436        }
2437    }
2438
2439    pub fn ui_event(&mut self, update: &EventUpdate) {
2440        match &mut self.0 {
2441            WindowCtrlMode::Headed(c) => c.ui_event(update),
2442            WindowCtrlMode::Headless(c) => c.ui_event(update),
2443            WindowCtrlMode::HeadlessWithRenderer(c) => c.ui_event(update),
2444            WindowCtrlMode::Nested(c) => c.ui_event(update),
2445        }
2446    }
2447
2448    pub fn layout(&mut self, layout_widgets: Arc<LayoutUpdates>) {
2449        match &mut self.0 {
2450            WindowCtrlMode::Headed(c) => c.layout(layout_widgets),
2451            WindowCtrlMode::Headless(c) => c.layout(layout_widgets),
2452            WindowCtrlMode::HeadlessWithRenderer(c) => c.layout(layout_widgets),
2453            WindowCtrlMode::Nested(c) => c.layout(layout_widgets),
2454        }
2455    }
2456
2457    pub fn render(&mut self, render_widgets: Arc<RenderUpdates>, render_update_widgets: Arc<RenderUpdates>) {
2458        match &mut self.0 {
2459            WindowCtrlMode::Headed(c) => c.render(render_widgets, render_update_widgets),
2460            WindowCtrlMode::Headless(c) => c.render(render_widgets, render_update_widgets),
2461            WindowCtrlMode::HeadlessWithRenderer(c) => c.render(render_widgets, render_update_widgets),
2462            WindowCtrlMode::Nested(c) => c.render(render_widgets, render_update_widgets),
2463        }
2464    }
2465
2466    pub fn focus(&mut self) {
2467        match &mut self.0 {
2468            WindowCtrlMode::Headed(c) => c.focus(),
2469            WindowCtrlMode::Headless(c) => c.focus(),
2470            WindowCtrlMode::HeadlessWithRenderer(c) => c.focus(),
2471            WindowCtrlMode::Nested(c) => c.focus(),
2472        }
2473    }
2474
2475    pub fn start_drag_drop(&mut self, data: Vec<DragDropData>, allowed_effects: DragDropEffect) -> Result<DragDropId, DragDropError> {
2476        match &mut self.0 {
2477            WindowCtrlMode::Headed(c) => c.start_drag_drop(data, allowed_effects),
2478            WindowCtrlMode::Headless(c) => c.start_drag_drop(data, allowed_effects),
2479            WindowCtrlMode::HeadlessWithRenderer(c) => c.start_drag_drop(data, allowed_effects),
2480            WindowCtrlMode::Nested(c) => c.start_drag_drop(data, allowed_effects),
2481        }
2482    }
2483
2484    pub fn drag_dropped(&mut self, drop_id: DragDropId, applied: DragDropEffect) {
2485        match &mut self.0 {
2486            WindowCtrlMode::Headed(c) => c.drag_dropped(drop_id, applied),
2487            WindowCtrlMode::Headless(c) => c.drag_dropped(drop_id, applied),
2488            WindowCtrlMode::HeadlessWithRenderer(c) => c.drag_dropped(drop_id, applied),
2489            WindowCtrlMode::Nested(c) => c.drag_dropped(drop_id, applied),
2490        }
2491    }
2492
2493    pub fn bring_to_top(&mut self) {
2494        match &mut self.0 {
2495            WindowCtrlMode::Headed(c) => c.bring_to_top(),
2496            WindowCtrlMode::Headless(c) => c.bring_to_top(),
2497            WindowCtrlMode::HeadlessWithRenderer(c) => c.bring_to_top(),
2498            WindowCtrlMode::Nested(c) => c.bring_to_top(),
2499        }
2500    }
2501
2502    pub fn close(&mut self) {
2503        match &mut self.0 {
2504            WindowCtrlMode::Headed(c) => c.close(),
2505            WindowCtrlMode::Headless(c) => c.close(),
2506            WindowCtrlMode::HeadlessWithRenderer(c) => c.close(),
2507            WindowCtrlMode::Nested(c) => c.close(),
2508        }
2509    }
2510
2511    pub(crate) fn view_task(&mut self, task: Box<dyn FnOnce(Option<&ViewWindow>) + Send>) {
2512        match &mut self.0 {
2513            WindowCtrlMode::Headed(c) => c.view_task(task),
2514            WindowCtrlMode::Headless(c) => c.view_task(task),
2515            WindowCtrlMode::HeadlessWithRenderer(c) => c.view_task(task),
2516            WindowCtrlMode::Nested(c) => c.view_task(task),
2517        }
2518    }
2519}
2520
2521fn default_min_size(scale_factor: Factor) -> PxSize {
2522    DipSize::new(Dip::new(192), Dip::new(48)).to_px(scale_factor)
2523}
2524
2525fn default_size(scale_factor: Factor) -> PxSize {
2526    DipSize::new(Dip::new(800), Dip::new(600)).to_px(scale_factor)
2527}
2528
2529/// Respawned error is ok here, because we recreate the window/surface on respawn.
2530type Ignore = Result<(), ChannelError>;
2531
2532pub(crate) struct NestedContentCtrl {
2533    content: ContentCtrl,
2534    pending_layout: Option<Arc<LayoutUpdates>>,
2535    pending_render: Option<[Arc<RenderUpdates>; 2]>,
2536    ctx: WindowCtx,
2537    host: Option<(WindowId, WidgetId)>,
2538    #[cfg(feature = "image")]
2539    pending_frame_capture: FrameCapture,
2540}
2541
2542/// Implementer of an endpoint to an `WindowRoot` being used as an widget.
2543struct NestedCtrl {
2544    c: Arc<Mutex<NestedContentCtrl>>,
2545    actual_parent: Option<WindowId>,
2546    // actual_color_scheme and scale_factor binding.
2547    var_bindings: VarHandles,
2548}
2549impl NestedCtrl {
2550    pub fn new(c: Arc<Mutex<NestedContentCtrl>>) -> Self {
2551        Self {
2552            c,
2553            actual_parent: None,
2554            var_bindings: VarHandles::dummy(),
2555        }
2556    }
2557
2558    fn update(&mut self, update_widgets: &WidgetUpdates) {
2559        let mut c = self.c.lock();
2560        c.content.update(update_widgets);
2561
2562        let vars = &c.content.vars;
2563
2564        if update_parent(&mut self.actual_parent, vars) || self.var_bindings.is_dummy() {
2565            let m_scale_factor = if let Some(p) = self.actual_parent.and_then(|p| WINDOWS.vars(p).ok()) {
2566                p.actual_monitor()
2567                    .get()
2568                    .and_then(|m| MONITORS.monitor(m))
2569                    .map(|m| m.scale_factor().get())
2570            } else {
2571                None
2572            };
2573            self.var_bindings = update_headless_vars(m_scale_factor, vars);
2574        }
2575    }
2576
2577    fn info(&mut self, info_widgets: Arc<InfoUpdates>) -> Option<WidgetInfoTree> {
2578        self.c.lock().content.info(info_widgets)
2579    }
2580
2581    fn pre_event(&mut self, update: &EventUpdate) {
2582        #[cfg(feature = "image")]
2583        if let Some(args) = RAW_FRAME_RENDERED_EVENT.on(update) {
2584            let mut c = self.c.lock();
2585            let c = &mut *c;
2586            if let Some((win, _)) = c.host
2587                && args.window_id == win
2588            {
2589                let image = match mem::take(&mut c.pending_frame_capture) {
2590                    FrameCapture::None => None,
2591                    FrameCapture::Full => Some(WINDOWS.frame_image(win, None).get()),
2592                    FrameCapture::Mask(m) => Some(WINDOWS.frame_image(win, Some(m)).get()),
2593                    _ => None,
2594                };
2595                let args = FrameImageReadyArgs::new(args.timestamp, args.propagation().clone(), win, args.frame_id, image);
2596                FRAME_IMAGE_READY_EVENT.notify(args);
2597            }
2598            return;
2599        }
2600        self.c.lock().content.pre_event(update)
2601    }
2602
2603    fn ui_event(&mut self, update: &EventUpdate) {
2604        self.c.lock().content.ui_event(update)
2605    }
2606
2607    fn layout(&self, layout_widgets: Arc<LayoutUpdates>) {
2608        if layout_widgets.delivery_list().enter_window(WINDOW.id()) {
2609            let mut c = self.c.lock();
2610            let c = &mut *c;
2611            if let Some((_, wgt_id)) = &c.host {
2612                c.pending_layout = Some(layout_widgets);
2613                UPDATES.layout(*wgt_id);
2614            }
2615        }
2616    }
2617
2618    fn render(&self, render_widgets: Arc<RenderUpdates>, render_update_widgets: Arc<RenderUpdates>) {
2619        let id = WINDOW.id();
2620        if render_widgets.delivery_list().enter_window(id) || render_update_widgets.delivery_list().enter_window(id) {
2621            let mut c = self.c.lock();
2622            let c = &mut *c;
2623            if let Some((_, wgt_id)) = &c.host {
2624                c.pending_render = Some([render_widgets, render_update_widgets]);
2625                UPDATES.render(*wgt_id);
2626            }
2627        }
2628    }
2629
2630    fn focus(&self) {
2631        self.bring_to_top();
2632        // many services track window focus with this event.
2633        let args = RawWindowFocusArgs::now(WINDOWS.focused_window_id(), Some(WINDOW.id()));
2634        RAW_WINDOW_FOCUS_EVENT.notify(args);
2635    }
2636
2637    pub fn start_drag_drop(&mut self, data: Vec<DragDropData>, allowed_effects: DragDropEffect) -> Result<DragDropId, DragDropError> {
2638        if let Some((win_id, _)) = &self.c.lock().host {
2639            return WINDOWS_DRAG_DROP.start_drag_drop(*win_id, data, allowed_effects);
2640        }
2641        Err(DragDropError::CannotStart("nested window host unavailable".into()))
2642    }
2643
2644    pub fn drag_dropped(&mut self, drop_id: DragDropId, applied: DragDropEffect) {
2645        let _ = (drop_id, applied);
2646    }
2647
2648    fn bring_to_top(&self) {
2649        if let Some((win_id, _)) = &self.c.lock().host {
2650            let _ = WINDOWS.bring_to_top(*win_id);
2651        }
2652    }
2653
2654    fn close(&mut self) {
2655        let mut c = self.c.lock();
2656        c.content.close();
2657        c.pending_layout = None;
2658        c.pending_render = None;
2659        if let Some((_, wgt_id)) = &c.host {
2660            // NestedWindowNode collapses on close
2661            UPDATES.layout(*wgt_id);
2662        }
2663    }
2664
2665    fn view_task(&self, task: Box<dyn FnOnce(Option<&ViewWindow>)>) {
2666        task(None)
2667    }
2668}
2669
2670/// UI node implementation that presents a [`WindowRoot`] as embedded content.
2671pub struct NestedWindowNode {
2672    c: Arc<Mutex<NestedContentCtrl>>,
2673}
2674impl NestedWindowNode {
2675    fn layout_impl(&mut self, is_measure: bool, measure_layout: impl FnOnce(&mut UiNode) -> PxSize) -> PxSize {
2676        let mut c = self.c.lock();
2677        let c = &mut *c;
2678
2679        if !c.content.vars.0.is_open.get() {
2680            return PxSize::zero();
2681        }
2682
2683        let auto_size = c.content.vars.auto_size().get();
2684        let constraints = LAYOUT.constraints();
2685
2686        let metrics = LayoutMetrics::new(LAYOUT.scale_factor(), PxSize::splat(Px::MAX), LAYOUT.root_font_size())
2687            .with_constraints(constraints)
2688            .with_screen_density(LAYOUT.screen_density())
2689            .with_direction(DIRECTION_VAR.get());
2690
2691        // only the same app_local!, APP.id
2692        LocalContext::capture_filtered(zng_app_context::CaptureFilter::app_only()).with_context(|| {
2693            WINDOW.with_context(&mut c.ctx, || {
2694                WIDGET.with_context(&mut c.content.root_ctx, WidgetUpdateMode::Bubble, || {
2695                    LAYOUT.with_root_context(c.content.layout_pass, metrics, || {
2696                        let mut root_cons = LAYOUT.constraints();
2697
2698                        // equivalent of with_fill_metrics used by `max_size` property
2699                        let dft = root_cons.fill_size();
2700                        let (min_size, max_size, pref_size) =
2701                            LAYOUT.with_constraints(root_cons.with_fill_vector(root_cons.is_bounded()), || {
2702                                let max = c.content.vars.max_size().layout_dft(dft);
2703                                (c.content.vars.min_size().layout(), max, c.content.vars.size().layout_dft(max))
2704                            });
2705
2706                        let min_size = min_size.max(root_cons.min_size());
2707                        let max_size = max_size.min(root_cons.max_size_or(PxSize::splat(Px::MAX)));
2708                        let pref_size = pref_size.clamp(min_size, max_size);
2709
2710                        if auto_size.contains(AutoSize::CONTENT_WIDTH) {
2711                            root_cons.x = PxConstraints::new_range(min_size.width, max_size.width);
2712                        } else {
2713                            root_cons.x = PxConstraints::new_exact(pref_size.width);
2714                        }
2715                        if auto_size.contains(AutoSize::CONTENT_HEIGHT) {
2716                            root_cons.y = PxConstraints::new_range(min_size.height, max_size.height);
2717                        } else {
2718                            root_cons.y = PxConstraints::new_exact(pref_size.height);
2719                        }
2720
2721                        if auto_size.is_empty() && is_measure {
2722                            pref_size
2723                        } else {
2724                            LAYOUT.with_constraints(root_cons, || measure_layout(&mut c.content.root))
2725                        }
2726                    })
2727                })
2728            })
2729        })
2730    }
2731}
2732impl UiNodeImpl for NestedWindowNode {
2733    fn children_len(&self) -> usize {
2734        1
2735    }
2736
2737    fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
2738        if index == 0 {
2739            visitor(&mut self.c.lock().content.root)
2740        }
2741    }
2742
2743    fn init(&mut self) {
2744        let mut c = self.c.lock();
2745        let parent_id = WINDOW.id();
2746        c.content.vars.parent().set(parent_id);
2747        let nest_parent = WIDGET.id();
2748        c.content.vars.0.nest_parent.set(nest_parent);
2749        c.host = Some((parent_id, nest_parent));
2750        // init handled by // NestedCtrl::update
2751    }
2752
2753    fn deinit(&mut self) {
2754        // this can be a parent reinit or node move, if not inited after 100ms close the window.
2755        let mut c = self.c.lock();
2756        c.host = None;
2757        let c = &self.c;
2758        TIMERS
2759            .on_deadline(
2760                100.ms(),
2761                hn_once!(c, |_| {
2762                    let c = c.lock();
2763                    if c.host.is_none() {
2764                        let _ = WINDOWS.close(c.ctx.id());
2765                    }
2766                }),
2767            )
2768            .perm();
2769
2770        // deinit handled by NestedCtrl::close
2771    }
2772
2773    fn info(&mut self, info: &mut WidgetInfoBuilder) {
2774        info.set_meta(*NESTED_WINDOW_INFO_ID, self.c.lock().ctx.id());
2775    }
2776
2777    fn event(&mut self, _: &EventUpdate) {
2778        // event handled by NestedCtrl::ui_event
2779    }
2780
2781    fn update(&mut self, _: &WidgetUpdates) {
2782        // update handled by NestedCtrl::update
2783    }
2784
2785    fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
2786        self.layout_impl(true, |r| wm.with_widget(|wm| r.measure(wm)))
2787    }
2788
2789    fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
2790        let pending = self.c.lock().pending_layout.take();
2791        let size = self.layout_impl(false, |r| {
2792            if let Some(p) = pending {
2793                wl.with_layout_updates(p, |wl| wl.with_widget(|wl| r.layout(wl)))
2794            } else {
2795                wl.with_widget(|wl| r.layout(wl))
2796            }
2797        });
2798        let c = self.c.lock();
2799        let factor = LAYOUT.scale_factor();
2800        c.content.vars.0.scale_factor.set(factor);
2801        c.content.vars.0.actual_size.set(size.to_dip(factor));
2802        size
2803    }
2804
2805    fn render(&mut self, frame: &mut FrameBuilder) {
2806        let mut c = self.c.lock();
2807        let c = &mut *c;
2808
2809        if !c.content.vars.0.is_open.get() {
2810            return;
2811        }
2812
2813        let [render_widgets, render_update_widgets] = c.pending_render.take().unwrap_or_default();
2814        // only the same app_local!, APP.id
2815        LocalContext::capture_filtered(zng_app_context::CaptureFilter::app_only()).with_context(|| {
2816            WINDOW.with_context(&mut c.ctx, || {
2817                let root_id = c.content.root_ctx.id();
2818                let root_bounds = c.content.root_ctx.bounds();
2819                WIDGET.with_context(&mut c.content.root_ctx, WidgetUpdateMode::Bubble, || {
2820                    frame.with_nested_window(
2821                        render_widgets,
2822                        render_update_widgets,
2823                        root_id,
2824                        &root_bounds,
2825                        &WINDOW.info(),
2826                        FontAntiAliasing::Default,
2827                        |frame| {
2828                            c.content.root.render(frame);
2829                        },
2830                    );
2831                });
2832                #[cfg(feature = "image")]
2833                {
2834                    c.pending_frame_capture = c.content.take_frame_capture();
2835                }
2836            })
2837        })
2838    }
2839
2840    fn render_update(&mut self, update: &mut FrameUpdate) {
2841        let mut c = self.c.lock();
2842        let c = &mut *c;
2843
2844        if !c.content.vars.0.is_open.get() {
2845            return;
2846        }
2847
2848        let [_, render_update_widgets] = c.pending_render.take().unwrap_or_default();
2849        // only the same app_local!, APP.id
2850        LocalContext::capture_filtered(zng_app_context::CaptureFilter::app_only()).with_context(|| {
2851            WINDOW.with_context(&mut c.ctx, || {
2852                WIDGET.with_context(&mut c.content.root_ctx, WidgetUpdateMode::Bubble, || {
2853                    update.with_nested_window(render_update_widgets, WIDGET.id(), WIDGET.bounds(), |update| {
2854                        c.content.root.render_update(update);
2855                    })
2856                })
2857            })
2858        })
2859    }
2860
2861    fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
2862        if self.c.lock().content.root.as_widget().is_some() {
2863            Some(self)
2864        } else {
2865            None
2866        }
2867    }
2868}
2869impl WidgetUiNodeImpl for NestedWindowNode {
2870    fn with_context(&mut self, update_mode: WidgetUpdateMode, visitor: &mut dyn FnMut()) {
2871        let mut lock = self.c.lock();
2872        if let Some(mut w) = lock.content.root.as_widget() {
2873            w.with_context(update_mode, visitor)
2874        }
2875    }
2876}
2877
2878static_id! {
2879    static ref NESTED_WINDOW_INFO_ID: StateId<WindowId>;
2880}
2881
2882/// Extension methods for widget info about a node that hosts a nested window.
2883pub trait NestedWindowWidgetInfoExt {
2884    /// Gets the hosted window ID if the widget hosts a nested window.
2885    fn nested_window(&self) -> Option<WindowId>;
2886
2887    /// Gets the hosted window info tree if the widget hosts a nested window that is open.
2888    fn nested_window_tree(&self) -> Option<WidgetInfoTree> {
2889        WINDOWS.widget_tree(self.nested_window()?).ok()
2890    }
2891}
2892
2893impl NestedWindowWidgetInfoExt for WidgetInfo {
2894    fn nested_window(&self) -> Option<WindowId> {
2895        self.meta().get_clone(*NESTED_WINDOW_INFO_ID)
2896    }
2897}
2898
2899#[allow(clippy::large_enum_variant)] // Normal is the largest, but also most common
2900enum OpenNestedHandlerArgsValue {
2901    Normal {
2902        ctx: WindowCtx,
2903        vars: WindowVars,
2904        commands: WindowCommands,
2905        window: WindowRoot,
2906    },
2907    Nested {
2908        ctx: WindowCtx,
2909        node: Arc<Mutex<NestedContentCtrl>>,
2910    },
2911    TempNone,
2912}
2913
2914/// Arguments for the [`WINDOWS.register_open_nested_handler`] handler.
2915///
2916/// [`WINDOWS.register_open_nested_handler`]: WINDOWS::register_open_nested_handler
2917pub struct OpenNestedHandlerArgs {
2918    c: OpenNestedHandlerArgsValue,
2919}
2920impl OpenNestedHandlerArgs {
2921    pub(crate) fn new(ctx: WindowCtx, vars: WindowVars, commands: WindowCommands, window: WindowRoot) -> Self {
2922        Self {
2923            c: OpenNestedHandlerArgsValue::Normal {
2924                ctx,
2925                vars,
2926                commands,
2927                window,
2928            },
2929        }
2930    }
2931
2932    /// New window context.
2933    pub fn ctx(&self) -> &WindowCtx {
2934        match &self.c {
2935            OpenNestedHandlerArgsValue::Normal { ctx, .. } | OpenNestedHandlerArgsValue::Nested { ctx, .. } => ctx,
2936            OpenNestedHandlerArgsValue::TempNone => unreachable!(),
2937        }
2938    }
2939
2940    /// Window vars.
2941    pub fn vars(&mut self) -> WindowVars {
2942        let ctx = match &mut self.c {
2943            OpenNestedHandlerArgsValue::Normal { ctx, .. } | OpenNestedHandlerArgsValue::Nested { ctx, .. } => ctx,
2944            OpenNestedHandlerArgsValue::TempNone => unreachable!(),
2945        };
2946        WINDOW.with_context(ctx, || WINDOW.vars())
2947    }
2948
2949    /// Instantiate a node that layouts and renders the window content.
2950    ///
2951    /// Calling this will stop the normal window chrome from opening, the caller is responsible for inserting the node into the
2952    /// main window layout.
2953    ///
2954    /// Note that the window will notify *open* like normal, but it will only be visible on this node.
2955    pub fn nest(&mut self) -> NestedWindowNode {
2956        match mem::replace(&mut self.c, OpenNestedHandlerArgsValue::TempNone) {
2957            OpenNestedHandlerArgsValue::Normal {
2958                mut ctx,
2959                vars,
2960                commands,
2961                window,
2962            } => {
2963                let node = NestedWindowNode {
2964                    c: Arc::new(Mutex::new(NestedContentCtrl {
2965                        content: ContentCtrl::new(vars, commands, window),
2966                        pending_layout: None,
2967                        pending_render: None,
2968                        #[cfg(feature = "image")]
2969                        pending_frame_capture: FrameCapture::None,
2970                        ctx: ctx.share(),
2971                        host: None,
2972                    })),
2973                };
2974                self.c = OpenNestedHandlerArgsValue::Nested { ctx, node: node.c.clone() };
2975                node
2976            }
2977            _ => panic!("already nesting"),
2978        }
2979    }
2980
2981    pub(crate) fn has_nested(&self) -> bool {
2982        matches!(&self.c, OpenNestedHandlerArgsValue::Nested { .. })
2983    }
2984
2985    pub(crate) fn take_normal(
2986        self,
2987    ) -> Result<(WindowCtx, WindowVars, WindowCommands, WindowRoot), (WindowCtx, Arc<Mutex<NestedContentCtrl>>)> {
2988        match self.c {
2989            OpenNestedHandlerArgsValue::Normal {
2990                ctx,
2991                vars,
2992                commands,
2993                window,
2994            } => Ok((ctx, vars, commands, window)),
2995            OpenNestedHandlerArgsValue::Nested { ctx, node } => Err((ctx, node)),
2996            OpenNestedHandlerArgsValue::TempNone => unreachable!(),
2997        }
2998    }
2999}