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