1use std::{any::Any, mem, pin::Pin, sync::Arc};
2
3use zng_app::{
4 APP, Deadline, async_hn_once,
5 render::{FrameBuilder, FrameUpdate},
6 static_id,
7 timer::{DeadlineHandle, TIMERS},
8 update::{LayoutUpdates, RenderUpdates, UPDATES},
9 view_process::{
10 VIEW_PROCESS, ViewHeadless, ViewRenderer, ViewWindow,
11 raw_events::{
12 RAW_COLORS_CONFIG_CHANGED_EVENT, RAW_HEADLESS_OPEN_EVENT, RAW_MONITORS_CHANGED_EVENT, RAW_WINDOW_FOCUS_EVENT,
13 RAW_WINDOW_OPEN_EVENT, RawWindowFocusArgs,
14 },
15 },
16 widget::{VarLayout as _, WIDGET, WidgetCtx, base::PARALLEL_VAR, info::WidgetInfoTree},
17 window::{MonitorId, WINDOW, WindowCtx, WindowId, WindowMode},
18};
19use zng_app_context::LocalContext;
20use zng_color::{COLOR_SCHEME_VAR, Rgba, colors::ACCENT_COLOR_VAR};
21use zng_layout::unit::{DipSize, TimeUnits as _};
22use zng_layout::{
23 context::LayoutPassId,
24 unit::{
25 Dip, DipPoint, DipToPx as _, FactorUnits as _, Layout2d as _, Length, Px, PxConstraints, PxConstraints2d, PxPoint, PxRect, PxSize,
26 PxToDip as _, PxVector,
27 },
28};
29use zng_state_map::StateId;
30use zng_task::parking_lot::Mutex;
31use zng_txt::Txt;
32use zng_unique_id::IdSet;
33use zng_var::{ResponderVar, ResponseVar, VarHandle};
34use zng_view_api::{
35 ViewProcessGen,
36 api_extension::{ApiExtensionId, ApiExtensionPayload},
37 config::{ColorsConfig, FontAntiAliasing},
38 window::{FrameId, FrameRequest, FrameUpdateRequest, FrameWaitId, HeadlessRequest, WindowCapability, WindowRequest, WindowState},
39};
40use zng_wgt::{
41 node::with_context_var,
42 prelude::{DIRECTION_VAR, LAYOUT, LayoutMetrics, UiNode, UiNodeImpl, WidgetInfo, WidgetInfoBuilder, WidgetLayout},
43};
44
45use crate::{
46 AutoSize, CloseWindowResult, MONITORS, OpenNestedHandlerArgs, StartPosition, WINDOW_CLOSE_EVENT, WINDOW_LOAD_EVENT, WINDOW_OPEN_EVENT,
47 WINDOWS, WINDOWS_EXTENSIONS, WINDOWS_SV, WidgetInfoImeArea as _, WidgetUpdateArgs, WindowCloseArgs, WindowInstanceState,
48 WindowLoadingHandle, WindowOpenArgs, WindowRoot, WindowRootExtenderArgs, WindowVars,
49};
50
51#[expect(non_camel_case_types)]
55pub trait WINDOW_Ext {
56 fn vars(&self) -> WindowVars {
58 WindowVars::req()
59 }
60
61 fn enable_access(&self) {
65 let vars = WINDOW.vars();
66 let access_enabled = &vars.0.access_enabled;
67 if access_enabled.get().is_disabled() {
68 access_enabled.modify(|e| **e |= zng_app::widget::info::access::AccessEnabled::APP);
69 }
70 }
71
72 fn loading_handle(&self, deadline: impl Into<Deadline>, debug_name: impl Into<Txt>) -> Option<WindowLoadingHandle> {
83 WINDOWS.loading_handle(WINDOW.id(), deadline, debug_name)
84 }
85
86 #[cfg(feature = "image")]
90 fn frame_image(&self, mask: Option<zng_ext_image::ImageMaskMode>) -> zng_ext_image::ImageVar {
91 WINDOWS.frame_image(WINDOW.id(), mask)
92 }
93
94 #[cfg(feature = "image")]
100 fn frame_image_rect(&self, rect: zng_layout::unit::PxRect, mask: Option<zng_ext_image::ImageMaskMode>) -> zng_ext_image::ImageVar {
101 WINDOWS.frame_image_rect(WINDOW.id(), rect, mask)
102 }
103
104 fn bring_to_top(&self) {
110 WINDOWS.bring_to_top(WINDOW.id());
111 }
112
113 fn close(&self) -> ResponseVar<CloseWindowResult> {
123 WINDOWS.close(WINDOW.id())
124 }
125}
126impl WINDOW_Ext for WINDOW {}
127
128pub(crate) struct WindowInstance {
129 pub(crate) mode: WindowMode,
130 pub(crate) pending_loading: std::sync::Weak<dyn Any + Send + Sync>,
131 pub(crate) vars: Option<WindowVars>,
132 pub(crate) info: Option<WidgetInfoTree>,
133 pub(crate) extensions_init: Vec<(ApiExtensionId, ApiExtensionPayload)>,
134 pub(crate) root: Option<WindowNode>,
135
136 pub(crate) view_window: Option<ViewWindow>,
137 pub(crate) view_headless: Option<ViewHeadless>,
138 pub(crate) renderer: Option<ViewRenderer>,
139
140 pub(crate) view_generation: ViewProcessGen,
141}
142impl WindowInstance {
143 pub(crate) fn new(
144 id: WindowId,
145 mode: WindowMode,
146 new_window: Pin<Box<dyn Future<Output = WindowRoot> + Send + 'static>>,
147 r: ResponderVar<WindowVars>,
148 ) -> Self {
149 let w = Self {
150 mode,
151 pending_loading: std::sync::Weak::<()>::new(),
152 vars: None,
153 info: None,
154 extensions_init: vec![],
155 root: None,
156 view_window: None,
157 view_headless: None,
158 renderer: None,
159 view_generation: ViewProcessGen::INVALID,
160 };
161 UPDATES
162 .run(async move {
163 let primary_scale_factor = match mode {
165 WindowMode::Headed => MONITORS
166 .primary_monitor()
167 .get()
168 .map(|m| m.scale_factor().get())
169 .unwrap_or_else(|| 1.fct()),
170 WindowMode::Headless | WindowMode::HeadlessWithRenderer => 1.fct(),
171 };
172
173 let system_colors = match mode {
174 WindowMode::Headed => RAW_COLORS_CONFIG_CHANGED_EVENT
175 .var_latest()
176 .get()
177 .map(|a| a.config)
178 .unwrap_or_default(),
179 WindowMode::Headless | WindowMode::HeadlessWithRenderer => ColorsConfig::default(),
180 };
181
182 let vars = {
183 let mut s = WINDOWS_SV.write();
184 let vars = WindowVars::new(s.default_render_mode.get(), primary_scale_factor, system_colors);
185 crate::hooks::hook_window_vars_cmds(id, &vars);
186 r.respond(vars.clone());
187 s.windows.get_mut(&id).unwrap().vars = Some(vars.clone());
188 vars
189 };
190 let mut ctx = WindowCtx::new(id, mode);
191 ctx.with_state(|s| s.borrow_mut().set(*crate::WINDOW_VARS_ID, vars.clone()));
192
193 struct CtxFut {
195 f: Pin<Box<dyn Future<Output = WindowRoot> + Send + 'static>>,
196 ctx: Option<WindowCtx>,
197 }
198 impl Future for CtxFut {
199 type Output = (WindowRoot, WindowCtx);
200
201 fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
202 let s = &mut *self.as_mut();
203 let r = WINDOW.with_context(s.ctx.as_mut().unwrap(), || s.f.as_mut().poll(cx));
204 match r {
205 std::task::Poll::Ready(w) => std::task::Poll::Ready((w, s.ctx.take().unwrap())),
206 std::task::Poll::Pending => std::task::Poll::Pending,
207 }
208 }
209 }
210 let new_window = CtxFut {
211 f: new_window,
212 ctx: Some(ctx),
213 };
214 let (mut root, mut win_ctx) = new_window.await;
215
216 win_ctx.set_widget_tree(WidgetInfoTree::wgt(id, root.id));
217
218 zng_task::yield_now().await;
220
221 let mut nested = None;
222
223 if root.kiosk {
225 vars.0.chrome.set(false);
226 let chrome_wk = vars.0.chrome.downgrade();
227 vars.0
228 .chrome
229 .hook(move |a| {
230 if !a.value() {
231 tracing::error!("cannot enable chrome in kiosk mode");
232 if let Some(c) = chrome_wk.upgrade() {
233 c.set(false);
234 }
235 }
236 true
237 })
238 .perm();
239
240 if !vars.0.state.get().is_fullscreen() {
241 let try_exclusive =
242 !VIEW_PROCESS.is_connected() || VIEW_PROCESS.info().window.contains(WindowCapability::EXCLUSIVE);
243 if try_exclusive {
244 vars.0.state.set(WindowState::Exclusive);
245 } else {
246 vars.0.state.set(WindowState::Fullscreen);
247 }
248 let state_wk = vars.0.state.downgrade();
249 vars.0
250 .state
251 .hook(move |a| {
252 if !a.value().is_fullscreen() {
253 tracing::error!("cannot exit fullscreen in kiosk mode");
254 if let Some(s) = state_wk.upgrade() {
255 let try_exclusive = !VIEW_PROCESS.is_connected()
256 || VIEW_PROCESS.info().window.contains(WindowCapability::EXCLUSIVE);
257 if try_exclusive {
258 s.set(WindowState::Exclusive);
259 } else {
260 s.set(WindowState::Fullscreen);
261 }
262 }
263 }
264
265 true
266 })
267 .perm();
268 }
269 }
270
271 let mut extenders;
273 let mut nest_handlers;
274 {
275 let mut s = WINDOWS_SV.write();
276 extenders = mem::take(&mut s.root_extenders).into_inner();
277 nest_handlers = mem::take(&mut s.open_nested_handlers).into_inner();
278 }
279 WINDOW.with_context(&mut win_ctx, || {
280 for ext in &mut extenders {
281 root.child = ext(WindowRootExtenderArgs {
282 root: mem::replace(&mut root.child, UiNode::nil()),
283 });
284 }
285 let child = mem::replace(&mut root.child, UiNode::nil());
287 let child = with_context_var(child, ACCENT_COLOR_VAR, vars.actual_accent_color());
288 let child = with_context_var(child, COLOR_SCHEME_VAR, vars.actual_color_scheme());
289 let child = with_context_var(child, PARALLEL_VAR, vars.parallel());
290 root.child = child;
291
292 let mut args = OpenNestedHandlerArgs::new();
293 for nest in &mut nest_handlers {
294 nest(&mut args);
295 if args.has_nested {
296 nested = Some(NestedData {
297 pending_layout: None,
298 pending_render: None,
299 });
300 break;
301 }
302 }
303 });
304 {
305 let mut s = WINDOWS_SV.write();
306 extenders.append(s.root_extenders.get_mut());
307 *s.root_extenders.get_mut() = extenders;
308 nest_handlers.append(s.open_nested_handlers.get_mut());
309 *s.open_nested_handlers.get_mut() = nest_handlers;
310 }
311
312 let mut root = WindowNode {
314 win_ctx,
315 wgt_ctx: WidgetCtx::new(root.id),
316 root: Mutex::new(root),
317
318 view_opening: VarHandle::dummy(),
319 view_spawned: false,
320
321 nested,
322
323 layout_pass: LayoutPassId::new(),
324 frame_id: FrameId::INVALID,
325 clear_color: Rgba::default(),
326 frame_wait_id: None,
327 };
328 root.with_root(|n| n.init());
329 UPDATES.update_info_window(id);
330 UPDATES.layout_window(id);
331 WINDOWS_SV.write().windows.get_mut(&id).unwrap().root = Some(root);
332 vars.0.instance_state.set(WindowInstanceState::Loading);
333 WINDOW_OPEN_EVENT.notify(WindowOpenArgs::now(id));
334
335 })
337 .perm();
338 w
339 }
340}
341
342pub(crate) struct WindowNode {
343 pub(crate) win_ctx: WindowCtx,
344 pub(crate) wgt_ctx: WidgetCtx,
345 pub(crate) root: Mutex<WindowRoot>,
347
348 pub(crate) view_opening: VarHandle,
349 pub(crate) view_spawned: bool,
351
352 pub(crate) nested: Option<NestedData>,
353
354 pub(crate) layout_pass: LayoutPassId,
355 pub(crate) frame_id: FrameId,
356 pub(crate) clear_color: Rgba,
357 pub(crate) frame_wait_id: Option<FrameWaitId>,
358}
359impl WindowNode {
360 pub(crate) fn with_root<R>(&mut self, f: impl FnOnce(&mut UiNode) -> R) -> R {
361 WINDOW.with_context(&mut self.win_ctx, || {
362 WIDGET.with_context(&mut self.wgt_ctx, zng_app::widget::WidgetUpdateMode::Bubble, || {
363 f(&mut self.root.get_mut().child)
364 })
365 })
366 }
367}
368
369pub(crate) struct NestedData {
370 pending_layout: Option<Arc<LayoutUpdates>>,
371 pending_render: Option<[Arc<RenderUpdates>; 2]>,
372}
373
374static_id! {
375 static ref NESTED_WINDOW_INFO_ID: StateId<WindowId>;
376}
377
378pub trait NestedWindowWidgetInfoExt {
380 fn nested_window(&self) -> Option<WindowId>;
382
383 fn nested_window_tree(&self) -> Option<WidgetInfoTree> {
385 WINDOWS.widget_tree(self.nested_window()?)
386 }
387}
388
389impl NestedWindowWidgetInfoExt for WidgetInfo {
390 fn nested_window(&self) -> Option<WindowId> {
391 self.meta().get_clone(*NESTED_WINDOW_INFO_ID)
392 }
393}
394
395pub(crate) fn layout_open_view(a: &mut WidgetUpdateArgs, updates: &Arc<LayoutUpdates>) {
396 if !updates.delivery_list().enter_window(a.id) {
397 return;
398 }
399
400 let vars = a.vars.take().unwrap();
401
402 if let Some(n) = &mut a.node.nested {
403 n.pending_layout = Some(updates.clone());
405 if let Some(t) = vars.0.nest_parent.get() {
406 UPDATES.layout(t);
407 }
408 return;
409 }
410
411 let mut monitor_rect = PxRect::zero();
413 let monitor_density;
414 let mut scale_factor = 1.fct();
415 if a.node.win_ctx.mode().is_headed() {
416 let monitor = vars.0.actual_monitor.get().and_then(|id| MONITORS.monitor(id));
418
419 let monitor = monitor.unwrap_or_else(|| vars.0.monitor.get().select_fallback(a.id));
421
422 if monitor.id() == MonitorId::fallback() && matches!(vars.0.instance_state.get(), WindowInstanceState::Loading) {
423 let handle = WINDOWS.loading_handle(a.id, 1.secs(), "monitors");
425 RAW_MONITORS_CHANGED_EVENT
426 .hook(move |_| {
427 let _hold = &handle;
428 false
429 })
430 .perm();
431 }
432
433 monitor_rect = monitor.px_rect();
434 monitor_density = monitor.density().get();
435 scale_factor = monitor.scale_factor().get();
436 } else {
437 debug_assert!(a.node.win_ctx.mode().is_headless());
438 let m = &a.node.root.get_mut().headless_monitor;
440 if let Some(f) = m.scale_factor {
441 scale_factor = f;
442 }
443 monitor_rect.size = m.size.to_px(scale_factor);
444 monitor_density = m.density;
445 }
446
447 let font_size_dft = Length::pt_to_px(11.0, scale_factor);
449 let monitor_metrics = || {
450 LayoutMetrics::new(scale_factor, monitor_rect.size, font_size_dft)
451 .with_screen_density(monitor_density)
452 .with_direction(DIRECTION_VAR.get())
453 };
454
455 let auto_size = if matches!(vars.state().get(), WindowState::Normal) {
457 vars.0.auto_size.get()
458 } else {
459 AutoSize::empty()
460 };
461
462 a.node.layout_pass = a.node.layout_pass.next();
464 let (final_size, min_size, max_size) = LAYOUT.with_root_context(a.node.layout_pass, monitor_metrics(), || {
465 let font_size = vars.0.font_size.layout_dft_x(font_size_dft);
467 LAYOUT.with_font_size(font_size, || {
468 let min_size = vars.0.min_size.layout();
470 let max_size = vars.0.max_size.layout_dft(PxSize::splat(Px::MAX)).max(min_size);
471 let mut size = vars.0.actual_size.get().to_px(scale_factor);
472 if size.is_empty() {
473 size = vars
475 .0
476 .size
477 .layout_dft(DipSize::new(Dip::new(800), Dip::new(600)).to_px(scale_factor));
478 }
479
480 let metrics = LAYOUT.metrics();
481 let mut root_cons = metrics.constraints();
482 let mut viewport = size;
483 if auto_size.contains(AutoSize::CONTENT_WIDTH) {
484 root_cons.x = PxConstraints::new_range(min_size.width, max_size.width);
485 viewport.width = monitor_rect.size.width;
486 } else {
487 root_cons.x = PxConstraints::new_exact(size.width);
488 }
489 if auto_size.contains(AutoSize::CONTENT_HEIGHT) {
490 root_cons.y = PxConstraints::new_range(min_size.height, max_size.height);
491 viewport.height = monitor_rect.size.height;
492 } else {
493 root_cons.y = PxConstraints::new_exact(size.height);
494 }
495
496 let metrics = metrics.with_constraints(root_cons).with_viewport(viewport);
498 let desired_size = LAYOUT.with_context(metrics, || {
499 a.node
500 .with_root(|n| WidgetLayout::with_root_widget(updates.clone(), |wl| n.layout(wl)))
501 });
502
503 let mut final_size = size;
505 if auto_size.contains(AutoSize::CONTENT_WIDTH) {
506 final_size.width = desired_size.width.max(min_size.width).min(max_size.width);
507 }
508 if auto_size.contains(AutoSize::CONTENT_HEIGHT) {
509 final_size.height = desired_size.height.max(min_size.height).min(max_size.height);
510 }
511
512 (final_size, min_size, max_size)
513 })
514 });
515
516 if a.node.wgt_ctx.is_pending_reinit() {
517 a.node.with_root(|_| WIDGET.update());
518 }
519
520 let size_dip = final_size.to_dip(scale_factor);
521 if !auto_size.is_empty() {
522 vars.0.actual_size.set(size_dip);
524 }
525 let min_size_dip = min_size.to_dip(scale_factor);
526 let max_size_dip = max_size.to_dip(scale_factor);
527 vars.0.actual_min_size.set(min_size_dip);
528 vars.0.actual_max_size.set(max_size_dip);
529
530 if matches!(vars.0.instance_state.get(), WindowInstanceState::Loading) {
532 let mut s = WINDOWS_SV.write();
533 let w = s.windows.get_mut(&a.id).unwrap();
534 if w.pending_loading.strong_count() > 0 {
535 tracing::debug!(" window {:?} skipping transition to Loaded, active loading handles", a.id);
537 return;
538 }
539 tracing::trace!("window {:?} has Loaded", a.id);
540 w.pending_loading = std::sync::Weak::<()>::new();
541 vars.0.instance_state.set(WindowInstanceState::Loaded { has_view: false });
542 if !a.node.win_ctx.mode().has_renderer() {
543 WINDOW_LOAD_EVENT.notify(WindowOpenArgs::now(a.id));
545 }
546 }
547
548 match a.node.win_ctx.mode() {
550 WindowMode::Headed => {
551 if let Some(view) = &a.view_window {
552 let prev_size = vars.0.actual_size.get().to_px(scale_factor);
554 if !auto_size.is_empty() && prev_size != final_size {
555 let mut s = vars.window_state_all();
556
557 let prev_center = LAYOUT.with_root_context(
558 LayoutPassId::new(),
559 monitor_metrics().with_constraints(PxConstraints2d::new_exact_size(prev_size)),
560 || vars.0.auto_size_origin.layout(),
561 );
562 let new_center = LAYOUT.with_root_context(
563 LayoutPassId::new(),
564 monitor_metrics().with_constraints(PxConstraints2d::new_exact_size(final_size)),
565 || vars.0.auto_size_origin.layout(),
566 );
567 let offset = if prev_size.is_empty() {
568 PxVector::zero()
569 } else {
570 prev_center.to_vector() - new_center.to_vector()
571 };
572 s.restore_rect.origin += offset.to_dip(scale_factor);
573 s.restore_rect.size = size_dip;
574 s.min_size = min_size_dip;
575 s.max_size = max_size_dip;
576 vars.0.restore_rect.modify(move |a| {
577 if a.value().size != size_dip {
578 a.value_mut().size = size_dip;
579 }
580 });
581 let _ = view.set_state(s);
582 }
583 } else if a.node.view_opening.is_dummy() {
584 if !VIEW_PROCESS.is_available() || !VIEW_PROCESS.is_connected() {
587 tracing::debug!("skipping view-process open window {:?}, no view-process connected", a.id);
588 return;
589 }
590
591 let id = a.node.win_ctx.id();
592
593 a.node.view_opening = RAW_WINDOW_OPEN_EVENT.hook(move |a| {
594 if a.window_id != id {
595 return true;
596 }
597
598 let mut s = WINDOWS_SV.write();
599 if let Some(w) = s.windows.get_mut(&id) {
600 let vars = w.vars.as_ref().unwrap();
601 vars.0.instance_state.set(WindowInstanceState::Loaded { has_view: true });
602 WINDOW_LOAD_EVENT.notify(WindowOpenArgs::now(id));
603 let r = w.root.as_mut().unwrap();
604 let window = a.window.upgrade().unwrap();
605
606 if mem::take(&mut r.root.get_mut().start_focused) {
607 let _ = window.focus();
608 }
609
610 w.view_generation = window.generation();
611 w.renderer = Some(window.renderer());
612 w.view_window = Some(window);
613 r.view_opening = VarHandle::dummy();
614 r.view_spawned = true;
615 UPDATES.render_window(id);
616
617 vars.set_from_view(|v| &v.0.state, a.data.state.state);
618 vars.set_from_view(|v| &v.0.global_position, a.data.state.global_position);
619 vars.set_from_view(|v| &v.0.restore_rect, a.data.state.restore_rect);
620 vars.set_from_view(|v| &v.0.restore_state, a.data.state.restore_state);
621 vars.set_from_view(|v| &v.0.restore_state_fullscreen, a.data.state.restore_state_fullscreen);
622 vars.set_from_view(|v| &v.0.chrome, a.data.state.chrome_visible);
623
624 vars.set_from_view(|v| &v.0.actual_monitor, a.data.monitor);
625 vars.set_from_view(|v| &v.0.actual_position, a.data.position.1);
626 vars.set_from_view(|v| &v.0.global_position, a.data.position.0);
627 vars.set_from_view(|v| &v.0.actual_size, a.data.size);
628 vars.set_from_view(|v| &v.0.scale_factor, a.data.scale_factor);
629 vars.set_from_view(|v| &v.0.refresh_rate, a.data.refresh_rate);
630 vars.set_from_view(|v| &v.0.render_mode, a.data.render_mode);
631 vars.set_from_view(|v| &v.0.safe_padding, a.data.safe_padding);
632
633 s.set_frame_duration();
634
635 tracing::trace!(
636 "open window {:?} in {:?}, {:?}",
637 id,
638 a.data.monitor,
639 (&a.data.state.global_position, a.data.size)
640 );
641 }
642
643 false
644 });
645
646 let mut system_pos = false;
648 let mut global_position = PxPoint::zero();
649 let mut position = DipPoint::zero();
650 if a.node.view_spawned {
651 global_position = vars.0.global_position.get();
653 position = vars.0.actual_position.get();
654 } else {
655 match a.node.root.get_mut().start_position {
656 StartPosition::Default => {
657 let pos = vars.0.position.get();
658 system_pos = pos.x.is_default() || pos.y.is_default();
659 if !system_pos {
660 LAYOUT.with_root_context(a.node.layout_pass, monitor_metrics(), || {
661 let pos = pos.layout();
662 position = pos.to_dip(scale_factor);
663 global_position = monitor_rect.origin + pos.to_vector();
664 });
665
666 let window_rect = PxRect::new(global_position, final_size);
667 if !matches!(window_rect.intersection(&monitor_rect), Some(inter) if inter.area() > window_rect.area() / Px(2))
668 {
669 system_pos = true;
671 }
672 }
673 if system_pos {
674 position = DipPoint::splat(Dip::new(60));
676 global_position = monitor_rect.origin + position.to_px(scale_factor).to_vector();
677 }
678 }
679 start_position => {
680 let center_rect = match start_position {
681 StartPosition::CenterMonitor => monitor_rect,
682 StartPosition::CenterParent => {
683 if let Some(parent_id) = vars.0.parent.get()
684 && let Some(parent_vars) = WINDOWS.vars(parent_id)
685 && matches!(parent_vars.0.instance_state.get(), WindowInstanceState::Loaded { has_view: true })
686 {
687 PxRect::new(parent_vars.0.global_position.get(), parent_vars.actual_size_px().get())
688 } else {
689 monitor_rect
690 }
691 }
692 _ => unreachable!(),
693 };
694 let center_point = center_rect.origin + center_rect.size.to_vector() / Px(2);
695
696 global_position = center_point - final_size.to_vector() / Px(2);
697
698 let title_height = Dip::new(30).to_px(scale_factor);
700 let correction_y = (monitor_rect.origin.y + title_height - global_position.y).max(Px(0));
701 global_position.y += correction_y;
702
703 let pos = (global_position - monitor_rect.origin).to_point();
704 position = pos.to_dip(scale_factor);
705 }
706 }
707 }
708
709 let mut state_all = vars.window_state_all();
710 state_all.global_position = global_position;
711 state_all.restore_rect.origin = position;
712 state_all.restore_rect.size = size_dip;
713 state_all.min_size = min_size_dip;
714 state_all.max_size = max_size_dip;
715
716 let mut ime_area = None;
717 {
718 let s = WINDOWS_SV.read();
719 s.focused.with(|f| {
720 if let Some(f) = f
721 && f.window_id() == id
722 && let Some(w) = s.windows.get(&id)
723 && let Some(i) = &w.info
724 && let Some(i) = i.get(f.widget_id())
725 && let Some(r) = i.ime_area()
726 {
727 ime_area = Some(r.to_dip(scale_factor));
728 }
729 });
730 }
731 let mut args = WindowRequest::new(
732 zng_view_api::window::WindowId::from_raw(id.get()),
733 vars.0.title.get(),
734 state_all,
735 a.node.root.get_mut().kiosk,
736 system_pos,
737 vars.0.video_mode.get(),
738 vars.0.visible.get(),
739 vars.0.taskbar_visible.get(),
740 vars.0.always_on_top.get(),
741 vars.0.movable.get(),
742 vars.0.resizable.get(),
743 vars.0.can_minimize.get(),
744 vars.0.can_maximize.get(),
745 vars.0.can_fullscreen.get(),
746 vars.0.can_close.get(),
747 #[cfg(feature = "image")]
748 vars.0.actual_icon.with(|w| w.as_ref().map(|w| w.view_handle().image_id())),
749 #[cfg(not(feature = "image"))]
750 None,
751 vars.0.cursor.with(|c| c.icon()),
752 #[cfg(feature = "image")]
753 vars.0
754 .actual_cursor_img
755 .with(|i| i.as_ref().map(|(i, p)| (i.view_handle().image_id(), *p))),
756 #[cfg(not(feature = "image"))]
757 None,
758 a.node.root.get_mut().transparent,
759 #[cfg(feature = "image")]
760 matches!(vars.0.frame_capture_mode.get(), crate::FrameCaptureMode::All),
761 #[cfg(not(feature = "image"))]
762 false,
763 a.node
764 .root
765 .get_mut()
766 .render_mode
767 .unwrap_or_else(|| WINDOWS.default_render_mode().get()),
768 vars.0.focus_indicator.get(),
769 vars.0.focused.get(),
770 ime_area,
771 vars.0.system_shutdown_warn.get(),
772 WINDOWS_EXTENSIONS.take_view_extensions_init(id),
773 );
774 args.cache_shaders = a
775 .node
776 .root
777 .get_mut()
778 .cache_shaders
779 .unwrap_or_else(|| WINDOWS.default_cache_shaders().get());
780 let r = VIEW_PROCESS.open_window(args);
781 if r.is_err() {
782 tracing::error!("view-process window {id:?} open request failed, will retry on respawn");
783 a.node.view_opening = VarHandle::dummy();
784 }
785 }
786 }
787 WindowMode::HeadlessWithRenderer => {
788 if let Some(view) = &a.view_headless {
789 if !auto_size.is_empty() {
790 let _ = view.set_size(size_dip, scale_factor);
791 }
792 } else if a.node.view_opening.is_dummy() {
793 if APP.window_mode().is_headless() && !vars.0.instance_state.get().is_loaded() {
794 let args = RawWindowFocusArgs::now(WINDOWS_SV.read().focused.with(|p| p.as_ref().map(|p| p.window_id())), Some(a.id));
796 RAW_WINDOW_FOCUS_EVENT.notify(args);
797 }
798
799 if !VIEW_PROCESS.is_connected() {
800 tracing::debug!("skipping view-process open headless {:?}, no view-process connected", a.id);
801 return;
802 }
803
804 let id = a.node.win_ctx.id();
806 a.node.view_opening = RAW_HEADLESS_OPEN_EVENT.hook(move |a| {
807 if a.window_id != id {
808 return true;
809 }
810
811 let mut s = WINDOWS_SV.write();
812 if let Some(w) = s.windows.get_mut(&id) {
813 w.vars
814 .as_ref()
815 .unwrap()
816 .0
817 .instance_state
818 .set(WindowInstanceState::Loaded { has_view: true });
819 WINDOW_LOAD_EVENT.notify(WindowOpenArgs::now(id));
820
821 let r = w.root.as_mut().unwrap();
822 let surface = a.surface.upgrade().unwrap();
823 w.view_generation = surface.generation();
824 w.renderer = Some(surface.renderer());
825 w.view_headless = Some(surface);
826 r.view_opening = VarHandle::dummy();
827 r.view_spawned = true;
828
829 UPDATES.render_window(id);
830 }
831
832 false
833 });
834
835 let mut args = HeadlessRequest::new(
836 zng_view_api::window::WindowId::from_raw(id.get()),
837 scale_factor,
838 size_dip,
839 a.node
840 .root
841 .get_mut()
842 .render_mode
843 .unwrap_or_else(|| WINDOWS.default_render_mode().get()),
844 WINDOWS_EXTENSIONS.take_view_extensions_init(id),
845 );
846 args.cache_shaders = a
847 .node
848 .root
849 .get_mut()
850 .cache_shaders
851 .unwrap_or_else(|| WINDOWS.default_cache_shaders().get());
852 let r = VIEW_PROCESS.open_headless(args);
853 if r.is_err() {
854 tracing::error!("view-process headless surface {id:?} open request failed, will retry on respawn");
855 a.node.view_opening = VarHandle::dummy();
856 }
857 }
858 }
859 WindowMode::Headless => {
860 if APP.window_mode().is_headless() && !vars.0.instance_state.get().is_loaded() {
861 let args = RawWindowFocusArgs::now(WINDOWS_SV.read().focused.with(|p| p.as_ref().map(|p| p.window_id())), Some(a.id));
863 RAW_WINDOW_FOCUS_EVENT.notify(args);
864 }
865 }
866 }
867}
868
869pub(crate) fn render(a: &mut WidgetUpdateArgs, render_widgets: &Arc<RenderUpdates>, render_update_widgets: &Arc<RenderUpdates>) {
870 if render_widgets.delivery_list().enter_window(a.id)
871 || (a.node.frame_id == FrameId::INVALID && render_update_widgets.delivery_list().enter_window(a.id))
872 {
873 if let Some(n) = &mut a.node.nested {
876 n.pending_render = Some([render_widgets.clone(), render_update_widgets.clone()]);
878 if let Some(t) = a.vars.take().unwrap().0.nest_parent.get() {
879 UPDATES.render(t);
880 }
881 return;
882 }
883
884 if matches!(a.node.win_ctx.mode(), WindowMode::Headed | WindowMode::HeadlessWithRenderer) && a.renderer.is_none() {
885 tracing::debug!("skipping render {:?}, no renderer connected", a.id);
887 return;
888 }
889
890 let vars = a.vars.take().unwrap();
891 let info = a.node.win_ctx.widget_tree();
892
893 a.node.frame_id = a.node.frame_id.next();
895 let mut frame = FrameBuilder::new(
896 render_widgets.clone(),
897 render_update_widgets.clone(),
898 a.node.frame_id,
899 a.node.wgt_ctx.id(),
900 &a.node.wgt_ctx.bounds(),
901 &info,
902 a.renderer.clone(),
903 vars.0.scale_factor.get(),
904 FontAntiAliasing::Default,
905 );
906 a.node.with_root(|n| {
907 n.render(&mut frame);
908 });
909 let frame = frame.finalize(&info);
910 a.node.clear_color = frame.clear_color;
911
912 let capture = vars.take_frame_capture();
913 let wait_id = a.node.frame_wait_id.take();
914 if let Some(r) = &a.renderer {
915 let _ = r.render(FrameRequest::new(
917 a.node.frame_id,
918 a.node.clear_color,
919 frame.display_list,
920 capture,
921 wait_id,
922 ));
923 } else {
924 #[cfg(feature = "image")]
925 {
926 let error = match capture {
927 zng_view_api::window::FrameCapture::None => "",
928 zng_view_api::window::FrameCapture::Full => "cannot capture frame, no renderer",
929 zng_view_api::window::FrameCapture::Mask(_) => "cannot capture frame mask, no renderer",
930 _ => "",
931 };
932 if !error.is_empty() {
933 let frame_image = zng_var::VarEq(zng_var::var(zng_ext_image::ImageEntry::new_error(error.into())));
934 crate::FRAME_IMAGE_READY_EVENT.notify(crate::FrameImageReadyArgs::now(a.id, a.node.frame_id, frame_image.downgrade()));
935 UPDATES.once_next_update("", move || {
936 let _hold = &frame_image;
937 });
938 }
939 }
940 }
941
942 if a.node.wgt_ctx.is_pending_reinit() {
943 a.node.with_root(|_| WIDGET.update());
944 }
945 } else if render_update_widgets.delivery_list().enter_window(a.id) {
946 if let Some(n) = &mut a.node.nested {
947 n.pending_render = Some([render_widgets.clone(), render_update_widgets.clone()]);
948 if let Some(t) = a.vars.take().unwrap().0.nest_parent.get() {
949 UPDATES.render_update(t);
950 }
951 return;
952 }
953
954 a.node.frame_id = a.node.frame_id.next_update();
956 let mut update = FrameUpdate::new(
957 render_update_widgets.clone(),
958 a.node.frame_id,
959 a.node.wgt_ctx.id(),
960 a.node.wgt_ctx.bounds(),
961 a.node.clear_color,
962 );
963 a.node.with_root(|n| {
964 n.render_update(&mut update);
965 });
966 let update = update.finalize(&a.node.win_ctx.widget_tree());
967 if let Some(c) = update.clear_color {
968 a.node.clear_color = c;
969 }
970 let vars = a.vars.take().unwrap();
971 let capture = vars.take_frame_capture();
972 let wait_id = a.node.frame_wait_id.take();
973
974 if let Some(r) = &a.renderer {
975 let _ = r.render_update(FrameUpdateRequest::new(
977 a.node.frame_id,
978 update.transforms,
979 update.floats,
980 update.colors,
981 update.clear_color,
982 capture,
983 wait_id,
984 update.extensions,
985 ));
986 }
987
988 if a.node.wgt_ctx.is_pending_reinit() {
989 a.node.with_root(|_| WIDGET.update());
990 }
991 }
992}
993
994pub struct NestedWindowNode {
998 window_id: WindowId,
999 close_deadline: DeadlineHandle,
1000}
1001impl NestedWindowNode {
1002 pub(crate) fn new(window_id: WindowId) -> Self {
1003 Self {
1004 window_id,
1005 close_deadline: DeadlineHandle::dummy(),
1006 }
1007 }
1008
1009 fn take_node(&self) -> Option<WindowNode> {
1010 WINDOWS_SV.write().windows.get_mut(&self.window_id)?.root.take()
1011 }
1012
1013 fn restore_node(&self, node: WindowNode) {
1014 if let Some(w) = WINDOWS_SV.write().windows.get_mut(&self.window_id) {
1015 w.root = Some(node);
1016 }
1017 }
1018}
1019impl UiNodeImpl for NestedWindowNode {
1021 fn children_len(&self) -> usize {
1022 0
1023 }
1024 fn with_child(&mut self, _: usize, _: &mut dyn FnMut(&mut UiNode)) {}
1025
1026 fn init(&mut self) {
1027 if let Some(mut n) = self.take_node() {
1028 let inner_vars = WINDOW.with_context(&mut n.win_ctx, || WINDOW.vars());
1031 let parent_id = WINDOW.id();
1032 let nest_id = WIDGET.id();
1033 inner_vars.0.parent.set(Some(parent_id));
1034 inner_vars.0.nest_parent.set(Some(nest_id));
1035
1036 let this = inner_vars.0.parent.downgrade();
1038 inner_vars
1039 .0
1040 .parent
1041 .hook(move |a| {
1042 if *a.value() != Some(parent_id) {
1043 this.upgrade().unwrap().set(Some(parent_id));
1044 }
1045 true
1046 })
1047 .perm();
1048
1049 self.restore_node(n);
1050 }
1051
1052 self.close_deadline = DeadlineHandle::dummy();
1054 }
1055
1056 fn deinit(&mut self) {
1057 let id = self.window_id;
1059 self.close_deadline = TIMERS.on_deadline(
1060 100.ms(),
1061 async_hn_once!(|_| {
1062 let r = WINDOWS.close(id).wait_rsp().await;
1063 if matches!(r, CloseWindowResult::Cancel) {
1064 tracing::error!("nested window {id} already deinited, cannot cancel close");
1066 let mut s = WINDOWS_SV.write();
1067 let mut windows = IdSet::new();
1068 if let Some(mut w) = s.windows.remove(&id) {
1069 let vars = w.vars.take().unwrap();
1070 vars.0.instance_state.set(WindowInstanceState::Closed);
1071 windows.insert(id);
1072
1073 if let Some(mut r) = w.root.take() {
1074 r.with_root(|n| n.deinit());
1075 }
1076 }
1077 WINDOW_CLOSE_EVENT.notify(WindowCloseArgs::now(windows));
1078 }
1079 }),
1080 );
1081 }
1082
1083 fn info(&mut self, info: &mut WidgetInfoBuilder) {
1084 info.set_meta(*NESTED_WINDOW_INFO_ID, self.window_id);
1085 }
1086
1087 fn measure(&mut self, wm: &mut zng_wgt::prelude::WidgetMeasure) -> PxSize {
1088 let mut r = PxSize::zero();
1089 if let Some(mut n) = self.take_node() {
1091 let mut ctx = LocalContext::capture_filtered(zng_app_context::CaptureFilter::app_only());
1092 ctx.with_context(|| {
1093 n.with_root(|n| {
1094 r = wm.with_widget(|wm| n.measure(wm));
1095 })
1096 });
1097 self.restore_node(n);
1098 }
1099 r
1100 }
1101
1102 fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
1103 let mut r = PxSize::zero();
1104 if let Some(mut n) = self.take_node() {
1105 let pending = n.nested.as_mut().unwrap().pending_layout.take();
1106 let mut ctx = LocalContext::capture_filtered(zng_app_context::CaptureFilter::app_only());
1107 ctx.with_context(|| {
1108 n.with_root(|n| {
1109 r = if let Some(p) = pending {
1110 wl.with_layout_updates(p, |wl| n.layout(wl))
1111 } else {
1112 wl.with_widget(|wl| n.layout(wl))
1113 };
1114 });
1115 });
1116 self.restore_node(n);
1117 }
1118 r
1119 }
1120
1121 fn render(&mut self, frame: &mut FrameBuilder) {
1122 if let Some(mut n) = self.take_node() {
1123 let [render_widgets, render_update_widgets] = n.nested.as_mut().unwrap().pending_render.take().unwrap_or_default();
1124 let mut ctx = LocalContext::capture_filtered(zng_app_context::CaptureFilter::app_only());
1125 ctx.with_context(|| {
1126 let root_id = n.wgt_ctx.id();
1127 let root_bounds = n.wgt_ctx.bounds();
1128 let info = n.win_ctx.widget_tree();
1129 n.with_root(|n| {
1130 frame.with_nested_window(
1131 render_widgets,
1132 render_update_widgets,
1133 root_id,
1134 &root_bounds,
1135 &info,
1136 FontAntiAliasing::Default,
1137 |frame| n.render(frame),
1138 );
1139 });
1140 });
1141 self.restore_node(n);
1142 }
1143 }
1144
1145 fn render_update(&mut self, update: &mut FrameUpdate) {
1146 if let Some(mut n) = self.take_node() {
1147 let [_, render_update_widgets] = n.nested.as_mut().unwrap().pending_render.take().unwrap_or_default();
1148 let mut ctx = LocalContext::capture_filtered(zng_app_context::CaptureFilter::app_only());
1149 ctx.with_context(|| {
1150 let root_id = n.wgt_ctx.id();
1151 let root_bounds = n.wgt_ctx.bounds();
1152 n.with_root(|n| {
1153 update.with_nested_window(render_update_widgets, root_id, root_bounds, |update| {
1154 n.render_update(update);
1155 });
1156 })
1157 });
1158 self.restore_node(n);
1159 }
1160 }
1161}