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,
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
86struct 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 start_position: StartPosition,
98 start_focused: bool,
99 kiosk: Option<WindowState>, transparent: bool,
101 render_mode: Option<RenderMode>,
102
103 state: Option<WindowStateAll>, monitor: Option<MonitorInfo>,
106 resize_wait_id: Option<FrameWaitId>,
107 img_res: ImageResources,
108 actual_state: Option<WindowState>, 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>, 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 update(view);
157 } else if self.waiting_view {
158 self.delayed_view_updates.push(Box::new(update));
161 } else {
162 }
164 }
165
166 pub fn update(&mut self, update_widgets: &WidgetUpdates) {
167 if self.window.is_none() && !self.waiting_view {
168 UPDATES.layout_window(WINDOW.id());
170
171 if let Some(enforced_fullscreen) = self.kiosk {
172 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 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 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 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 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 }
526 }
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 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 self.vars.0.children.with(|c| {
615 for &c in c.iter() {
616 RESTORE_CMD.scoped(c).notify();
617 }
618 });
619
620 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 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 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 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 if info.access_enabled() == AccessEnabled::VIEW && self.render_access_update.is_none() {
890 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 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 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 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 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 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 fn layout_init(&mut self) {
1016 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 if !WINDOWS.try_load(WINDOW.id()) {
1031 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 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 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 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 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 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 } }
1175
1176 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 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 if let Some(mut update) = info.to_access_updates(&prev_tree) {
1319 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 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 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
1402fn 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 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 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 *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
1478struct 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 render_mode: Option<RenderMode>,
1488 headless_monitor: HeadlessMonitor,
1489 headless_simulator: HeadlessSimulator,
1490
1491 size: DipSize,
1493
1494 actual_parent: Option<WindowId>,
1495 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 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 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 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 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 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 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 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
1749struct HeadlessCtrl {
1751 vars: WindowVars,
1752 content: ContentCtrl,
1753
1754 headless_monitor: HeadlessMonitor,
1755 headless_simulator: HeadlessSimulator,
1756
1757 actual_parent: Option<WindowId>,
1758 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 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
1886struct 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 }
1924}
1925
1926#[derive(Clone, Copy)]
1927enum InitState {
1928 SkipOne,
1932 Init,
1933 Inited,
1934}
1935
1936struct 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 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(); } else {
1980 WIDGET.with_context(&mut self.root_ctx, WidgetUpdateMode::Bubble, || {
1982 update_widgets.with_widget(|| {
1983 self.root.update(update_widgets);
1984 });
1985 });
1986
1987 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 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(); }
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(); }
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 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 #[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 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 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 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 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
2290pub(super) struct WindowCtrl(WindowCtrlMode);
2292#[allow(clippy::large_enum_variant)] enum 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
2432type 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
2444struct NestedCtrl {
2446 c: Arc<Mutex<NestedContentCtrl>>,
2447 actual_parent: Option<WindowId>,
2448 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 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 UPDATES.layout(*wgt_id);
2563 }
2564 }
2565
2566 fn view_task(&self, task: Box<dyn FnOnce(Option<&ViewWindow>)>) {
2567 task(None)
2568 }
2569}
2570
2571pub 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 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 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 }
2643
2644 fn deinit(&mut self) {
2645 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 }
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 }
2671
2672 fn update(&mut self, _: &WidgetUpdates) {
2673 }
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 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 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
2754pub trait NestedWindowWidgetInfoExt {
2756 fn nested_window(&self) -> Option<WindowId>;
2758
2759 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)] enum 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
2786pub 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 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 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 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}