1use zng_app::{
2 AppBuilder, AppControlFlow, HeadlessApp,
3 view_process::raw_events::{RAW_WINDOW_FOCUS_EVENT, RawWindowFocusArgs},
4 window::WindowId,
5};
6
7use crate::{CloseWindowResult, WINDOWS, WindowInstanceState, WindowRoot, WindowVars};
8
9pub trait AppRunWindowExt {
13 fn run_window<F>(self, window_id: impl Into<WindowId>, new_window: impl IntoFuture<IntoFuture = F>)
60 where
61 F: Future<Output = WindowRoot> + Send + 'static;
62}
63impl AppRunWindowExt for AppBuilder {
64 fn run_window<F>(self, window_id: impl Into<WindowId>, new_window: impl IntoFuture<IntoFuture = F>)
65 where
66 F: Future<Output = WindowRoot> + Send + 'static,
67 {
68 let window_id = window_id.into();
69 let new_window = new_window.into_future();
70 self.run(async move {
71 WINDOWS.open(window_id, new_window);
72 })
73 }
74}
75
76pub trait HeadlessAppWindowExt {
81 fn open_window<F>(&mut self, window_id: impl Into<WindowId>, new_window: impl IntoFuture<IntoFuture = F>) -> WindowVars
90 where
91 F: Future<Output = WindowRoot> + Send + 'static;
92
93 fn focus_window(&mut self, window_id: impl Into<WindowId>);
95 fn blur_window(&mut self, window_id: impl Into<WindowId>);
97
98 #[cfg(feature = "image")]
102 fn window_frame_image(
103 &mut self,
104 window_id: impl Into<WindowId>,
105 mask: Option<zng_view_api::image::ImageMaskMode>,
106 ) -> zng_ext_image::ImageVar;
107
108 fn close_window(&mut self, window_id: impl Into<WindowId>) -> bool;
112
113 fn run_window<F>(&mut self, window_id: impl Into<WindowId>, new_window: impl IntoFuture<IntoFuture = F>)
115 where
116 F: Send + Future<Output = WindowRoot> + 'static;
117
118 #[cfg(any(test, doc, feature = "test_util"))]
120 fn doc_test_window<F>(&mut self, new_window: impl IntoFuture<IntoFuture = F>)
121 where
122 F: Future<Output = WindowRoot> + 'static + Send;
123}
124impl HeadlessAppWindowExt for HeadlessApp {
125 fn open_window<F>(&mut self, window_id: impl Into<WindowId>, new_window: impl IntoFuture<IntoFuture = F>) -> WindowVars
126 where
127 F: Future<Output = WindowRoot> + Send + 'static,
128 {
129 let window_id = window_id.into();
130 let response = WINDOWS.open(window_id, new_window);
131 self.run_task(async move {
132 let vars = response.wait_rsp().await;
133 if let Some(mode) = WINDOWS.mode(window_id) {
134 if mode.has_renderer() {
135 vars.instance_state()
136 .wait_match(|s| matches!(s, WindowInstanceState::Loaded { has_view: true } | WindowInstanceState::Closed))
137 .await;
138 } else {
139 vars.instance_state()
140 .wait_match(|s| matches!(s, WindowInstanceState::Loaded { has_view: false } | WindowInstanceState::Closed))
141 .await;
142 }
143 }
144 vars
145 })
146 .unwrap()
147 }
148
149 fn focus_window(&mut self, window_id: impl Into<WindowId>) {
150 let args = RawWindowFocusArgs::now(None, Some(window_id.into()));
151 RAW_WINDOW_FOCUS_EVENT.notify(args);
152 let _ = self.update(false);
153 }
154
155 fn blur_window(&mut self, window_id: impl Into<WindowId>) {
156 let args = RawWindowFocusArgs::now(Some(window_id.into()), None);
157 RAW_WINDOW_FOCUS_EVENT.notify(args);
158 let _ = self.update(false);
159 }
160
161 #[cfg(feature = "image")]
162 fn window_frame_image(
163 &mut self,
164 window_id: impl Into<WindowId>,
165 mask: Option<zng_view_api::image::ImageMaskMode>,
166 ) -> zng_ext_image::ImageVar {
167 WINDOWS.frame_image(window_id, mask)
168 }
169
170 fn close_window(&mut self, window_id: impl Into<WindowId>) -> bool {
171 let r = WINDOWS.close(window_id);
172 let r = self.run_task(async move { r.wait_rsp().await });
173 r.is_none() || matches!(r.unwrap(), CloseWindowResult::Closed)
174 }
175
176 fn run_window<F>(&mut self, window_id: impl Into<WindowId>, new_window: impl IntoFuture<IntoFuture = F>)
177 where
178 F: Future<Output = WindowRoot> + Send + 'static,
179 {
180 let state = self.open_window(window_id, new_window).instance_state();
181 while !matches!(state.get(), WindowInstanceState::Closed) {
182 if let AppControlFlow::Exit = self.update(true) {
183 return;
184 }
185 }
186 }
187
188 #[cfg(any(test, doc, feature = "test_util"))]
189 fn doc_test_window<F>(&mut self, new_window: impl IntoFuture<IntoFuture = F>)
190 where
191 F: Future<Output = WindowRoot> + Send + 'static,
192 {
193 use zng_layout::unit::TimeUnits;
194
195 let timer = zng_app::timer::TIMERS.deadline(60.secs());
196
197 zng_task::spawn(async {
198 zng_task::deadline(65.secs()).await;
199 eprintln!("doc_test_window reached 65s fallback deadline");
200 zng_env::exit(-1);
201 });
202 let state = self.open_window(WindowId::new_unique(), new_window).instance_state();
203
204 while !matches!(state.get(), WindowInstanceState::Closed) {
205 if let AppControlFlow::Exit = self.update(true) {
206 return;
207 }
208 if timer.get().has_elapsed() {
209 panic!("doc_test_window reached 60s deadline");
210 }
211 }
212 }
213}