1use std::{mem, sync::Arc};
2
3use parking_lot::Mutex;
4use zng_app::{
5 APP, AppEventSender, Deadline, EXIT_REQUESTED_EVENT,
6 event::AnyEventArgs,
7 hn_once,
8 timer::{DeadlineHandle, TIMERS},
9 update::{EventUpdate, InfoUpdates, LayoutUpdates, RenderUpdates, UPDATES, WidgetUpdates},
10 view_process::{
11 self, VIEW_PROCESS, VIEW_PROCESS_INITED_EVENT, ViewWindowOrHeadless,
12 raw_events::{
13 RAW_CHROME_CONFIG_CHANGED_EVENT, RAW_COLORS_CONFIG_CHANGED_EVENT, RAW_WINDOW_CLOSE_EVENT, RAW_WINDOW_CLOSE_REQUESTED_EVENT,
14 RAW_WINDOW_FOCUS_EVENT,
15 },
16 },
17 widget::{
18 UiTaskWidget, WidgetId,
19 base::PARALLEL_VAR,
20 info::{InteractionPath, WidgetInfo, WidgetInfoTree},
21 node::UiNode,
22 },
23 window::{WINDOW, WindowCtx, WindowId, WindowMode},
24};
25use zng_app_context::app_local;
26
27use zng_color::{COLOR_SCHEME_VAR, colors::ACCENT_COLOR_VAR};
28use zng_layout::unit::FactorUnits;
29use zng_layout::unit::TimeUnits as _;
30use zng_task::{
31 ParallelIteratorExt, UiTask,
32 rayon::iter::{IntoParallelRefMutIterator, ParallelIterator},
33};
34use zng_txt::{ToTxt as _, Txt, formatx};
35use zng_unique_id::{IdMap, IdSet};
36use zng_var::{ResponderVar, ResponseVar, Var, const_var, impl_from_and_into_var, response_done_var, response_var, var, var_default};
37use zng_view_api::{
38 DragDropId,
39 api_extension::{ApiExtensionId, ApiExtensionPayload},
40 config::{ChromeConfig, ColorsConfig},
41 drag_drop::{DragDropData, DragDropEffect, DragDropError},
42 window::{RenderMode, WindowState},
43};
44use zng_wgt::node::with_context_var;
45
46use crate::{
47 CloseWindowResult, MONITORS, ViewExtensionError, WINDOW_CLOSE_EVENT, WINDOW_CLOSE_REQUESTED_EVENT, WINDOW_FOCUS_CHANGED_EVENT,
48 WINDOW_LOAD_EVENT, WINDOW_VARS_ID, WindowCloseArgs, WindowCloseRequestedArgs, WindowFocusChangedArgs, WindowLoadingHandle,
49 WindowManager, WindowNotFoundError, WindowOpenArgs, WindowRoot, WindowVars, cmd::WindowCommands, control::WindowCtrl,
50};
51
52#[cfg(feature = "image")]
53use std::any::Any;
54
55#[cfg(feature = "image")]
56use zng_app::view_process::{
57 ViewImage, ViewRenderer,
58 raw_events::{RAW_IMAGE_LOAD_ERROR_EVENT, RAW_IMAGE_LOADED_EVENT},
59};
60
61#[cfg(feature = "image")]
62use zng_ext_image::{ImageRenderWindowRoot, ImageRenderWindowsService, ImageVar, Img};
63
64#[cfg(feature = "image")]
65use crate::{FRAME_IMAGE_READY_EVENT, FrameCaptureMode, HeadlessMonitor, StartPosition};
66
67#[cfg(feature = "image")]
68use zng_view_api::{image::ImageMaskMode, ipc::ViewChannelError};
69
70#[cfg(feature = "image")]
71use zng_var::WeakVar;
72
73#[cfg(feature = "image")]
74use zng_layout::unit::{Factor, LengthUnits, PxRect};
75
76app_local! {
77 pub(super) static WINDOWS_SV: WindowsService = {
78 APP.extensions().require::<WindowManager>();
79 WindowsService::new()
80 };
81 static FOCUS_SV: Var<Option<InteractionPath>> = const_var(None);
82}
83pub(super) struct WindowsService {
84 exit_on_last_close: Var<bool>,
85 default_render_mode: Var<RenderMode>,
86 parallel: Var<ParallelWin>,
87 system_chrome: Var<ChromeConfig>,
88 root_extenders: Mutex<Vec<Box<dyn FnMut(WindowRootExtenderArgs) -> UiNode + Send>>>, open_nested_handlers: Mutex<Vec<Box<dyn FnMut(&mut crate::OpenNestedHandlerArgs) + Send>>>,
90
91 windows: IdMap<WindowId, AppWindow>,
92 windows_info: IdMap<WindowId, AppWindowInfo>,
93
94 open_loading: IdMap<WindowId, WindowLoading>,
95 open_requests: Vec<OpenWindowRequest>,
96 open_tasks: Vec<AppWindowTask>,
97
98 close_requests: Vec<CloseWindowRequest>,
99 close_responders: IdMap<WindowId, Vec<ResponderVar<CloseWindowResult>>>,
100 exit_on_close: bool,
101
102 focus_request: Option<WindowId>,
103 bring_to_top_requests: Vec<WindowId>,
104
105 #[cfg(feature = "image")]
106 frame_images: Vec<WeakVar<Img>>,
107
108 loading_deadline: Option<DeadlineHandle>,
109 latest_colors_cfg: ColorsConfig,
110
111 view_window_tasks: Vec<ViewWindowTask>,
112}
113impl WindowsService {
114 fn new() -> Self {
115 Self {
116 exit_on_last_close: var(true),
117 default_render_mode: var_default(),
118 root_extenders: Mutex::new(vec![]),
119 open_nested_handlers: Mutex::new(vec![]),
120 system_chrome: var_default(),
121 parallel: var_default(),
122 windows: IdMap::default(),
123 windows_info: IdMap::default(),
124 open_loading: IdMap::new(),
125 open_tasks: vec![],
126 open_requests: Vec::with_capacity(1),
127 exit_on_close: false,
128 close_responders: IdMap::default(),
129 close_requests: vec![],
130 focus_request: None,
131 bring_to_top_requests: vec![],
132 #[cfg(feature = "image")]
133 frame_images: vec![],
134 loading_deadline: None,
135 latest_colors_cfg: ColorsConfig::default(),
136 view_window_tasks: vec![],
137 }
138 }
139
140 fn open_impl(&mut self, id: WindowId, new_window: UiTask<WindowRoot>, force_headless: Option<WindowMode>) -> ResponseVar<WindowId> {
141 let (responder, response) = response_var();
142 let request = OpenWindowRequest {
143 id,
144 new: Mutex::new(new_window),
145 force_headless,
146 responder,
147 };
148 self.open_requests.push(request);
149 self.open_loading.insert(id, WindowLoading::new());
150 UPDATES.update(None);
151
152 response
153 }
154
155 fn loading_handle_impl(&mut self, window_id: WindowId, deadline: Deadline) -> Option<WindowLoadingHandle> {
156 let mut handle = None;
157
158 if let Some(info) = self.windows_info.get_mut(&window_id) {
159 if !info.is_loaded {
161 handle = Some(info.loading_handle.new_handle(UPDATES.sender(), deadline))
162 }
163
164 self.loading_deadline = None;
166 } else if let Some(h) = self.open_loading.get_mut(&window_id) {
167 handle = Some(h.new_handle(UPDATES.sender(), deadline));
169 }
170
171 handle
172 }
173
174 fn close_together(
175 &mut self,
176 windows: impl IntoIterator<Item = WindowId>,
177 ) -> Result<ResponseVar<CloseWindowResult>, WindowNotFoundError> {
178 let mut group = IdSet::default();
179
180 for w in windows {
181 if !self.windows_info.contains_key(&w) {
182 return Err(WindowNotFoundError::new(w));
183 }
184 group.insert(w);
185 }
186
187 if group.is_empty() {
188 return Ok(response_done_var(CloseWindowResult::Cancel));
189 }
190
191 let (responder, response) = response_var();
192 self.close_requests.push(CloseWindowRequest { responder, windows: group });
193 UPDATES.update(None);
194
195 Ok(response)
196 }
197
198 #[cfg(feature = "image")]
199 fn frame_image_impl(
200 &mut self,
201 window_id: WindowId,
202 action: impl FnOnce(&ViewRenderer) -> std::result::Result<ViewImage, ViewChannelError>,
203 ) -> ImageVar {
204 if let Some(w) = self.windows_info.get(&window_id) {
205 if let Some(r) = &w.view {
206 match action(&r.renderer()) {
207 Ok(img) => {
208 let img = Img::new(img);
209 let img = var(img);
210 self.frame_images.retain(|i| i.strong_count() > 0);
211 self.frame_images.push(img.downgrade());
212 img.read_only()
213 }
214 Err(_) => var(Img::dummy(Some(formatx!("{}", WindowNotFoundError::new(window_id))))).read_only(),
215 }
216 } else {
217 var(Img::dummy(Some(formatx!("window `{window_id}` is headless without renderer")))).read_only()
218 }
219 } else {
220 var(Img::dummy(Some(formatx!("{}", WindowNotFoundError::new(window_id))))).read_only()
221 }
222 }
223
224 fn view_window_task(&mut self, window_id: WindowId, task: impl FnOnce(Option<&view_process::ViewWindow>) + Send + 'static) {
225 self.view_window_tasks.push(ViewWindowTask {
226 window_id,
227 task: Mutex::new(Box::new(task)),
228 });
229 }
230
231 fn take_requests(
232 &mut self,
233 ) -> (
234 Vec<OpenWindowRequest>,
235 Vec<AppWindowTask>,
236 Vec<CloseWindowRequest>,
237 Option<WindowId>,
238 Vec<WindowId>,
239 Vec<ViewWindowTask>,
240 ) {
241 (
242 mem::take(&mut self.open_requests),
243 mem::take(&mut self.open_tasks),
244 mem::take(&mut self.close_requests),
245 self.focus_request.take(),
246 mem::take(&mut self.bring_to_top_requests),
247 mem::take(&mut self.view_window_tasks),
248 )
249 }
250}
251
252bitflags! {
253 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
259 #[serde(transparent)]
260 pub struct ParallelWin: u8 {
261 const UPDATE = 0b0001;
263 const EVENT = 0b0010;
265 const LAYOUT = 0b0100;
267 const RENDER = 0b1000;
269 }
270}
271impl Default for ParallelWin {
272 fn default() -> Self {
274 Self::all()
275 }
276}
277impl_from_and_into_var! {
278 fn from(all: bool) -> ParallelWin {
279 if all { ParallelWin::all() } else { ParallelWin::empty() }
280 }
281}
282
283pub struct WINDOWS;
289impl WINDOWS {
290 pub fn exit_on_last_close(&self) -> Var<bool> {
298 WINDOWS_SV.read().exit_on_last_close.clone()
299 }
300
301 pub fn default_render_mode(&self) -> Var<RenderMode> {
306 WINDOWS_SV.read().default_render_mode.clone()
307 }
308
309 pub fn parallel(&self) -> Var<ParallelWin> {
316 WINDOWS_SV.read().parallel.clone()
317 }
318
319 pub fn open<F: Future<Output = WindowRoot> + Send + 'static>(
334 &self,
335 new_window: impl IntoFuture<IntoFuture = F>,
336 ) -> ResponseVar<WindowId> {
337 WINDOWS_SV
338 .write()
339 .open_impl(WindowId::new_unique(), UiTask::new(None, new_window), None)
340 }
341
342 pub fn open_id<F: Future<Output = WindowRoot> + Send + 'static>(
348 &self,
349 window_id: impl Into<WindowId>,
350 new_window: impl IntoFuture<IntoFuture = F>,
351 ) -> ResponseVar<WindowId> {
352 let window_id = window_id.into();
353 self.assert_id_unused(window_id);
354 WINDOWS_SV.write().open_impl(window_id, UiTask::new(None, new_window), None)
355 }
356
357 pub fn open_headless<F: Future<Output = WindowRoot> + Send + 'static>(
366 &self,
367 new_window: impl IntoFuture<IntoFuture = F>,
368 with_renderer: bool,
369 ) -> ResponseVar<WindowId> {
370 WINDOWS_SV.write().open_impl(
371 WindowId::new_unique(),
372 UiTask::new(None, new_window),
373 Some(if with_renderer {
374 WindowMode::HeadlessWithRenderer
375 } else {
376 WindowMode::Headless
377 }),
378 )
379 }
380
381 pub fn open_headless_id<F: Future<Output = WindowRoot> + Send + 'static>(
387 &self,
388 window_id: impl Into<WindowId>,
389 new_window: impl IntoFuture<IntoFuture = F>,
390 with_renderer: bool,
391 ) -> ResponseVar<WindowId> {
392 let window_id = window_id.into();
393 self.assert_id_unused(window_id);
394 WINDOWS_SV.write().open_impl(
395 window_id,
396 UiTask::new(None, new_window),
397 Some(if with_renderer {
398 WindowMode::HeadlessWithRenderer
399 } else {
400 WindowMode::Headless
401 }),
402 )
403 }
404
405 #[track_caller]
406 fn assert_id_unused(&self, id: WindowId) {
407 let w = WINDOWS_SV.read();
408 if w.windows_info.contains_key(&id) || w.open_loading.contains_key(&id) {
409 panic!("window id `{id:?}` is already in use")
410 }
411 }
412
413 pub fn loading_handle(&self, window_id: impl Into<WindowId>, deadline: impl Into<Deadline>) -> Option<WindowLoadingHandle> {
424 WINDOWS_SV.write().loading_handle_impl(window_id.into(), deadline.into())
425 }
426
427 pub fn close(&self, window_id: impl Into<WindowId>) -> Result<ResponseVar<CloseWindowResult>, WindowNotFoundError> {
434 self.close_together([window_id.into()])
435 }
436
437 pub fn close_together(
448 &self,
449 windows: impl IntoIterator<Item = WindowId>,
450 ) -> Result<ResponseVar<CloseWindowResult>, WindowNotFoundError> {
451 WINDOWS_SV.write().close_together(windows)
452 }
453
454 pub fn close_all(&self) -> ResponseVar<CloseWindowResult> {
462 let set: Vec<_> = WINDOWS_SV.read().windows_info.keys().copied().collect();
463 self.close_together(set).unwrap()
464 }
465
466 pub fn mode(&self, window_id: impl Into<WindowId>) -> Result<WindowMode, WindowNotFoundError> {
474 let window_id = window_id.into();
475 WINDOWS_SV
476 .read()
477 .windows_info
478 .get(&window_id)
479 .map(|w| w.mode)
480 .ok_or(WindowNotFoundError::new(window_id))
481 }
482
483 pub fn widget_tree(&self, window_id: impl Into<WindowId>) -> Result<WidgetInfoTree, WindowNotFoundError> {
487 let window_id = window_id.into();
488 WINDOWS_SV
489 .read()
490 .windows_info
491 .get(&window_id)
492 .map(|w| w.widget_tree.clone())
493 .ok_or(WindowNotFoundError::new(window_id))
494 }
495
496 pub fn widget_info(&self, widget_id: impl Into<WidgetId>) -> Option<WidgetInfo> {
498 let widget_id = widget_id.into();
499 WINDOWS_SV.read().windows_info.values().find_map(|w| w.widget_tree.get(widget_id))
500 }
501
502 #[cfg(feature = "image")]
510 pub fn frame_image(&self, window_id: impl Into<WindowId>, mask: Option<ImageMaskMode>) -> ImageVar {
511 let window_id = window_id.into();
512 if let Some((win, wgt)) = self.nest_parent(window_id) {
513 if let Ok(tree) = self.widget_tree(win)
514 && let Some(wgt) = tree.get(wgt)
515 {
516 return WINDOWS_SV
517 .write()
518 .frame_image_impl(win, |vr| vr.frame_image_rect(wgt.inner_bounds(), mask));
519 }
520 tracing::error!("did not find nest parent {win:?}//.../{wgt:?}, will capture parent window frame")
521 }
522 WINDOWS_SV.write().frame_image_impl(window_id, move |vr| vr.frame_image(mask))
523 }
524
525 #[cfg(feature = "image")]
533 pub fn frame_image_rect(&self, window_id: impl Into<WindowId>, mut rect: PxRect, mask: Option<ImageMaskMode>) -> ImageVar {
534 let mut window_id = window_id.into();
535 if let Some((win, wgt)) = self.nest_parent(window_id) {
536 if let Ok(tree) = self.widget_tree(win)
537 && let Some(wgt) = tree.get(wgt)
538 {
539 window_id = win;
540 let bounds = wgt.inner_bounds();
541 rect.origin += bounds.origin.to_vector();
542 rect = rect.intersection(&bounds).unwrap_or_default();
543 }
544 if window_id != win {
545 tracing::error!("did not find nest parent {win:?}//.../{wgt:?}, will capture parent window frame")
546 }
547 }
548 WINDOWS_SV.write().frame_image_impl(window_id, |vr| vr.frame_image_rect(rect, mask))
549 }
550
551 pub fn vars(&self, window_id: impl Into<WindowId>) -> Result<WindowVars, WindowNotFoundError> {
555 let window_id = window_id.into();
556 WINDOWS_SV
557 .read()
558 .windows_info
559 .get(&window_id)
560 .map(|w| w.vars.clone())
561 .ok_or(WindowNotFoundError::new(window_id))
562 }
563
564 pub fn is_focused(&self, window_id: impl Into<WindowId>) -> Result<bool, WindowNotFoundError> {
569 let window_id = window_id.into();
570 let w = WINDOWS_SV.read();
571 if let Some(w) = w.windows_info.get(&window_id) {
572 Ok(w.is_focused)
573 } else if w.open_loading.contains_key(&window_id) {
574 Ok(false)
575 } else {
576 Err(WindowNotFoundError::new(window_id))
577 }
578 }
579
580 pub fn widget_trees(&self) -> Vec<WidgetInfoTree> {
582 WINDOWS_SV.read().windows_info.values().map(|w| w.widget_tree.clone()).collect()
583 }
584
585 pub fn focused_window_id(&self) -> Option<WindowId> {
587 WINDOWS_SV.read().windows_info.values().find(|w| w.is_focused).map(|w| w.id)
588 }
589
590 pub fn focused_info(&self) -> Option<WidgetInfoTree> {
592 WINDOWS_SV
593 .read()
594 .windows_info
595 .values()
596 .find(|w| w.is_focused)
597 .map(|w| w.widget_tree.clone())
598 }
599
600 pub fn is_open(&self, window_id: impl Into<WindowId>) -> bool {
607 WINDOWS_SV.read().windows_info.contains_key(&window_id.into())
608 }
609
610 pub fn is_opening(&self, window_id: impl Into<WindowId>) -> bool {
614 let window_id = window_id.into();
615 let sv = WINDOWS_SV.read();
616 sv.open_loading.contains_key(&window_id)
617 }
618
619 pub fn is_loading(&self, window_id: impl Into<WindowId>) -> bool {
621 let window_id = window_id.into();
622 let sv = WINDOWS_SV.read();
623 sv.open_loading.contains_key(&window_id) || sv.windows_info.get(&window_id).map(|i| !i.is_loaded).unwrap_or(false)
624 }
625
626 pub fn is_loaded(&self, window_id: impl Into<WindowId>) -> bool {
632 let window_id = window_id.into();
633 WINDOWS_SV.read().windows_info.get(&window_id).map(|i| i.is_loaded).unwrap_or(false)
634 }
635
636 pub fn wait_loaded(&self, window_id: impl Into<WindowId>, wait_event: bool) -> impl Future<Output = bool> + Send + Sync + 'static {
642 Self::wait_loaded_impl(window_id.into(), wait_event)
643 }
644 async fn wait_loaded_impl(window_id: WindowId, wait_event: bool) -> bool {
645 if Self.is_loaded(window_id) {
646 if wait_event {
647 zng_task::yield_now().await;
649 }
650 return true;
651 }
652
653 let recv = WINDOW_LOAD_EVENT.receiver();
655 while Self.is_loading(window_id) {
656 while let Ok(msg) = zng_task::with_deadline(recv.recv_async(), 1.secs()).await {
657 if let Ok(args) = msg
658 && args.window_id == window_id
659 {
660 if wait_event {
661 zng_task::yield_now().await;
662 }
663 return true;
664 }
665 }
666 }
668
669 if Self.is_loaded(window_id) {
670 if wait_event {
671 zng_task::yield_now().await;
672 }
673 return true;
674 }
675 false
676 }
677
678 pub fn focus(&self, window_id: impl Into<WindowId>) -> Result<(), WindowNotFoundError> {
688 let window_id = window_id.into();
689 if !self.is_focused(window_id)? {
690 let mut w = WINDOWS_SV.write();
691 w.focus_request = Some(window_id);
692 UPDATES.update(None);
693 }
694 Ok(())
695 }
696
697 pub fn focus_or_open(
699 &self,
700 window_id: impl Into<WindowId>,
701 open: impl Future<Output = WindowRoot> + Send + 'static,
702 ) -> Option<ResponseVar<WindowId>> {
703 let window_id = window_id.into();
704 if self.focus(window_id).is_ok() {
705 None
706 } else {
707 let r = self.open_id(window_id, async move {
708 let w = open.await;
709 WINDOWS.focus(WINDOW.id()).unwrap();
711 w
712 });
713 Some(r)
714 }
715 }
716
717 pub fn bring_to_top(&self, window_id: impl Into<WindowId>) -> Result<(), WindowNotFoundError> {
724 let window_id = window_id.into();
725 let mut w = WINDOWS_SV.write();
726 if w.windows_info.contains_key(&window_id) {
727 w.bring_to_top_requests.push(window_id);
728 UPDATES.update(None);
729 Ok(())
730 } else {
731 Err(WindowNotFoundError::new(window_id))
732 }
733 }
734
735 pub fn register_root_extender(&self, extender: impl FnMut(WindowRootExtenderArgs) -> UiNode + Send + 'static) {
751 WINDOWS_SV.write().root_extenders.get_mut().push(Box::new(extender))
752 }
753
754 pub fn system_chrome(&self) -> Var<ChromeConfig> {
762 WINDOWS_SV.read().system_chrome.read_only()
763 }
764
765 pub fn register_open_nested_handler(&self, handler: impl FnMut(&mut crate::OpenNestedHandlerArgs) + Send + 'static) {
780 WINDOWS_SV.write().open_nested_handlers.get_mut().push(Box::new(handler))
781 }
782
783 pub fn nest_parent(&self, maybe_nested: impl Into<WindowId>) -> Option<(WindowId, WidgetId)> {
785 let vars = self.vars(maybe_nested.into()).ok()?;
786 let nest = vars.nest_parent().get()?;
787 let parent = vars.parent().get()?;
788 Some((parent, nest))
789 }
790
791 pub fn view_extensions_init(
802 &self,
803 window_id: impl Into<WindowId>,
804 extension_id: ApiExtensionId,
805 request: ApiExtensionPayload,
806 ) -> Result<(), WindowNotFoundError> {
807 let window_id = window_id.into();
808 match WINDOWS_SV.write().windows_info.get_mut(&window_id) {
809 Some(i) => {
810 i.extensions.push((extension_id, request));
811 Ok(())
812 }
813 None => Err(WindowNotFoundError::new(window_id)),
814 }
815 }
816
817 pub(super) fn system_colors_config(&self) -> ColorsConfig {
818 WINDOWS_SV.read().latest_colors_cfg
819 }
820
821 pub(super) fn take_view_extensions_init(&self, id: WindowId) -> Vec<(ApiExtensionId, ApiExtensionPayload)> {
822 std::mem::take(&mut WINDOWS_SV.write().windows_info.get_mut(&id).unwrap().extensions)
823 }
824
825 pub fn view_window_extension_raw(
829 &self,
830 window_id: impl Into<WindowId>,
831 extension_id: ApiExtensionId,
832 request: ApiExtensionPayload,
833 ) -> Result<ApiExtensionPayload, ViewExtensionError> {
834 let window_id = window_id.into();
835 let sv = WINDOWS_SV.read();
836 match WINDOWS_SV.read().windows_info.get(&window_id) {
837 Some(i) => match &i.view {
838 Some(r) => match r {
839 ViewWindowOrHeadless::Window(r) => {
840 let r = r.clone();
841 drop(sv);
842 r.window_extension_raw(extension_id, request)
843 .map_err(|_| ViewExtensionError::Disconnected)
844 }
845 ViewWindowOrHeadless::Headless(_) => Err(ViewExtensionError::WindowNotHeaded(window_id)),
846 },
847 None => Err(ViewExtensionError::NotOpenInViewProcess(window_id)),
848 },
849 None => Err(ViewExtensionError::WindowNotFound(WindowNotFoundError::new(window_id))),
850 }
851 }
852
853 pub fn view_window_extension<I, O>(
857 &self,
858 window_id: impl Into<WindowId>,
859 extension_id: ApiExtensionId,
860 request: &I,
861 ) -> Result<O, ViewExtensionError>
862 where
863 I: serde::Serialize,
864 O: serde::de::DeserializeOwned,
865 {
866 let window_id = window_id.into();
867 let sv = WINDOWS_SV.read();
868 match sv.windows_info.get(&window_id) {
869 Some(i) => match &i.view {
870 Some(r) => match r {
871 ViewWindowOrHeadless::Window(r) => {
872 let r = r.clone();
873 drop(sv);
874 let r = r
875 .window_extension(extension_id, request)
876 .map_err(|_| ViewExtensionError::Disconnected)?;
877 r.map_err(ViewExtensionError::Api)
878 }
879 ViewWindowOrHeadless::Headless(_) => Err(ViewExtensionError::WindowNotHeaded(window_id)),
880 },
881 None => Err(ViewExtensionError::NotOpenInViewProcess(window_id)),
882 },
883 None => Err(ViewExtensionError::WindowNotFound(WindowNotFoundError::new(window_id))),
884 }
885 }
886
887 pub fn view_render_extension_raw(
891 &self,
892 window_id: impl Into<WindowId>,
893 extension_id: ApiExtensionId,
894 request: ApiExtensionPayload,
895 ) -> Result<ApiExtensionPayload, ViewExtensionError> {
896 let window_id = window_id.into();
897 let sv = WINDOWS_SV.read();
898 match WINDOWS_SV.read().windows_info.get(&window_id) {
899 Some(i) => match &i.view {
900 Some(r) => {
901 let r = r.renderer();
902 drop(sv);
903 r.render_extension_raw(extension_id, request)
904 .map_err(|_| ViewExtensionError::Disconnected)
905 }
906 None => Err(ViewExtensionError::NotOpenInViewProcess(window_id)),
907 },
908 None => Err(ViewExtensionError::WindowNotFound(WindowNotFoundError::new(window_id))),
909 }
910 }
911
912 pub fn view_render_extension<I, O>(
916 &self,
917 window_id: impl Into<WindowId>,
918 extension_id: ApiExtensionId,
919 request: &I,
920 ) -> Result<O, ViewExtensionError>
921 where
922 I: serde::Serialize,
923 O: serde::de::DeserializeOwned,
924 {
925 let window_id = window_id.into();
926 let sv = WINDOWS_SV.read();
927 match sv.windows_info.get(&window_id) {
928 Some(i) => match &i.view {
929 Some(r) => {
930 let r = r.renderer();
931 drop(sv);
932 let r = r
933 .render_extension(extension_id, request)
934 .map_err(|_| ViewExtensionError::Disconnected)?;
935 r.map_err(ViewExtensionError::Api)
936 }
937 None => Err(ViewExtensionError::NotOpenInViewProcess(window_id)),
938 },
939 None => Err(ViewExtensionError::WindowNotFound(WindowNotFoundError::new(window_id))),
940 }
941 }
942
943 pub(super) fn set_view(&self, id: WindowId, view: ViewWindowOrHeadless) {
945 if let Some(info) = WINDOWS_SV.write().windows_info.get_mut(&id) {
946 info.view = Some(view);
947 }
948 }
949
950 pub(super) fn set_widget_tree(&self, info_tree: WidgetInfoTree) {
952 if let Some(info) = WINDOWS_SV.write().windows_info.get_mut(&info_tree.window_id()) {
953 info.widget_tree = info_tree;
954 }
955 }
956
957 pub(super) fn try_load(&self, window_id: WindowId) -> bool {
961 if let Some(info) = WINDOWS_SV.write().windows_info.get_mut(&window_id) {
962 info.is_loaded = info.loading_handle.try_load(window_id);
963
964 if info.is_loaded && !info.vars.0.is_loaded.get() {
965 info.vars.0.is_loaded.set(true);
966 WINDOW_LOAD_EVENT.notify(WindowOpenArgs::now(info.id));
967 }
968
969 info.is_loaded
970 } else {
971 unreachable!()
972 }
973 }
974
975 pub(super) fn on_pre_event(update: &EventUpdate) {
976 if let Some(args) = RAW_WINDOW_FOCUS_EVENT.on(update) {
977 let mut wns = WINDOWS_SV.write();
978
979 let mut prev = None;
980 let mut new = None;
981
982 if let Some(prev_focus) = args.prev_focus
983 && let Some(window) = wns.windows_info.get_mut(&prev_focus)
984 {
985 if window.is_focused {
986 window.is_focused = false;
987 prev = Some(prev_focus);
988 } else if args.new_focus.is_none()
989 && let Some(focused) = wns.windows_info.values_mut().find(|w| w.is_focused)
990 && focused.vars.nest_parent().get().is_some()
991 && focused.vars.parent().get() == Some(prev_focus)
992 {
993 focused.is_focused = false;
995 prev = Some(focused.id);
996 }
997 }
998 if let Some(new_focus) = args.new_focus {
999 if prev.is_none()
1000 && let Some((&id, window)) = wns.windows_info.iter_mut().find(|w| w.1.is_focused)
1001 && new_focus != id
1002 {
1003 window.is_focused = false;
1004 prev = Some(id);
1005 }
1006
1007 if let Some(window) = wns.windows_info.get_mut(&new_focus)
1008 && !window.is_focused
1009 {
1010 window.is_focused = true;
1011 window.vars.focus_indicator().set(None);
1012 new = Some(new_focus);
1013 }
1014 }
1015
1016 if prev.is_some() || new.is_some() {
1017 let args = WindowFocusChangedArgs::new(args.timestamp, args.propagation().clone(), prev, new, false);
1018 WINDOW_FOCUS_CHANGED_EVENT.notify(args);
1019 }
1020 } else if let Some(args) = RAW_WINDOW_CLOSE_REQUESTED_EVENT.on(update) {
1021 let _ = WINDOWS.close(args.window_id);
1022 } else if let Some(args) = RAW_WINDOW_CLOSE_EVENT.on(update) {
1023 if WINDOWS_SV.read().windows.contains_key(&args.window_id) {
1024 tracing::error!("view-process closed window without request");
1025 let mut windows = IdSet::default();
1026 windows.insert(args.window_id);
1027 let args = WindowCloseArgs::new(args.timestamp, args.propagation().clone(), windows);
1028 WINDOW_CLOSE_EVENT.notify(args);
1029 }
1030 } else if let Some(args) = RAW_COLORS_CONFIG_CHANGED_EVENT.on(update) {
1031 WINDOWS_SV.write().latest_colors_cfg = args.config;
1032 } else if let Some(args) = RAW_CHROME_CONFIG_CHANGED_EVENT.on(update) {
1033 WINDOWS_SV.read().system_chrome.set(args.config);
1034 } else if VIEW_PROCESS_INITED_EVENT.has(update) {
1035 UPDATES.update(None);
1037 } else {
1038 #[cfg(feature = "image")]
1039 if let Some(args) = RAW_IMAGE_LOADED_EVENT.on(update).or_else(|| RAW_IMAGE_LOAD_ERROR_EVENT.on(update)) {
1040 let mut sv = WINDOWS_SV.write();
1042 sv.frame_images.retain(|i| {
1043 if let Some(i) = i.upgrade() {
1044 if Some(&args.image) == i.get().view() {
1045 i.update();
1046 false
1047 } else {
1048 true
1049 }
1050 } else {
1051 false
1052 }
1053 });
1054 }
1055 }
1056
1057 Self::with_detached_windows(|windows, parallel| {
1058 if windows.len() > 1 && parallel.contains(ParallelWin::EVENT) {
1059 windows.par_iter_mut().with_ctx().for_each(|(_, window)| {
1060 window.pre_event(update);
1061 });
1062 } else {
1063 for (_, window) in windows.iter_mut() {
1064 window.pre_event(update);
1065 }
1066 }
1067 })
1068 }
1069
1070 pub(super) fn on_ui_event(update: &mut EventUpdate) {
1071 if update.delivery_list_mut().has_pending_search() {
1072 update
1073 .delivery_list_mut()
1074 .fulfill_search(WINDOWS_SV.read().windows_info.values().map(|w| &w.widget_tree));
1075 }
1076 Self::with_detached_windows(|windows, parallel| {
1077 if windows.len() > 1 && parallel.contains(ParallelWin::EVENT) {
1078 windows.par_iter_mut().with_ctx().for_each(|(_, window)| {
1079 window.ui_event(update);
1080 });
1081 } else {
1082 for (_, window) in windows.iter_mut() {
1083 window.ui_event(update);
1084 }
1085 }
1086 });
1087 }
1088
1089 pub(super) fn on_event(update: &mut EventUpdate) {
1090 if let Some(args) = WINDOW_CLOSE_REQUESTED_EVENT.on(update) {
1091 let key = args.windows.iter().next().unwrap();
1092 let mut sv = WINDOWS_SV.write();
1093 if let Some(rsp) = sv.close_responders.remove(key) {
1094 if !args.propagation().is_stopped() {
1095 WINDOW_CLOSE_EVENT.notify(WindowCloseArgs::now(args.windows.clone()));
1097 for r in rsp {
1098 r.respond(CloseWindowResult::Closed);
1099 }
1100 } else {
1101 for r in rsp {
1102 r.respond(CloseWindowResult::Cancel);
1103 }
1104 sv.exit_on_close = false;
1106 }
1107 }
1108 } else if let Some(args) = WINDOW_CLOSE_EVENT.on(update) {
1109 for w in args.windows.iter() {
1113 let w = WINDOWS_SV.write().windows.remove(w);
1114 if let Some(w) = w {
1115 let id = w.ctx.id();
1116 w.close();
1117
1118 let info = WINDOWS_SV.write().windows_info.remove(&id).unwrap();
1119
1120 info.vars.0.is_open.set(false);
1121
1122 if info.is_focused {
1123 let args = WindowFocusChangedArgs::now(Some(info.id), None, true);
1124 WINDOW_FOCUS_CHANGED_EVENT.notify(args)
1125 }
1126 }
1127 }
1128
1129 let is_headless_app = zng_app::APP.window_mode().is_headless();
1130 let mut wns = WINDOWS_SV.write();
1131
1132 if mem::take(&mut wns.exit_on_close)
1137 || (wns.exit_on_last_close.get()
1138 && !is_headless_app
1139 && !wns.windows.values().any(|w| matches!(w.ctx.mode(), WindowMode::Headed))
1140 && !wns
1141 .open_requests
1142 .iter()
1143 .any(|w| matches!(w.force_headless, None | Some(WindowMode::Headed)))
1144 && !wns.open_tasks.iter().any(|t| matches!(t.mode, WindowMode::Headed)))
1145 {
1146 APP.exit();
1148 }
1149 } else if let Some(args) = EXIT_REQUESTED_EVENT.on(update)
1150 && !args.propagation().is_stopped()
1151 {
1152 let mut windows = WINDOWS_SV.write();
1153 if !windows.windows_info.is_empty() {
1154 args.propagation().stop();
1155 windows.exit_on_close = true;
1156 drop(windows);
1157 WINDOWS.close_all();
1158 }
1159 }
1160 }
1161
1162 pub(super) fn on_ui_update(update_widgets: &mut WidgetUpdates) {
1163 if update_widgets.delivery_list_mut().has_pending_search() {
1164 update_widgets
1165 .delivery_list_mut()
1166 .fulfill_search(WINDOWS_SV.read().windows_info.values().map(|w| &w.widget_tree));
1167 }
1168
1169 Self::with_detached_windows(|windows, parallel| {
1170 if windows.len() > 1 && parallel.contains(ParallelWin::UPDATE) {
1171 windows.par_iter_mut().with_ctx().for_each(|(_, window)| {
1172 window.update(update_widgets);
1173 });
1174 } else {
1175 for (_, window) in windows.iter_mut() {
1176 window.update(update_widgets);
1177 }
1178 }
1179 });
1180 }
1181
1182 pub(super) fn on_update() {
1183 Self::fulfill_requests();
1184 }
1185
1186 fn fulfill_requests() {
1187 if VIEW_PROCESS.is_available() && !VIEW_PROCESS.is_connected() {
1188 return;
1190 }
1191
1192 let ((open, mut open_tasks, close, focus, bring_to_top, view_tasks), colors_cfg) = {
1193 let mut wns = WINDOWS_SV.write();
1194 (wns.take_requests(), wns.latest_colors_cfg)
1195 };
1196
1197 let window_mode = zng_app::APP.window_mode();
1198
1199 for r in open {
1201 let window_mode = match (window_mode, r.force_headless) {
1202 (WindowMode::Headed | WindowMode::HeadlessWithRenderer, Some(mode)) => {
1203 debug_assert!(!matches!(mode, WindowMode::Headed));
1204 mode
1205 }
1206 (mode, _) => mode,
1207 };
1208
1209 let colors_cfg = match window_mode {
1210 WindowMode::Headed => colors_cfg,
1211 WindowMode::Headless | WindowMode::HeadlessWithRenderer => ColorsConfig::default(),
1212 };
1213
1214 let task = AppWindowTask::new(r.id, window_mode, colors_cfg, r.new.into_inner(), r.responder);
1215 open_tasks.push(task);
1216 }
1217
1218 let mut any_ready = false;
1220 for task in &mut open_tasks {
1221 let ready = task.update();
1222 any_ready |= ready;
1223 }
1224 if any_ready {
1225 for mut task in open_tasks {
1226 if task.is_ready() {
1227 let window_id = task.ctx.id();
1228
1229 let mut wns = WINDOWS_SV.write();
1230 let loading = wns.open_loading.remove(&window_id).unwrap();
1231 let mut root_extenders = mem::take(&mut wns.root_extenders);
1232 let mut open_nested_handlers = mem::take(&mut wns.open_nested_handlers);
1233 drop(wns);
1234 let (window, info, responder) =
1235 task.finish(loading, &mut root_extenders.get_mut()[..], &mut open_nested_handlers.get_mut()[..]);
1236
1237 let mut wns = WINDOWS_SV.write();
1238 root_extenders.get_mut().append(wns.root_extenders.get_mut());
1239 open_nested_handlers.get_mut().append(wns.open_nested_handlers.get_mut());
1240 wns.root_extenders = root_extenders;
1241 wns.open_nested_handlers = open_nested_handlers;
1242
1243 if wns.windows.insert(window_id, window).is_some() {
1244 unreachable!();
1246 }
1247 wns.windows_info.insert(info.id, info);
1248
1249 responder.respond(window_id);
1250 } else {
1253 let mut wns = WINDOWS_SV.write();
1254 wns.open_tasks.push(task);
1255 }
1256 }
1257 } else {
1258 let mut wns = WINDOWS_SV.write();
1259 debug_assert!(wns.open_tasks.is_empty());
1260 wns.open_tasks = open_tasks;
1261 }
1262
1263 {
1267 let mut wns = WINDOWS_SV.write();
1268 let wns = &mut *wns;
1269
1270 let mut close_wns = IdSet::default();
1271
1272 for r in close {
1273 for w in r.windows {
1274 if let Some(info) = wns.windows_info.get(&w)
1275 && close_wns.insert(w)
1276 {
1277 wns.close_responders
1278 .entry(w)
1279 .or_insert_with(Default::default)
1280 .push(r.responder.clone());
1281
1282 info.vars.0.children.with(|c| {
1283 for &c in c.iter() {
1284 if wns.windows_info.contains_key(&c) && close_wns.insert(c) {
1285 wns.close_responders
1286 .entry(c)
1287 .or_insert_with(Default::default)
1288 .push(r.responder.clone());
1289 }
1290 }
1291 });
1292 }
1293 }
1294 }
1295 if !close_wns.is_empty() {
1296 let args = WindowCloseRequestedArgs::now(close_wns);
1297 WINDOW_CLOSE_REQUESTED_EVENT.notify(args);
1298 }
1299 }
1300
1301 if let Some(w_id) = focus {
1303 Self::with_detached_windows(|windows, _| {
1304 if let Some(w) = windows.get_mut(&w_id) {
1305 w.focus();
1306 }
1307 });
1308 }
1309
1310 for w_id in bring_to_top {
1311 Self::with_detached_windows(|windows, _| {
1312 if let Some(w) = windows.get_mut(&w_id) {
1313 w.bring_to_top();
1314 }
1315 });
1316 }
1317
1318 for view_task in view_tasks {
1319 let task = view_task.task.into_inner();
1320 Self::with_detached_windows(|windows, _| {
1321 if let Some(w) = windows.get_mut(&view_task.window_id) {
1322 w.view_task(task);
1323 } else {
1324 task(None);
1325 }
1326 })
1327 }
1328 }
1329
1330 pub(super) fn on_info(info_widgets: &mut InfoUpdates) {
1331 if info_widgets.delivery_list_mut().has_pending_search() {
1332 info_widgets
1333 .delivery_list_mut()
1334 .fulfill_search(WINDOWS_SV.read().windows_info.values().map(|w| &w.widget_tree));
1335 }
1336
1337 let info_widgets_arc = Arc::new(mem::take(info_widgets));
1338
1339 Self::with_detached_windows(|windows, parallel| {
1340 if windows.len() > 1 && parallel.contains(ParallelWin::LAYOUT) {
1341 windows.par_iter_mut().with_ctx().for_each(|(_, window)| {
1342 window.info(info_widgets_arc.clone());
1343 })
1344 } else {
1345 for (_, window) in windows.iter_mut() {
1346 window.info(info_widgets_arc.clone());
1347 }
1348 }
1349 });
1350
1351 match Arc::try_unwrap(info_widgets_arc) {
1352 Ok(w) => {
1353 *info_widgets = w;
1354 }
1355 Err(_) => {
1356 tracing::error!("info_widgets not released by window")
1357 }
1358 }
1359 }
1360
1361 pub(super) fn on_layout(layout_widgets: &mut LayoutUpdates) {
1362 if layout_widgets.delivery_list_mut().has_pending_search() {
1363 layout_widgets
1364 .delivery_list_mut()
1365 .fulfill_search(WINDOWS_SV.read().windows_info.values().map(|w| &w.widget_tree));
1366 }
1367
1368 let layout_widgets_arc = Arc::new(mem::take(layout_widgets));
1369
1370 Self::with_detached_windows(|windows, parallel| {
1371 if windows.len() > 1 && parallel.contains(ParallelWin::LAYOUT) {
1372 windows.par_iter_mut().with_ctx().for_each(|(_, window)| {
1373 window.layout(layout_widgets_arc.clone());
1374 })
1375 } else {
1376 for (_, window) in windows.iter_mut() {
1377 window.layout(layout_widgets_arc.clone());
1378 }
1379 }
1380 });
1381
1382 match Arc::try_unwrap(layout_widgets_arc) {
1383 Ok(w) => {
1384 *layout_widgets = w;
1385 }
1386 Err(_) => {
1387 tracing::debug!("layout_widgets not released by window")
1389 }
1390 }
1391 }
1392
1393 pub(super) fn on_render(render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
1394 for list in [&mut *render_widgets, &mut *render_update_widgets] {
1395 if list.delivery_list_mut().has_pending_search() {
1396 list.delivery_list_mut()
1397 .fulfill_search(WINDOWS_SV.read().windows_info.values().map(|w| &w.widget_tree));
1398 }
1399 }
1400
1401 let render_widgets_arc = Arc::new(mem::take(render_widgets));
1402 let render_update_widgets_arc = Arc::new(mem::take(render_update_widgets));
1403
1404 Self::with_detached_windows(|windows, parallel| {
1405 if windows.len() > 1 && parallel.contains(ParallelWin::RENDER) {
1406 windows.par_iter_mut().with_ctx().for_each(|(_, window)| {
1407 window.render(render_widgets_arc.clone(), render_update_widgets_arc.clone());
1408 });
1409 } else {
1410 for (_, window) in windows.iter_mut() {
1411 window.render(render_widgets_arc.clone(), render_update_widgets_arc.clone());
1412 }
1413 }
1414 });
1415
1416 match Arc::try_unwrap(render_widgets_arc) {
1417 Ok(w) => {
1418 *render_widgets = w;
1419 }
1420 Err(_) => {
1421 tracing::debug!("render_widgets not released by window")
1423 }
1424 }
1425 match Arc::try_unwrap(render_update_widgets_arc) {
1426 Ok(w) => {
1427 *render_update_widgets = w;
1428 }
1429 Err(_) => {
1430 tracing::debug!("render_update_widgets not released by window")
1432 }
1433 }
1434 }
1435
1436 fn with_detached_windows(f: impl FnOnce(&mut IdMap<WindowId, AppWindow>, ParallelWin)) {
1441 let (mut windows, parallel) = {
1442 let mut w = WINDOWS_SV.write();
1443 (mem::take(&mut w.windows), w.parallel.get())
1444 };
1445 f(&mut windows, parallel);
1446 let mut wns = WINDOWS_SV.write();
1447 debug_assert!(wns.windows.is_empty());
1448 wns.windows = windows;
1449 }
1450}
1451
1452impl WINDOWS {
1454 pub fn native_message_dialog(
1461 &self,
1462 window_id: WindowId,
1463 dialog: zng_view_api::dialog::MsgDialog,
1464 ) -> ResponseVar<zng_view_api::dialog::MsgDialogResponse> {
1465 let (responder, rsp) = response_var();
1466 WINDOWS_SV.write().view_window_task(window_id, move |win| match win {
1467 Some(win) => {
1468 if let Err(e) = win.message_dialog(dialog, responder.clone()) {
1469 responder.respond(zng_view_api::dialog::MsgDialogResponse::Error(formatx!("{e}")))
1470 }
1471 }
1472 None => responder.respond(zng_view_api::dialog::MsgDialogResponse::Error(Txt::from_static(
1473 "native window not found",
1474 ))),
1475 });
1476 rsp
1477 }
1478
1479 pub fn native_file_dialog(
1486 &self,
1487 window_id: WindowId,
1488 dialog: zng_view_api::dialog::FileDialog,
1489 ) -> ResponseVar<zng_view_api::dialog::FileDialogResponse> {
1490 let (responder, rsp) = response_var();
1491 WINDOWS_SV.write().view_window_task(window_id, move |win| match win {
1492 Some(win) => {
1493 if let Err(e) = win.file_dialog(dialog, responder.clone()) {
1494 responder.respond(zng_view_api::dialog::FileDialogResponse::Error(formatx!("{e}")))
1495 }
1496 }
1497 None => responder.respond(zng_view_api::dialog::FileDialogResponse::Error(Txt::from_static(
1498 "native window not found",
1499 ))),
1500 });
1501 rsp
1502 }
1503}
1504
1505#[allow(non_camel_case_types)]
1507pub struct WINDOWS_DRAG_DROP;
1508impl WINDOWS_DRAG_DROP {
1509 pub fn start_drag_drop(
1511 &self,
1512 window_id: WindowId,
1513 data: Vec<DragDropData>,
1514 allowed_effects: DragDropEffect,
1515 ) -> Result<DragDropId, DragDropError> {
1516 match WINDOWS_SV.write().windows.get_mut(&window_id) {
1517 Some(w) => w.start_drag_drop(data, allowed_effects),
1518 None => Err(DragDropError::CannotStart(WindowNotFoundError::new(window_id).to_txt())),
1519 }
1520 }
1521
1522 pub fn drag_dropped(&self, window_id: WindowId, drop_id: DragDropId, applied: DragDropEffect) -> Result<(), WindowNotFoundError> {
1524 match WINDOWS_SV.write().windows.get_mut(&window_id) {
1525 Some(w) => {
1526 w.drag_dropped(drop_id, applied);
1527 Ok(())
1528 }
1529 None => Err(WindowNotFoundError::new(window_id)),
1530 }
1531 }
1532}
1533
1534struct AppWindowInfo {
1536 id: WindowId,
1537 mode: WindowMode,
1538 view: Option<ViewWindowOrHeadless>,
1539 extensions: Vec<(ApiExtensionId, ApiExtensionPayload)>,
1540 vars: WindowVars,
1541
1542 widget_tree: WidgetInfoTree,
1543 is_focused: bool,
1545
1546 loading_handle: WindowLoading,
1547 is_loaded: bool,
1548}
1549impl AppWindowInfo {
1550 pub fn new(id: WindowId, root_id: WidgetId, mode: WindowMode, vars: WindowVars, loading_handle: WindowLoading) -> Self {
1551 Self {
1552 id,
1553 mode,
1554 view: None,
1555 extensions: vec![],
1556 vars,
1557 widget_tree: WidgetInfoTree::wgt(id, root_id),
1558 is_focused: false,
1559 loading_handle,
1560 is_loaded: false,
1561 }
1562 }
1563}
1564struct OpenWindowRequest {
1565 id: WindowId,
1566 new: Mutex<UiTask<WindowRoot>>, force_headless: Option<WindowMode>,
1568 responder: ResponderVar<WindowId>,
1569}
1570struct CloseWindowRequest {
1571 responder: ResponderVar<CloseWindowResult>,
1572 windows: IdSet<WindowId>,
1573}
1574
1575struct AppWindowTask {
1576 ctx: WindowCtx,
1577 mode: WindowMode,
1578 task: Mutex<UiTask<WindowRoot>>, responder: ResponderVar<WindowId>,
1580}
1581impl AppWindowTask {
1582 fn new(id: WindowId, mode: WindowMode, colors_cfg: ColorsConfig, new: UiTask<WindowRoot>, responder: ResponderVar<WindowId>) -> Self {
1583 let primary_scale_factor = match mode {
1584 WindowMode::Headed => MONITORS
1585 .primary_monitor()
1586 .get()
1587 .map(|m| m.scale_factor().get())
1588 .unwrap_or_else(|| 1.fct()),
1589 WindowMode::Headless | WindowMode::HeadlessWithRenderer => 1.fct(),
1590 };
1591
1592 let mut ctx = WindowCtx::new(id, mode);
1593
1594 let vars = WindowVars::new(WINDOWS_SV.read().default_render_mode.get(), primary_scale_factor, colors_cfg);
1595 ctx.with_state(|s| s.borrow_mut().set(*WINDOW_VARS_ID, vars.clone()));
1596
1597 Self {
1598 ctx,
1599 mode,
1600 responder,
1601 task: Mutex::new(new),
1602 }
1603 }
1604
1605 fn is_ready(&mut self) -> bool {
1606 self.task.get_mut().is_ready()
1607 }
1608
1609 fn update(&mut self) -> bool {
1610 WINDOW.with_context(&mut self.ctx, || {
1611 self.task.get_mut().update();
1612 });
1613 self.task.get_mut().is_ready()
1614 }
1615
1616 fn finish(
1617 self,
1618 loading: WindowLoading,
1619 extenders: &mut [Box<dyn FnMut(WindowRootExtenderArgs) -> UiNode + Send>],
1620 open_nested_handlers: &mut [Box<dyn FnMut(&mut crate::OpenNestedHandlerArgs) + Send>],
1621 ) -> (AppWindow, AppWindowInfo, ResponderVar<WindowId>) {
1622 let mut window = self.task.into_inner().into_result().unwrap_or_else(|_| panic!());
1623 let mut ctx = self.ctx;
1624
1625 WINDOW.with_context(&mut ctx, || {
1626 for ext in extenders.iter_mut().rev() {
1627 let root = mem::replace(&mut window.child, UiNode::nil());
1628 window.child = ext(WindowRootExtenderArgs { root });
1629 }
1630 let child = mem::replace(&mut window.child, UiNode::nil());
1631 let vars = WINDOW.vars();
1632 let child = with_context_var(child, ACCENT_COLOR_VAR, vars.actual_accent_color());
1633 let child = with_context_var(child, COLOR_SCHEME_VAR, vars.actual_color_scheme());
1634 let child = with_context_var(child, PARALLEL_VAR, vars.parallel());
1635 window.child = child;
1636 });
1637
1638 let mode = self.mode;
1639 let id = ctx.id();
1640
1641 ctx.set_widget_tree(WidgetInfoTree::wgt(id, window.id));
1642
1643 let vars = ctx.with_state(|s| s.borrow().get_clone(*WINDOW_VARS_ID)).unwrap();
1644
1645 if window.kiosk {
1646 vars.chrome().set(false);
1647 vars.visible().set(true);
1648 if !vars.state().get().is_fullscreen() {
1649 vars.state().set(WindowState::Exclusive);
1650 }
1651 }
1652
1653 let commands = WindowCommands::new(id);
1654
1655 let root_id = window.id;
1656
1657 let mut args = crate::OpenNestedHandlerArgs::new(ctx, vars.clone(), commands, window);
1658 for h in open_nested_handlers.iter_mut().rev() {
1659 h(&mut args);
1660 if args.has_nested() {
1661 break;
1662 }
1663 }
1664
1665 let (ctx, ctrl) = match args.take_normal() {
1666 Ok((ctx, vars, commands, window)) => (ctx, WindowCtrl::new(&vars, commands, mode, window)),
1667 Err((ctx, node)) => (ctx, WindowCtrl::new_nested(node)),
1668 };
1669
1670 let window = AppWindow {
1671 ctrl: Mutex::new(ctrl),
1672 ctx,
1673 };
1674 let info = AppWindowInfo::new(id, root_id, mode, vars, loading);
1675
1676 (window, info, self.responder)
1677 }
1678}
1679
1680struct ViewWindowTask {
1681 window_id: WindowId,
1682 task: Mutex<Box<dyn FnOnce(Option<&view_process::ViewWindow>) + Send>>, }
1684
1685struct AppWindow {
1687 ctrl: Mutex<WindowCtrl>, ctx: WindowCtx,
1689}
1690impl AppWindow {
1691 fn ctrl_in_ctx<R>(&mut self, action: impl FnOnce(&mut WindowCtrl) -> R) -> R {
1692 WINDOW.with_context(&mut self.ctx, || action(self.ctrl.get_mut()))
1693 }
1694
1695 pub fn pre_event(&mut self, update: &EventUpdate) {
1696 self.ctrl_in_ctx(|ctrl| ctrl.pre_event(update))
1697 }
1698
1699 pub fn ui_event(&mut self, update: &EventUpdate) {
1700 self.ctrl_in_ctx(|ctrl| ctrl.ui_event(update))
1701 }
1702
1703 pub fn update(&mut self, update_widgets: &WidgetUpdates) {
1704 self.ctrl_in_ctx(|ctrl| ctrl.update(update_widgets));
1705 }
1706
1707 pub fn info(&mut self, info_widgets: Arc<InfoUpdates>) {
1708 let info_update = self.ctrl_in_ctx(|ctrl| ctrl.info(info_widgets));
1709 if let Some(new) = info_update {
1710 self.ctx.set_widget_tree(new);
1711 }
1712 }
1713
1714 pub fn layout(&mut self, layout_widgets: Arc<LayoutUpdates>) {
1715 self.ctrl_in_ctx(|ctrl| ctrl.layout(layout_widgets));
1716 }
1717
1718 pub fn render(&mut self, render_widgets: Arc<RenderUpdates>, render_update_widgets: Arc<RenderUpdates>) {
1719 self.ctrl_in_ctx(|ctrl| ctrl.render(render_widgets, render_update_widgets));
1720 }
1721
1722 pub fn focus(&mut self) {
1723 self.ctrl_in_ctx(|ctrl| ctrl.focus());
1724 }
1725
1726 pub fn start_drag_drop(&mut self, data: Vec<DragDropData>, allowed_effects: DragDropEffect) -> Result<DragDropId, DragDropError> {
1727 self.ctrl_in_ctx(|ctx| ctx.start_drag_drop(data, allowed_effects))
1728 }
1729
1730 pub fn drag_dropped(&mut self, drop_id: DragDropId, applied: DragDropEffect) {
1731 self.ctrl_in_ctx(|ctx| ctx.drag_dropped(drop_id, applied))
1732 }
1733
1734 pub fn bring_to_top(&mut self) {
1735 self.ctrl_in_ctx(|ctrl| ctrl.bring_to_top());
1736 }
1737
1738 pub fn close(mut self) {
1739 self.ctrl_in_ctx(|ctrl| ctrl.close());
1740 }
1741
1742 fn view_task(&mut self, task: Box<dyn FnOnce(Option<&view_process::ViewWindow>) + Send>) {
1743 self.ctrl_in_ctx(|ctrl| ctrl.view_task(task));
1744 }
1745}
1746
1747struct WindowLoading {
1748 handles: Vec<std::sync::Weak<crate::WindowLoadingHandleData>>,
1749 timer: Option<DeadlineHandle>,
1750}
1751impl WindowLoading {
1752 pub fn new() -> Self {
1753 WindowLoading {
1754 handles: vec![],
1755 timer: None,
1756 }
1757 }
1758
1759 pub fn try_load(&mut self, window_id: WindowId) -> bool {
1761 let mut deadline = Deadline::MAX;
1762 self.handles.retain(|h| match h.upgrade() {
1763 Some(h) => {
1764 if h.deadline.has_elapsed() {
1765 false
1766 } else {
1767 deadline = deadline.min(h.deadline);
1768 true
1769 }
1770 }
1771 None => false,
1772 });
1773
1774 if self.handles.is_empty() {
1775 self.timer = None;
1776 true
1777 } else {
1778 if let Some(t) = &self.timer
1779 && t.deadline() != deadline
1780 {
1781 self.timer = None;
1782 }
1783 if self.timer.is_none() {
1784 let t = TIMERS.on_deadline(
1785 deadline,
1786 hn_once!(|_| {
1787 UPDATES.update_window(window_id).layout_window(window_id).render_window(window_id);
1788 }),
1789 );
1790 self.timer = Some(t);
1791 }
1792
1793 false
1794 }
1795 }
1796
1797 pub fn new_handle(&mut self, update: AppEventSender, deadline: Deadline) -> WindowLoadingHandle {
1798 let h = Arc::new(crate::WindowLoadingHandleData { update, deadline });
1799 self.handles.push(Arc::downgrade(&h));
1800 WindowLoadingHandle(h)
1801 }
1802}
1803
1804#[expect(non_camel_case_types)]
1808pub trait WINDOW_Ext {
1809 fn vars(&self) -> super::WindowVars {
1811 WindowVars::req()
1812 }
1813
1814 fn is_open(&self) -> bool {
1820 WINDOWS.is_open(WINDOW.id())
1821 }
1822
1823 fn is_loaded(&self) -> bool {
1827 WINDOWS.is_loaded(WINDOW.id())
1828 }
1829
1830 fn enable_access(&self) {
1834 let vars = WINDOW.vars();
1835 let access_enabled = &vars.0.access_enabled;
1836 if access_enabled.get().is_disabled() {
1837 access_enabled.modify(|e| **e |= zng_app::widget::info::access::AccessEnabled::APP);
1838 }
1839 }
1840
1841 fn loading_handle(&self, deadline: impl Into<Deadline>) -> Option<WindowLoadingHandle> {
1852 WINDOWS.loading_handle(WINDOW.id(), deadline)
1853 }
1854
1855 #[cfg(feature = "image")]
1859 fn frame_image(&self, mask: Option<ImageMaskMode>) -> ImageVar {
1860 WINDOWS.frame_image(WINDOW.id(), mask)
1861 }
1862
1863 #[cfg(feature = "image")]
1869 fn frame_image_rect(&self, rect: PxRect, mask: Option<ImageMaskMode>) -> ImageVar {
1870 WINDOWS.frame_image_rect(WINDOW.id(), rect, mask)
1871 }
1872
1873 fn bring_to_top(&self) {
1879 WINDOWS.bring_to_top(WINDOW.id()).ok();
1880 }
1881
1882 fn close(&self) -> ResponseVar<CloseWindowResult> {
1891 WINDOWS.close(WINDOW.id()).unwrap()
1892 }
1893}
1894impl WINDOW_Ext for WINDOW {}
1895
1896#[non_exhaustive]
1900pub struct WindowRootExtenderArgs {
1901 pub root: UiNode,
1904}
1905#[cfg(feature = "image")]
1906impl ImageRenderWindowRoot for WindowRoot {
1907 fn into_any(self: Box<Self>) -> Box<dyn Any> {
1908 self
1909 }
1910}
1911
1912#[doc(hidden)]
1913#[cfg(feature = "image")]
1914impl ImageRenderWindowsService for WINDOWS {
1915 fn new_window_root(&self, node: UiNode, render_mode: RenderMode, scale_factor: Option<Factor>) -> Box<dyn ImageRenderWindowRoot> {
1916 Box::new(WindowRoot::new_container(
1917 WidgetId::new_unique(),
1918 StartPosition::Default,
1919 false,
1920 true,
1921 Some(render_mode),
1922 scale_factor.map(HeadlessMonitor::new_scale).unwrap_or_default(),
1923 false,
1924 node,
1925 ))
1926 }
1927
1928 fn enable_frame_capture_in_window_context(&self, mask: Option<ImageMaskMode>) {
1929 let mode = if let Some(mask) = mask {
1930 FrameCaptureMode::AllMask(mask)
1931 } else {
1932 FrameCaptureMode::All
1933 };
1934 WINDOW.vars().frame_capture_mode().set(mode);
1935 }
1936
1937 fn set_parent_in_window_context(&self, parent_id: WindowId) {
1938 let vars = WINDOW.vars();
1939 vars.parent().set(parent_id);
1940 }
1941
1942 fn open_headless_window(&self, new_window_root: Box<dyn FnOnce() -> Box<dyn ImageRenderWindowRoot> + Send>) {
1943 WINDOWS.open_headless(
1944 async move {
1945 let w = *new_window_root()
1946 .into_any()
1947 .downcast::<WindowRoot>()
1948 .expect("expected `WindowRoot` in image render window");
1949 let vars = WINDOW.vars();
1950 vars.auto_size().set(true);
1951 vars.min_size().set((1.px(), 1.px()));
1952 w
1953 },
1954 true,
1955 );
1956 }
1957
1958 fn on_frame_image_ready(&self, update: &EventUpdate) -> Option<(WindowId, Img)> {
1959 if let Some(args) = FRAME_IMAGE_READY_EVENT.on(update)
1960 && let Some(img) = &args.frame_image
1961 {
1962 return Some((args.window_id, img.clone()));
1963 }
1964 None
1965 }
1966
1967 fn close_window(&self, window_id: WindowId) {
1968 let _ = WINDOWS.close(window_id);
1969 }
1970
1971 fn clone_boxed(&self) -> Box<dyn ImageRenderWindowsService> {
1972 Box::new(WINDOWS)
1973 }
1974}
1975
1976#[expect(non_camel_case_types)]
1978pub struct WINDOW_FOCUS;
1979impl WINDOW_FOCUS {
1980 pub fn hook_focus_service(&self, focused: Var<Option<InteractionPath>>) {
1984 *FOCUS_SV.write() = focused;
1985 }
1986
1987 pub(crate) fn focused(&self) -> Var<Option<InteractionPath>> {
1988 FOCUS_SV.get()
1989 }
1990}