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_var::{Var, VarHandles};
40use zng_view_api::{
41    DragDropId, FocusResult, Ime,
42    config::{ColorScheme, FontAntiAliasing},
43    drag_drop::{DragDropData, DragDropEffect, DragDropError},
44    ipc::ViewChannelError,
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            debug_assert!(self.window.is_some() || self.waiting_view || self.respawned);
216
217            let mut new_state = prev_state.clone();
218
219            if self.vars.chrome().is_new() || WINDOWS.system_chrome().is_new() {
220                let mut chrome = self.vars.chrome().get();
221
222                if self.kiosk.is_some() && !chrome {
223                    tracing::error!("window in `kiosk` mode can not show chrome");
224                    chrome = false;
225                }
226
227                new_state.chrome_visible = chrome && !WINDOWS.system_chrome().get().needs_custom();
228            }
229
230            if let Some(mut req_state) = self.vars.state().get_new() {
231                if let Some(enforced_fullscreen) = &mut self.kiosk {
232                    if !req_state.is_fullscreen() {
233                        tracing::error!("window in `kiosk` mode can only be fullscreen");
234
235                        req_state = *enforced_fullscreen;
236                    } else {
237                        *enforced_fullscreen = req_state;
238                    }
239                }
240
241                new_state.set_state(req_state);
242                self.vars.0.restore_state.set(new_state.restore_state);
243            }
244
245            if (self.vars.min_size().is_new() || self.vars.max_size().is_new())
246                && let Some(m) = &self.monitor
247            {
248                let scale_factor = m.scale_factor().get();
249                let screen_density = m.density().get();
250                let screen_size = m.size().get();
251                let (min_size, max_size) = self.content.outer_layout(scale_factor, screen_density, screen_size, || {
252                    let min_size = self.vars.min_size().layout_dft(default_min_size(scale_factor));
253                    let max_size = self.vars.max_size().layout_dft(screen_size);
254
255                    (min_size.to_dip(scale_factor), max_size.to_dip(scale_factor))
256                });
257
258                let size = new_state.restore_rect.size;
259
260                new_state.restore_rect.size = size.min(max_size).max(min_size);
261                new_state.min_size = min_size;
262                new_state.max_size = max_size;
263            }
264
265            if let Some(auto) = self.vars.auto_size().get_new()
266                && auto != AutoSize::DISABLED
267            {
268                UPDATES.layout_window(WINDOW.id());
269            }
270
271            if self.vars.size().is_new() {
272                let auto_size = self.vars.auto_size().get();
273
274                if auto_size != AutoSize::CONTENT
275                    && let Some(m) = &self.monitor
276                {
277                    let scale_factor = m.scale_factor().get();
278                    let screen_density = m.density().get();
279                    let screen_size = m.size().get();
280                    let size = self.content.outer_layout(scale_factor, screen_density, screen_size, || {
281                        self.vars.size().layout_dft(default_size(scale_factor)).to_dip(scale_factor)
282                    });
283
284                    let size = size.min(new_state.max_size).max(new_state.min_size);
285
286                    if !auto_size.contains(AutoSize::CONTENT_WIDTH) {
287                        new_state.restore_rect.size.width = size.width;
288                    }
289                    if !auto_size.contains(AutoSize::CONTENT_HEIGHT) {
290                        new_state.restore_rect.size.height = size.height;
291                    }
292                }
293            }
294
295            if let Some(pos) = self.vars.position().get_new()
296                && let Some(m) = &self.monitor
297            {
298                let scale_factor = m.scale_factor().get();
299                let screen_density = m.density().get();
300                let screen_size = m.size().get();
301                let pos = self.content.outer_layout(scale_factor, screen_density, screen_size, || {
302                    pos.layout_dft(PxPoint::new(Px(50), Px(50)))
303                });
304                new_state.restore_rect.origin = pos.to_dip(scale_factor);
305            }
306
307            if let Some(mut visible) = self.vars.visible().get_new() {
308                if !visible && self.kiosk.is_some() {
309                    tracing::error!("window in `kiosk` mode can not be hidden");
310                    visible = true;
311                }
312
313                self.update_gen(move |view| {
314                    let _: Ignore = view.set_visible(visible);
315                });
316            }
317
318            if let Some(movable) = self.vars.movable().get_new() {
319                self.update_gen(move |view| {
320                    let _: Ignore = view.set_movable(movable);
321                });
322            }
323
324            if let Some(resizable) = self.vars.resizable().get_new() {
325                self.update_gen(move |view| {
326                    let _: Ignore = view.set_resizable(resizable);
327                });
328            }
329
330            if let Some(buttons) = self.vars.enabled_buttons().get_new() {
331                self.update_gen(move |view| {
332                    let _: Ignore = view.set_enabled_buttons(buttons);
333                })
334            }
335
336            if let Some(reason) = self.vars.system_shutdown_warn().get_new() {
337                self.update_gen(move |view| {
338                    let _: Ignore = view.set_system_shutdown_warn(reason);
339                })
340            }
341
342            if prev_state != new_state {
343                self.update_gen(move |view| {
344                    let _: Ignore = view.set_state(new_state);
345                })
346            }
347        }
348
349        if let Some(font_size) = self.vars.font_size().get_new()
350            && let Some(m) = &self.monitor
351        {
352            let scale_factor = m.scale_factor().get();
353            let screen_density = m.density().get();
354            let screen_size = m.size().get();
355            let mut font_size_px = self.content.outer_layout(scale_factor, screen_density, screen_size, || {
356                font_size.layout_dft_x(Length::pt_to_px(11.0, scale_factor))
357            });
358            if font_size_px < Px(0) {
359                tracing::error!("invalid font size {font_size:?} => {font_size_px:?}");
360                font_size_px = Length::pt_to_px(11.0, scale_factor);
361            }
362            let font_size_dip = font_size_px.to_dip(scale_factor);
363
364            if font_size_dip != self.root_font_size {
365                self.root_font_size = font_size_dip;
366                UPDATES.layout_window(WINDOW.id());
367            }
368        }
369
370        #[cfg(feature = "image")]
371        let mut img_res_loading = vec![];
372
373        // icon:
374        #[cfg(feature = "image")]
375        let mut send_icon = false;
376        #[cfg(feature = "image")]
377        if let Some(ico) = self.vars.icon().get_new() {
378            self.img_res.icon_var = match ico {
379                WindowIcon::Default => None,
380                WindowIcon::Image(ImageSource::Render(ico, _)) => {
381                    Some(IMAGES.cache(ImageSource::Render(ico.clone(), Some(ImageRenderArgs::new(WINDOW.id())))))
382                }
383                WindowIcon::Image(source) => Some(IMAGES.cache(source)),
384            };
385
386            if let Some(ico) = &self.img_res.icon_var {
387                self.img_res.icon_binding = ico.bind_map(&self.vars.0.actual_icon, |img| Some(img.clone()));
388
389                if ico.get().is_loading() && self.window.is_none() && !self.waiting_view {
390                    img_res_loading.push(ico.clone());
391                }
392            } else {
393                self.vars.0.actual_icon.set(None);
394                self.img_res.icon_binding = VarHandle::dummy();
395            }
396
397            send_icon = true;
398        } else if self.img_res.icon_var.as_ref().map(|ico| ico.is_new()).unwrap_or(false) {
399            send_icon = true;
400        }
401        #[cfg(feature = "image")]
402        if send_icon {
403            let icon = self.img_res.icon_var.as_ref().and_then(|ico| ico.get().view().cloned());
404            self.update_gen(move |view| {
405                let _: Ignore = view.set_icon(icon.as_ref());
406            });
407        }
408
409        // cursor (image):
410        if let Some(cursor) = self.vars.cursor().get_new() {
411            match cursor {
412                crate::CursorSource::Icon(ico) => {
413                    #[cfg(feature = "image")]
414                    {
415                        self.img_res.cursor_var = None;
416                    }
417                    self.update_gen(move |view| {
418                        let _: Ignore = view.set_cursor(Some(ico));
419                        #[cfg(feature = "image")]
420                        let _: Ignore = view.set_cursor_image(None, PxPoint::zero());
421                    });
422                }
423                #[cfg(feature = "image")]
424                crate::CursorSource::Img(img) => {
425                    self.img_res.cursor_var = Some(match img.source {
426                        ImageSource::Render(cur, _) => {
427                            IMAGES.cache(ImageSource::Render(cur.clone(), Some(ImageRenderArgs::new(WINDOW.id()))))
428                        }
429                        source => IMAGES.cache(source),
430                    });
431
432                    self.update_gen(move |view| {
433                        let _: Ignore = view.set_cursor(Some(img.fallback));
434                        let _: Ignore = view.set_cursor_image(None, PxPoint::zero());
435                    });
436                }
437                crate::CursorSource::Hidden => {
438                    #[cfg(feature = "image")]
439                    {
440                        self.img_res.cursor_var = None;
441                    }
442                    self.update_gen(move |view| {
443                        let _: Ignore = view.set_cursor(None);
444                        #[cfg(feature = "image")]
445                        let _: Ignore = view.set_cursor_image(None, PxPoint::zero());
446                    });
447                }
448            }
449
450            #[cfg(feature = "image")]
451            if let Some(cur) = &self.img_res.cursor_var {
452                let hotspot = self.vars.cursor().with(|i| i.hotspot().cloned().unwrap_or_default());
453
454                let cursor_img_to_actual = move |img: &Img| -> Option<(Img, PxPoint)> {
455                    let hotspot = if img.is_loaded() {
456                        let mut metrics = LayoutMetrics::new(1.fct(), img.size(), Px(16));
457                        if let Some(density) = img.density() {
458                            metrics = metrics.with_screen_density(density.width);
459                        }
460
461                        LAYOUT.with_context(metrics, || hotspot.layout())
462                    } else {
463                        PxPoint::zero()
464                    };
465
466                    Some((img.clone(), hotspot))
467                };
468                self.vars.0.actual_cursor_img.set_from_map(cur, cursor_img_to_actual.clone());
469                self.img_res.cursor_binding = cur.bind_map(&self.vars.0.actual_cursor_img, cursor_img_to_actual);
470
471                if cur.get().is_loading() && self.window.is_none() && !self.waiting_view {
472                    img_res_loading.push(cur.clone());
473                }
474            } else {
475                self.vars.0.actual_cursor_img.set(None);
476                self.img_res.cursor_binding = VarHandle::dummy();
477            }
478        }
479        #[cfg(feature = "image")]
480        if let Some(img_hotspot) = self.vars.0.actual_cursor_img.get_new() {
481            self.update_gen(move |view| match img_hotspot {
482                Some((img, hotspot)) => {
483                    let _: Ignore = view.set_cursor_image(img.view(), hotspot);
484                }
485                None => {
486                    let _: Ignore = view.set_cursor_image(None, PxPoint::zero());
487                }
488            })
489        }
490
491        // setup init wait for images
492        #[cfg(feature = "image")]
493        if !img_res_loading.is_empty() {
494            if self.img_res.deadline.has_elapsed() {
495                UPDATES.layout_window(WINDOW.id());
496            } else {
497                let window_id = WINDOW.id();
498                TIMERS
499                    .on_deadline(
500                        self.img_res.deadline,
501                        hn_once!(|_| {
502                            if img_res_loading.iter().any(|i| i.get().is_loading()) {
503                                // window maybe still waiting.
504                                UPDATES.layout_window(window_id);
505                            }
506                        }),
507                    )
508                    .perm();
509            }
510        }
511
512        if let Some(title) = self.vars.title().get_new() {
513            self.update_gen(move |view| {
514                let _: Ignore = view.set_title(title);
515            });
516        }
517
518        if let Some(mode) = self.vars.video_mode().get_new() {
519            self.update_gen(move |view| {
520                let _: Ignore = view.set_video_mode(mode);
521            });
522        }
523
524        if let Some(visible) = self.vars.taskbar_visible().get_new() {
525            self.update_gen(move |view| {
526                let _: Ignore = view.set_taskbar_visible(visible);
527            });
528        }
529
530        if let Some(top) = self.vars.always_on_top().get_new() {
531            self.update_gen(move |view| {
532                let _: Ignore = view.set_always_on_top(top);
533            });
534        }
535
536        #[cfg(feature = "image")]
537        if let Some(mode) = self.vars.frame_capture_mode().get_new() {
538            self.update_gen(move |view| {
539                let _: Ignore = view.set_capture_mode(matches!(mode, FrameCaptureMode::All));
540            });
541        }
542
543        if let Some(m) = &self.monitor {
544            if let Some(fct) = m.scale_factor().get_new() {
545                self.vars.0.scale_factor.set(fct);
546            }
547            if m.scale_factor().is_new() || m.size().is_new() || m.density().is_new() {
548                UPDATES.layout_window(WINDOW.id());
549            }
550        }
551
552        if let Some(indicator) = self.vars.focus_indicator().get_new() {
553            if WINDOWS.is_focused(WINDOW.id()).unwrap_or(false) {
554                self.vars.focus_indicator().set(None);
555            } else if let Some(view) = &self.window {
556                let _ = view.set_focus_indicator(indicator);
557                // will be set to `None` once the window is focused.
558            }
559            // else indicator is send with init.
560        }
561
562        let mut update_colors = false;
563
564        if update_parent(&mut self.actual_parent, &self.vars) {
565            self.parent_color_scheme = self
566                .actual_parent
567                .and_then(|id| WINDOWS.vars(id).ok().map(|v| v.actual_color_scheme()));
568            self.parent_accent_color = self
569                .actual_parent
570                .and_then(|id| WINDOWS.vars(id).ok().map(|v| v.actual_accent_color()));
571            update_colors = true;
572        }
573
574        if update_colors || self.vars.color_scheme().is_new() || self.parent_color_scheme.as_ref().map(|t| t.is_new()).unwrap_or(false) {
575            let scheme = self
576                .vars
577                .color_scheme()
578                .get()
579                .or_else(|| self.parent_color_scheme.as_ref().map(|t| t.get()))
580                .unwrap_or_else(|| WINDOWS.system_colors_config().scheme);
581            self.vars.0.actual_color_scheme.set(scheme);
582        }
583        if update_colors || self.vars.accent_color().is_new() || self.parent_accent_color.as_ref().map(|t| t.is_new()).unwrap_or(false) {
584            let accent = self
585                .vars
586                .accent_color()
587                .get()
588                .or_else(|| self.parent_accent_color.as_ref().map(|t| t.get()))
589                .unwrap_or_else(|| WINDOWS.system_colors_config().accent.into());
590            self.vars.0.actual_accent_color.set(accent);
591        }
592
593        if self.vars.0.access_enabled.is_new() {
594            UPDATES.update_info_window(WINDOW.id());
595        } else if self.vars.0.access_enabled.get() == AccessEnabled::VIEW && WINDOW_FOCUS.focused().is_new() {
596            self.update_access_focused();
597        }
598
599        if super::IME_EVENT.has_subscribers() && WINDOW_FOCUS.focused().is_new() {
600            self.update_ime();
601        }
602
603        self.content.update(update_widgets);
604    }
605
606    pub fn pre_event(&mut self, update: &EventUpdate) {
607        if let Some(args) = RAW_WINDOW_CHANGED_EVENT.on(update) {
608            if args.window_id == WINDOW.id() {
609                let mut state_change = None;
610                let mut pos_change = None;
611                let mut size_change = None;
612
613                if let Some(monitor) = args.monitor
614                    && self.vars.0.actual_monitor.get().map(|m| m != monitor).unwrap_or(true)
615                {
616                    self.vars.0.actual_monitor.set(Some(monitor));
617                    self.monitor = MONITORS.monitor(monitor);
618                    if let Some(m) = &self.monitor {
619                        let fct = m.scale_factor().get();
620                        self.vars.0.scale_factor.set(fct);
621                    }
622                    UPDATES.layout_window(WINDOW.id());
623                }
624
625                if let Some(state) = args.state.clone() {
626                    self.vars.state().set(state.state);
627                    self.vars.0.restore_rect.set(state.restore_rect);
628                    self.vars.0.restore_state.set(state.restore_state);
629
630                    let new_state = state.state;
631                    if self.actual_state != Some(new_state) {
632                        let prev_state = self.actual_state.unwrap_or(WindowState::Normal);
633                        state_change = Some((prev_state, new_state));
634                        self.actual_state = Some(new_state);
635
636                        match (prev_state, new_state) {
637                            (_, WindowState::Minimized) => {
638                                // minimized, minimize children.
639                                self.vars.0.children.with(|c| {
640                                    for &c in c.iter() {
641                                        MINIMIZE_CMD.scoped(c).notify();
642                                    }
643                                });
644                            }
645                            (WindowState::Minimized, _) => {
646                                // restored, restore children.
647                                self.vars.0.children.with(|c| {
648                                    for &c in c.iter() {
649                                        RESTORE_CMD.scoped(c).notify();
650                                    }
651                                });
652
653                                // we skip layout & render when minimized.
654                                let w_id = WINDOW.id();
655                                UPDATES.layout_window(w_id).render_window(w_id);
656                            }
657                            _ => {}
658                        }
659                    }
660
661                    self.state = Some(state);
662                }
663
664                if let Some((global_pos, pos)) = args.position
665                    && (self.vars.0.actual_position.get() != pos || self.vars.0.global_position.get() != global_pos)
666                {
667                    self.vars.0.actual_position.set(pos);
668                    self.vars.0.global_position.set(global_pos);
669                    pos_change = Some((global_pos, pos));
670                }
671
672                if let Some(size) = args.size
673                    && self.vars.0.actual_size.get() != size
674                {
675                    self.vars.0.actual_size.set(size);
676                    size_change = Some(size);
677
678                    UPDATES.layout_window(WINDOW.id());
679
680                    if args.cause == EventCause::System {
681                        // resize by system (user)
682                        self.vars.auto_size().set(AutoSize::DISABLED);
683                    }
684                }
685
686                if let Some(padding) = args.safe_padding {
687                    self.vars.0.safe_padding.set(padding);
688                }
689
690                if let Some(id) = args.frame_wait_id {
691                    self.resize_wait_id = Some(id);
692
693                    UPDATES.render_update_window(WINDOW.id());
694                }
695
696                if state_change.is_some() || pos_change.is_some() || size_change.is_some() {
697                    let args = WindowChangedArgs::new(
698                        args.timestamp,
699                        args.propagation().clone(),
700                        args.window_id,
701                        state_change,
702                        pos_change,
703                        size_change,
704                        args.cause,
705                    );
706                    WINDOW_CHANGED_EVENT.notify(args);
707                }
708            } else if self.actual_state.unwrap_or(WindowState::Normal) == WindowState::Minimized
709                && args.state.as_ref().map(|s| s.state != WindowState::Minimized).unwrap_or(false)
710                && self.vars.0.children.with(|c| c.contains(&args.window_id))
711            {
712                // child restored.
713                RESTORE_CMD.scoped(WINDOW.id()).notify();
714            }
715        } else if let Some(args) = RAW_WINDOW_FOCUS_EVENT.on(update) {
716            if args.new_focus == Some(WINDOW.id()) {
717                self.vars.0.children.with(|c| {
718                    for &c in c.iter() {
719                        let _ = WINDOWS.bring_to_top(c);
720                    }
721                });
722            } else if let Some(new_focus) = args.new_focus {
723                self.vars.0.children.with(|c| {
724                    if c.contains(&new_focus) {
725                        let _ = WINDOWS.bring_to_top(WINDOW.id());
726
727                        for c in c.iter() {
728                            if *c != new_focus {
729                                let _ = WINDOWS.bring_to_top(WINDOW.id());
730                            }
731                        }
732
733                        let _ = WINDOWS.bring_to_top(new_focus);
734                    }
735                });
736            }
737        } else if let Some(args) = MONITORS_CHANGED_EVENT.on(update) {
738            if let Some(m) = &self.monitor
739                && args.removed.contains(&m.id())
740            {
741                self.monitor = None;
742                self.vars.0.actual_monitor.set(None);
743            }
744            self.vars.monitor().update();
745        } else if let Some(args) = RAW_WINDOW_OPEN_EVENT.on(update) {
746            if args.window_id == WINDOW.id() {
747                self.waiting_view = false;
748
749                WINDOWS.set_view(args.window_id, args.window.clone().into());
750
751                self.window = Some(args.window.clone());
752                self.cancel_ime_handle = super::cmd::CANCEL_IME_CMD.scoped(WINDOW.id()).subscribe(true);
753                self.open_title_menu_handle = super::cmd::OPEN_TITLE_BAR_CONTEXT_MENU_CMD.scoped(WINDOW.id()).subscribe(true);
754                self.drag_move_handle = super::cmd::DRAG_MOVE_RESIZE_CMD.scoped(WINDOW.id()).subscribe(true);
755
756                self.vars.0.render_mode.set(args.data.render_mode);
757                self.vars.state().set(args.data.state.state);
758                self.actual_state = Some(args.data.state.state);
759                self.vars.0.restore_state.set(args.data.state.restore_state);
760                self.vars.0.restore_rect.set(args.data.state.restore_rect);
761                self.vars.0.global_position.set(args.data.position.0);
762                self.vars.0.actual_position.set(args.data.position.1);
763                self.vars.0.actual_size.set(args.data.size);
764                self.vars.0.safe_padding.set(args.data.safe_padding);
765                self.vars.0.actual_monitor.set(args.data.monitor);
766                self.vars.0.scale_factor.set(args.data.scale_factor);
767
768                self.state = Some(args.data.state.clone());
769
770                let scheme = self
771                    .vars
772                    .color_scheme()
773                    .get()
774                    .or_else(|| self.parent_color_scheme.as_ref().map(|t| t.get()))
775                    .unwrap_or_else(|| WINDOWS.system_colors_config().scheme);
776                self.vars.0.actual_color_scheme.set(scheme);
777                let accent = self
778                    .vars
779                    .accent_color()
780                    .get()
781                    .or_else(|| self.parent_accent_color.as_ref().map(|t| t.get()))
782                    .unwrap_or_else(|| WINDOWS.system_colors_config().accent.into());
783                self.vars.0.actual_accent_color.set(accent);
784
785                UPDATES.layout_window(args.window_id).render_window(args.window_id);
786
787                for update in mem::take(&mut self.delayed_view_updates) {
788                    update(&args.window);
789                }
790            }
791        } else if let Some(args) = RAW_COLORS_CONFIG_CHANGED_EVENT.on(update) {
792            let scheme = self
793                .vars
794                .color_scheme()
795                .get()
796                .or_else(|| self.parent_color_scheme.as_ref().map(|t| t.get()))
797                .unwrap_or(args.config.scheme);
798            self.vars.0.actual_color_scheme.set(scheme);
799            let color = self
800                .vars
801                .accent_color()
802                .get()
803                .or_else(|| self.parent_accent_color.as_ref().map(|t| t.get()))
804                .unwrap_or_else(|| args.config.accent.into());
805            self.vars.0.actual_accent_color.set(color);
806        } else if let Some(args) = RAW_WINDOW_OR_HEADLESS_OPEN_ERROR_EVENT.on(update) {
807            let w_id = WINDOW.id();
808            if args.window_id == w_id && self.window.is_none() && self.waiting_view {
809                tracing::error!("view-process failed to open a window, {}", args.error);
810
811                // was waiting view and failed, treat like a respawn.
812
813                self.waiting_view = false;
814                self.delayed_view_updates = vec![];
815                self.respawned = true;
816
817                UPDATES.layout_window(w_id).render_window(w_id);
818            }
819        } else if let Some(args) = RAW_IME_EVENT.on(update) {
820            let w_id = WINDOW.id();
821            if args.window_id == w_id {
822                match &args.ime {
823                    Ime::Preview(s, c) => {
824                        if let Some(info) = &mut self.ime_info {
825                            info.has_preview = !s.is_empty();
826                            let args = super::ImeArgs::now(info.target.clone(), s.clone(), *c);
827                            super::IME_EVENT.notify(args);
828                        }
829                    }
830                    Ime::Commit(s) => {
831                        if let Some(info) = &mut self.ime_info {
832                            info.has_preview = false;
833                            let args = super::ImeArgs::now(info.target.clone(), s.clone(), None);
834                            super::IME_EVENT.notify(args);
835                        }
836                    }
837                }
838            }
839        } else if let Some(args) = ACCESS_INITED_EVENT.on(update) {
840            if args.window_id == WINDOW.id() {
841                tracing::info!("accessibility info enabled in view for {:?}", args.window_id);
842                self.vars.0.access_enabled.set(AccessEnabled::VIEW);
843            }
844        } else if let Some(args) = ACCESS_DEINITED_EVENT.on(update) {
845            if args.window_id == WINDOW.id() {
846                tracing::info!("accessibility info disabled in view for {:?}", args.window_id);
847                self.vars.0.access_enabled.modify(|a| {
848                    if a.is_enabled() {
849                        **a = AccessEnabled::APP;
850                    }
851                });
852            }
853        } else if let Some(args) = VIEW_PROCESS_INITED_EVENT.on(update)
854            && self
855                .window
856                .as_ref()
857                .map(|w| w.renderer().generation() != Ok(args.generation))
858                .unwrap_or(args.is_respawn)
859        {
860            debug_assert!(args.is_respawn);
861
862            self.window = None;
863            self.cancel_ime_handle = CommandHandle::dummy();
864            self.open_title_menu_handle = CommandHandle::dummy();
865            self.drag_move_handle = CommandHandle::dummy();
866            self.waiting_view = false;
867            self.delayed_view_updates = vec![];
868            self.respawned = true;
869
870            let w_id = WINDOW.id();
871            UPDATES.layout_window(w_id).render_window(w_id);
872        }
873
874        self.content.pre_event(update);
875
876        if self.ime_info.is_some() && super::cmd::CANCEL_IME_CMD.scoped(WINDOW.id()).has(update) {
877            let prev = self.ime_info.take().unwrap();
878            if prev.has_preview {
879                let args = super::ImeArgs::now(prev.target, "", (0, 0));
880                super::IME_EVENT.notify(args);
881            }
882            if let Some(w) = &self.window {
883                let _ = w.set_ime_area(None);
884            }
885        } else if let Some(args) = super::cmd::DRAG_MOVE_RESIZE_CMD.scoped(WINDOW.id()).on(update) {
886            let r = args.handle_enabled(&self.drag_move_handle, |args| args.param::<crate::cmd::ResizeDirection>().copied());
887            if let Some(r) = r {
888                self.view_task(Box::new(move |w| {
889                    let _ = if let Some(r) = r {
890                        w.unwrap().drag_resize(r)
891                    } else {
892                        w.unwrap().drag_move()
893                    };
894                }));
895            }
896        } else if let Some(args) = super::cmd::OPEN_TITLE_BAR_CONTEXT_MENU_CMD.scoped(WINDOW.id()).on(update) {
897            let pos = args.handle_enabled(&self.open_title_menu_handle, |args| {
898                if let Some(p) = args.param::<DipPoint>() {
899                    *p
900                } else if let Some(p) = args.param::<PxPoint>() {
901                    p.to_dip(self.vars.scale_factor().get())
902                } else {
903                    DipPoint::splat(Dip::new(24))
904                }
905            });
906            if let Some(pos) = pos {
907                self.view_task(Box::new(move |w| {
908                    let _ = w.unwrap().open_title_bar_context_menu(pos);
909                }));
910            }
911        };
912    }
913
914    pub fn ui_event(&mut self, update: &EventUpdate) {
915        self.content.ui_event(update);
916    }
917
918    #[must_use]
919    pub fn info(&mut self, info_widgets: Arc<InfoUpdates>) -> Option<WidgetInfoTree> {
920        let prev_tree = WINDOW.info();
921        let info = self.content.info(info_widgets);
922        if let Some(info) = &info
923            && self.window.is_some()
924        {
925            // updated widget info and has view-process window
926            if info.access_enabled() == AccessEnabled::VIEW && self.render_access_update.is_none() {
927                // view window requires access info, next frame
928                self.render_access_update = Some(prev_tree);
929                UPDATES.render_window(WINDOW.id());
930            } else if self.ime_info.is_some() {
931                UPDATES.render_window(WINDOW.id());
932            }
933        }
934
935        info
936    }
937
938    fn accessible_focused(&self, info: &WidgetInfoTree) -> Option<WidgetId> {
939        if WINDOWS.is_focused(info.window_id()).unwrap_or(false) {
940            WINDOW_FOCUS.focused().with(|p| {
941                if let Some(p) = p
942                    && p.window_id() == info.window_id()
943                    && let Some(wgt) = info.get(p.widget_id())
944                    && let Some(wgt) = wgt.access()
945                    && wgt.is_accessible()
946                {
947                    // is focused accessible widget inside window
948                    return Some(wgt.info().id());
949                }
950                None
951            })
952        } else {
953            None
954        }
955    }
956
957    fn update_access_focused(&mut self) {
958        if self.render_access_update.is_some() {
959            // will update next frame
960            return;
961        }
962        if let Some(view) = &self.window {
963            let info = WINDOW.info();
964            if info.access_enabled().is_enabled() {
965                let _ = view.access_update(zng_view_api::access::AccessTreeUpdate::new(
966                    vec![],
967                    None,
968                    self.accessible_focused(&info).unwrap_or(info.root().id()).into(),
969                ));
970            }
971        }
972    }
973
974    fn update_ime(&mut self) {
975        WINDOW_FOCUS.focused().with(|f| {
976            let mut ime_path = None;
977            if let Some(f) = f
978                && f.interactivity().is_enabled()
979                && f.window_id() == WINDOW.id()
980                && super::IME_EVENT.is_subscriber(f.widget_id())
981            {
982                ime_path = Some(f.as_path().clone());
983            }
984
985            if ime_path.as_ref() == self.ime_info.as_ref().map(|p| &p.target) {
986                return;
987            }
988
989            if let Some(p) = ime_path {
990                let info = WINDOW.info();
991                if let Some(w) = info.get(p.widget_id()) {
992                    if let Some(prev) = self.ime_info.take()
993                        && prev.has_preview
994                    {
995                        // clear
996                        let args = super::ImeArgs::now(prev.target, "", (0, 0));
997                        super::IME_EVENT.notify(args);
998                    }
999
1000                    self.ime_info = Some(ImeInfo {
1001                        target: p.clone(),
1002                        has_preview: false,
1003                        area: DipRect::zero(),
1004                    });
1005
1006                    if let Some(win) = &self.window {
1007                        let area = w.ime_area().to_dip(info.scale_factor());
1008                        self.ime_info.as_mut().unwrap().area = area;
1009
1010                        // set to `None` to force a refresh, some IME (MS Emoji) behave like
1011                        // they are in the same widget still if only the position changes
1012                        let _ = win.set_ime_area(None);
1013                        let _ = win.set_ime_area(Some(area));
1014                    }
1015                    return;
1016                }
1017            }
1018
1019            if let Some(prev) = self.ime_info.take() {
1020                if let Some(w) = &self.window {
1021                    let _ = w.set_ime_area(None);
1022                }
1023
1024                if prev.has_preview {
1025                    // clear
1026                    let args = super::ImeArgs::now(prev.target, "", (0, 0));
1027                    super::IME_EVENT.notify(args);
1028                }
1029            }
1030        });
1031    }
1032
1033    pub fn layout(&mut self, layout_widgets: Arc<LayoutUpdates>) {
1034        if !layout_widgets.delivery_list().enter_window(WINDOW.id()) {
1035            return;
1036        }
1037
1038        if self.window.is_some() {
1039            if matches!(self.state.as_ref().map(|s| s.state), Some(WindowState::Minimized)) {
1040                return;
1041            }
1042            self.layout_update(layout_widgets);
1043        } else if self.respawned && !self.waiting_view {
1044            self.layout_respawn();
1045        } else if !self.waiting_view {
1046            self.layout_init();
1047        }
1048    }
1049
1050    /// First layout, opens the window.
1051    fn layout_init(&mut self) {
1052        // await images load up to 1s.
1053        #[cfg(feature = "image")]
1054        if self.img_res.deadline.has_elapsed() {
1055            if let Some(icon) = &self.img_res.icon_var
1056                && icon.get().is_loading()
1057            {
1058                return;
1059            }
1060            if let Some(cursor) = &self.img_res.cursor_var
1061                && cursor.get().is_loading()
1062            {
1063                return;
1064            }
1065        }
1066        // update window "load" state, `is_loaded` and the `WindowLoadEvent` happen here.
1067        if !WINDOWS.try_load(WINDOW.id()) {
1068            // block on loading handles.
1069            return;
1070        }
1071
1072        self.monitor = Some(self.vars.monitor().get().select_fallback());
1073        let m = self.monitor.as_ref().unwrap();
1074        self.vars.0.scale_factor.set(m.scale_factor().get());
1075
1076        let scale_factor = m.scale_factor().get();
1077        let screen_density = m.density().get();
1078        let screen_rect = m.px_rect();
1079
1080        // Layout min, max and size in the monitor space.
1081        let (min_size, max_size, mut size, root_font_size) =
1082            self.content.outer_layout(scale_factor, screen_density, screen_rect.size, || {
1083                let min_size = self.vars.min_size().layout_dft(default_min_size(scale_factor));
1084                let max_size = self.vars.max_size().layout_dft(screen_rect.size);
1085                let size = self.vars.size().layout_dft(default_size(scale_factor));
1086
1087                let font_size = self.vars.font_size().get();
1088                let mut root_font_size = font_size.layout_dft_x(Length::pt_to_px(11.0, scale_factor));
1089                if root_font_size < Px(0) {
1090                    tracing::error!("invalid font size {font_size:?} => {root_font_size:?}");
1091                    root_font_size = Length::pt_to_px(11.0, scale_factor);
1092                }
1093
1094                (min_size, max_size, size.min(max_size).max(min_size), root_font_size)
1095            });
1096
1097        self.root_font_size = root_font_size.to_dip(scale_factor);
1098
1099        let state = self.vars.state().get();
1100        if state == WindowState::Normal && self.vars.auto_size().get() != AutoSize::DISABLED {
1101            // layout content to get auto-size size.
1102            size = self.content.layout(
1103                Arc::default(),
1104                scale_factor,
1105                screen_density,
1106                min_size,
1107                max_size,
1108                size,
1109                root_font_size,
1110                false,
1111            );
1112        }
1113
1114        // Layout initial position in the monitor space.
1115        let mut system_pos = false;
1116        let position = match self.start_position {
1117            StartPosition::Default => {
1118                let pos = self.vars.position().get();
1119                if pos.x.is_default() || pos.y.is_default() {
1120                    system_pos = true;
1121                    screen_rect.origin + PxVector::splat(Px(40))
1122                } else {
1123                    self.content.outer_layout(scale_factor, screen_density, screen_rect.size, || {
1124                        pos.layout() + screen_rect.origin.to_vector()
1125                    })
1126                }
1127            }
1128            StartPosition::CenterMonitor => {
1129                PxPoint::new(
1130                    (screen_rect.size.width - size.width) / Px(2),
1131                    (screen_rect.size.height - size.height) / Px(2),
1132                ) + screen_rect.origin.to_vector()
1133            }
1134            StartPosition::CenterParent => {
1135                // center monitor if no parent
1136                let mut parent_rect = screen_rect;
1137
1138                if let Some(parent) = self.vars.parent().get()
1139                    && let Ok(w) = WINDOWS.vars(parent)
1140                {
1141                    let factor = w.scale_factor().get();
1142                    let pos = w.actual_position().get().to_px(factor);
1143                    let size = w.actual_size().get().to_px(factor);
1144
1145                    parent_rect = PxRect::new(pos, size);
1146                }
1147
1148                PxPoint::new(
1149                    (parent_rect.size.width - size.width) / Px(2),
1150                    (parent_rect.size.height - size.height) / Px(2),
1151                ) + parent_rect.origin.to_vector()
1152            }
1153        };
1154
1155        // send view window request:
1156
1157        let m_position = (position - screen_rect.origin.to_vector()).to_dip(scale_factor);
1158        let size = size.to_dip(scale_factor);
1159
1160        let state = WindowStateAll::new(
1161            state,
1162            position,
1163            DipRect::new(m_position, size),
1164            WindowState::Normal,
1165            min_size.to_dip(scale_factor),
1166            max_size.to_dip(scale_factor),
1167            self.vars.chrome().get() && !WINDOWS.system_chrome().get().needs_custom(),
1168        );
1169
1170        let window_id = WINDOW.id();
1171
1172        let request = WindowRequest::new(
1173            zng_view_api::window::WindowId::from_raw(window_id.get()),
1174            self.vars.title().get(),
1175            state.clone(),
1176            self.kiosk.is_some(),
1177            system_pos,
1178            self.vars.video_mode().get(),
1179            self.vars.visible().get(),
1180            self.vars.taskbar_visible().get(),
1181            self.vars.always_on_top().get(),
1182            self.vars.movable().get(),
1183            self.vars.resizable().get(),
1184            {
1185                #[cfg(feature = "image")]
1186                {
1187                    self.img_res
1188                        .icon_var
1189                        .as_ref()
1190                        .and_then(|ico| ico.get().view().map(|ico| ico.id()))
1191                        .flatten()
1192                }
1193                #[cfg(not(feature = "image"))]
1194                None
1195            },
1196            self.vars.cursor().with(|c| c.icon()),
1197            {
1198                #[cfg(feature = "image")]
1199                {
1200                    self.vars
1201                        .actual_cursor_img()
1202                        .get()
1203                        .and_then(|(i, h)| i.view().and_then(|i| i.id()).map(|i| (i, h)))
1204                }
1205                #[cfg(not(feature = "image"))]
1206                None
1207            },
1208            self.transparent,
1209            {
1210                #[cfg(feature = "image")]
1211                {
1212                    matches!(self.vars.frame_capture_mode().get(), FrameCaptureMode::All)
1213                }
1214
1215                #[cfg(not(feature = "image"))]
1216                false
1217            },
1218            self.render_mode.unwrap_or_else(|| WINDOWS.default_render_mode().get()),
1219            self.vars.focus_indicator().get(),
1220            self.start_focused,
1221            self.ime_info.as_ref().and_then(|a| {
1222                let area = WINDOW.info().get(a.target.widget_id())?.ime_area().to_dip(scale_factor);
1223                Some(area)
1224            }),
1225            self.vars.enabled_buttons().get(),
1226            self.vars.system_shutdown_warn().get(),
1227            WINDOWS.take_view_extensions_init(window_id),
1228        );
1229
1230        if let Ok(()) = VIEW_PROCESS.open_window(request) {
1231            self.state = Some(state);
1232            self.waiting_view = true;
1233        } // else respawn
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<(), ViewChannelError>;
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}