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