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