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