1use std::{borrow::Cow, fmt, sync::Arc};
4
5use crate::{update::UpdatesTrace, widget::info::WidgetInfoTree};
6use parking_lot::RwLock;
7use zng_app_context::context_local;
8use zng_state_map::{OwnedStateMap, StateId, StateMapMut, StateMapRef, StateValue};
9use zng_txt::Txt;
10
11zng_unique_id::unique_id_32! {
12 pub struct WindowId;
24}
25zng_unique_id::impl_unique_id_name!(WindowId);
26zng_unique_id::impl_unique_id_fmt!(WindowId);
27zng_unique_id::impl_unique_id_bytemuck!(WindowId);
28
29zng_var::impl_from_and_into_var! {
30 fn from(name: &'static str) -> WindowId {
32 WindowId::named(name)
33 }
34 fn from(name: String) -> WindowId {
36 WindowId::named(name)
37 }
38 fn from(name: Cow<'static, str>) -> WindowId {
40 WindowId::named(name)
41 }
42 fn from(name: char) -> WindowId {
44 WindowId::named(name)
45 }
46 fn from(name: Txt) -> WindowId {
48 WindowId::named(name)
49 }
50
51 fn from(some: WindowId) -> Option<WindowId>;
52}
53impl serde::Serialize for WindowId {
54 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
55 where
56 S: serde::Serializer,
57 {
58 let name = self.name();
59 if name.is_empty() {
60 use serde::ser::Error;
61 return Err(S::Error::custom("cannot serialize unnamed `WindowId`"));
62 }
63 name.serialize(serializer)
64 }
65}
66impl<'de> serde::Deserialize<'de> for WindowId {
67 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
68 where
69 D: serde::Deserializer<'de>,
70 {
71 let name = Txt::deserialize(deserializer)?;
72 Ok(WindowId::named(name))
73 }
74}
75
76zng_unique_id::unique_id_32! {
77 pub struct MonitorId;
79}
80zng_unique_id::impl_unique_id_bytemuck!(MonitorId);
81impl fmt::Debug for MonitorId {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 if f.alternate() {
84 f.debug_struct("MonitorId")
85 .field("id", &self.get())
86 .field("sequential", &self.sequential())
87 .finish()
88 } else {
89 write!(f, "MonitorId({})", self.sequential())
90 }
91 }
92}
93impl fmt::Display for MonitorId {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 write!(f, "MonitorId({})", self.sequential())
96 }
97}
98impl MonitorId {
99 pub fn fallback() -> MonitorId {
101 static FALLBACK: once_cell::sync::Lazy<MonitorId> = once_cell::sync::Lazy::new(MonitorId::new_unique);
102 *FALLBACK
103 }
104}
105
106pub struct WINDOW;
115impl WINDOW {
116 pub fn is_in_window(&self) -> bool {
118 !WINDOW_CTX.is_default()
119 }
120
121 pub fn try_id(&self) -> Option<WindowId> {
123 if WINDOW_CTX.is_default() { None } else { Some(WINDOW_CTX.get().id) }
124 }
125
126 pub fn id(&self) -> WindowId {
128 WINDOW_CTX.get().id
129 }
130
131 pub fn mode(&self) -> WindowMode {
133 WINDOW_CTX.get().mode
134 }
135
136 pub fn info(&self) -> WidgetInfoTree {
140 WINDOW_CTX.get().widget_tree.read().clone().expect("window not init")
141 }
142
143 pub fn with_state<R>(&self, f: impl FnOnce(StateMapRef<WINDOW>) -> R) -> R {
145 f(WINDOW_CTX.get().state.read().borrow())
146 }
147
148 pub fn with_state_mut<R>(&self, f: impl FnOnce(StateMapMut<WINDOW>) -> R) -> R {
150 f(WINDOW_CTX.get().state.write().borrow_mut())
151 }
152
153 pub fn get_state<T: StateValue + Clone>(&self, id: impl Into<StateId<T>>) -> Option<T> {
155 let id = id.into();
156 self.with_state(|s| s.get_clone(id))
157 }
158
159 pub fn req_state<T: StateValue + Clone>(&self, id: impl Into<StateId<T>>) -> T {
163 let id = id.into();
164 self.with_state(|s| s.req(id).clone())
165 }
166
167 pub fn set_state<T: StateValue>(&self, id: impl Into<StateId<T>>, value: impl Into<T>) -> Option<T> {
171 let id = id.into();
172 let value = value.into();
173 self.with_state_mut(|mut s| s.set(id, value))
174 }
175
176 pub fn flag_state(&self, id: impl Into<StateId<()>>) -> bool {
180 let id = id.into();
181 self.with_state_mut(|mut s| s.flag(id))
182 }
183
184 pub fn init_state<T: StateValue>(&self, id: impl Into<StateId<T>>, init: impl FnOnce() -> T) {
186 let id = id.into();
187 self.with_state_mut(|mut s| {
188 s.entry(id).or_insert_with(init);
189 });
190 }
191
192 pub fn init_state_default<T: StateValue + Default>(&self, id: impl Into<StateId<T>>) {
194 self.init_state(id.into(), Default::default)
195 }
196
197 pub fn contains_state<T: StateValue>(&self, id: impl Into<StateId<T>>) -> bool {
199 let id = id.into();
200 self.with_state(|s| s.contains(id))
201 }
202
203 pub fn with_context<R>(&self, ctx: &mut WindowCtx, f: impl FnOnce() -> R) -> R {
205 let _span = match ctx.0.as_mut() {
206 Some(c) => UpdatesTrace::window_span(c.id),
207 None => panic!("window is required"),
208 };
209 WINDOW_CTX.with_context(&mut ctx.0, f)
210 }
211
212 pub fn with_no_context<R>(&self, f: impl FnOnce() -> R) -> R {
214 WINDOW_CTX.with_default(f)
215 }
216}
217
218#[cfg(any(test, doc, feature = "test_util"))]
220mod _impl {
221 use zng_color::colors;
222 use zng_layout::{
223 context::{InlineConstraints, InlineConstraintsLayout, InlineConstraintsMeasure, LAYOUT, LayoutMetrics},
224 unit::{FactorUnits, Length, Px, PxConstraints2d, PxSize, PxTransform},
225 };
226 use zng_state_map::{StateId, static_id};
227 use zng_view_api::config::FontAntiAliasing;
228
229 use super::*;
230 use crate::{
231 render::FrameValueKey,
232 update::{ContextUpdates, EventUpdate, LayoutUpdates, UPDATES, UpdateDeliveryList, WidgetUpdates},
233 widget::{
234 WIDGET, WIDGET_CTX, WidgetCtx, WidgetId, WidgetUpdateMode,
235 info::{WidgetBorderInfo, WidgetBoundsInfo, WidgetPath},
236 node::UiNode,
237 },
238 };
239 use atomic::Ordering::Relaxed;
240
241 static_id! {
242 static ref TEST_WINDOW_CFG: StateId<TestWindowCfg>;
243 }
244
245 struct TestWindowCfg {
246 size: PxSize,
247 }
248
249 impl WINDOW {
257 pub fn with_test_context<R>(&self, update_mode: WidgetUpdateMode, f: impl FnOnce() -> R) -> R {
259 let window_id = WindowId::new_unique();
260 let root_id = WidgetId::new_unique();
261 let mut ctx = WindowCtx::new(window_id, WindowMode::Headless);
262 ctx.set_widget_tree(WidgetInfoTree::wgt(window_id, root_id));
263 WINDOW.with_context(&mut ctx, || {
264 WINDOW.set_state(
265 *TEST_WINDOW_CFG,
266 TestWindowCfg {
267 size: PxSize::new(Px(1132), Px(956)),
268 },
269 );
270
271 let mut ctx = WidgetCtx::new(root_id);
272 WIDGET.with_context(&mut ctx, update_mode, f)
273 })
274 }
275
276 pub fn test_window_size(&self) -> PxSize {
280 WINDOW.with_state_mut(|mut s| s.get_mut(*TEST_WINDOW_CFG).expect("not in test window").size)
281 }
282
283 pub fn set_test_window_size(&self, size: PxSize) {
285 WINDOW.with_state_mut(|mut s| {
286 s.get_mut(*TEST_WINDOW_CFG).expect("not in test window").size = size;
287 })
288 }
289
290 pub fn test_init(&self, content: &mut impl UiNode) -> ContextUpdates {
294 content.init();
295 WIDGET.test_root_updates();
296 UPDATES.apply()
297 }
298
299 pub fn test_deinit(&self, content: &mut impl UiNode) -> ContextUpdates {
303 content.deinit();
304 WIDGET.test_root_updates();
305 UPDATES.apply()
306 }
307
308 pub fn test_info(&self, content: &mut impl UiNode) -> ContextUpdates {
312 let l_size = self.test_window_size();
313 let mut info = crate::widget::info::WidgetInfoBuilder::new(
314 Arc::default(),
315 WINDOW.id(),
316 crate::widget::info::access::AccessEnabled::APP,
317 WIDGET.id(),
318 WidgetBoundsInfo::new_size(l_size, l_size),
319 WidgetBorderInfo::new(),
320 1.fct(),
321 );
322 content.info(&mut info);
323 let tree = info.finalize(Some(self.info()), false);
324 *WINDOW_CTX.get().widget_tree.write() = Some(tree);
325 WIDGET.test_root_updates();
326 UPDATES.apply()
327 }
328
329 pub fn test_event(&self, content: &mut impl UiNode, update: &mut EventUpdate) -> ContextUpdates {
333 update.delivery_list_mut().fulfill_search([&WINDOW.info()].into_iter());
334 content.event(update);
335 WIDGET.test_root_updates();
336 UPDATES.apply()
337 }
338
339 pub fn test_update(&self, content: &mut impl UiNode, updates: Option<&mut WidgetUpdates>) -> ContextUpdates {
345 if let Some(updates) = updates {
346 updates.delivery_list_mut().fulfill_search([&WINDOW.info()].into_iter());
347 content.update(updates)
348 } else {
349 let target = if let Some(content_id) = content.with_context(WidgetUpdateMode::Ignore, || WIDGET.id()) {
350 WidgetPath::new(WINDOW.id(), vec![WIDGET.id(), content_id].into())
351 } else {
352 WidgetPath::new(WINDOW.id(), vec![WIDGET.id()].into())
353 };
354
355 let mut updates = WidgetUpdates::new(UpdateDeliveryList::new_any());
356 updates.delivery_list.insert_wgt(&target);
357
358 content.update(&updates);
359 }
360 WIDGET.test_root_updates();
361 UPDATES.apply()
362 }
363
364 pub fn test_layout(&self, content: &mut impl UiNode, constraints: Option<PxConstraints2d>) -> (PxSize, ContextUpdates) {
368 let font_size = Length::pt_to_px(14.0, 1.0.fct());
369 let viewport = self.test_window_size();
370 let mut metrics = LayoutMetrics::new(1.fct(), viewport, font_size);
371 if let Some(c) = constraints {
372 metrics = metrics.with_constraints(c);
373 }
374 let mut updates = LayoutUpdates::new(UpdateDeliveryList::new_any());
375 updates.delivery_list.insert_updates_root(WINDOW.id(), WIDGET.id());
376 let size = LAYOUT.with_context(metrics, || {
377 crate::widget::info::WidgetLayout::with_root_widget(Arc::new(updates), |wl| content.layout(wl))
378 });
379 WIDGET.test_root_updates();
380 (size, UPDATES.apply())
381 }
382
383 pub fn test_layout_inline(
389 &self,
390 content: &mut impl UiNode,
391 measure_constraints: (PxConstraints2d, InlineConstraintsMeasure),
392 layout_constraints: (PxConstraints2d, InlineConstraintsLayout),
393 ) -> ((PxSize, PxSize), ContextUpdates) {
394 let font_size = Length::pt_to_px(14.0, 1.0.fct());
395 let viewport = self.test_window_size();
396
397 let metrics = LayoutMetrics::new(1.fct(), viewport, font_size)
398 .with_constraints(measure_constraints.0)
399 .with_inline_constraints(Some(InlineConstraints::Measure(measure_constraints.1)));
400 let measure_size = LAYOUT.with_context(metrics, || {
401 content.measure(&mut crate::widget::info::WidgetMeasure::new(Arc::default()))
402 });
403
404 let metrics = LayoutMetrics::new(1.fct(), viewport, font_size)
405 .with_constraints(layout_constraints.0)
406 .with_inline_constraints(Some(InlineConstraints::Layout(layout_constraints.1)));
407
408 let mut updates = LayoutUpdates::new(UpdateDeliveryList::new_any());
409 updates.delivery_list.insert_updates_root(WINDOW.id(), WIDGET.id());
410
411 let layout_size = LAYOUT.with_context(metrics, || {
412 crate::widget::info::WidgetLayout::with_root_widget(Arc::new(updates), |wl| content.layout(wl))
413 });
414 WIDGET.test_root_updates();
415 ((measure_size, layout_size), UPDATES.apply())
416 }
417
418 pub fn test_render(&self, content: &mut impl UiNode) -> (crate::render::BuiltFrame, ContextUpdates) {
422 use crate::render::*;
423
424 let mut frame = {
425 let win = WINDOW_CTX.get();
426 let wgt = WIDGET_CTX.get();
427
428 let frame_id = win.frame_id.load(Relaxed);
429 win.frame_id.store(frame_id.next(), Relaxed);
430
431 let f = FrameBuilder::new_renderless(
432 Arc::default(),
433 Arc::default(),
434 frame_id,
435 wgt.id,
436 &wgt.bounds.lock(),
437 win.widget_tree.read().as_ref().unwrap(),
438 1.fct(),
439 FontAntiAliasing::Default,
440 );
441 f
442 };
443
444 frame.push_inner(self.test_root_translation_key(), false, |frame| {
445 content.render(frame);
446 });
447
448 let tree = WINDOW_CTX.get().widget_tree.read().as_ref().unwrap().clone();
449 let f = frame.finalize(&tree);
450 WIDGET.test_root_updates();
451 (f, UPDATES.apply())
452 }
453
454 pub fn test_render_update(&self, content: &mut impl UiNode) -> (crate::render::BuiltFrameUpdate, ContextUpdates) {
458 use crate::render::*;
459
460 let mut update = {
461 let win = WINDOW_CTX.get();
462 let wgt = WIDGET_CTX.get();
463
464 let frame_id = win.frame_id.load(Relaxed);
465 win.frame_id.store(frame_id.next_update(), Relaxed);
466
467 let f = FrameUpdate::new(Arc::default(), frame_id, wgt.id, wgt.bounds.lock().clone(), colors::BLACK);
468 f
469 };
470
471 update.update_inner(self.test_root_translation_key(), false, |update| {
472 content.render_update(update);
473 });
474 let tree = WINDOW_CTX.get().widget_tree.read().as_ref().unwrap().clone();
475 let f = update.finalize(&tree);
476 WIDGET.test_root_updates();
477 (f, UPDATES.apply())
478 }
479
480 fn test_root_translation_key(&self) -> FrameValueKey<PxTransform> {
481 static_id! {
482 static ref ID: StateId<FrameValueKey<PxTransform>>;
483 }
484 WINDOW.with_state_mut(|mut s| *s.entry(*ID).or_insert_with(FrameValueKey::new_unique))
485 }
486 }
487}
488
489context_local! {
490 static WINDOW_CTX: WindowCtxData = WindowCtxData::no_context();
491}
492
493pub struct WindowCtx(Option<Arc<WindowCtxData>>);
497impl WindowCtx {
498 pub fn new(id: WindowId, mode: WindowMode) -> Self {
500 Self(Some(Arc::new(WindowCtxData {
501 id,
502 mode,
503 state: RwLock::new(OwnedStateMap::default()),
504 widget_tree: RwLock::new(None),
505
506 #[cfg(any(test, doc, feature = "test_util"))]
507 frame_id: atomic::Atomic::new(zng_view_api::window::FrameId::first()),
508 })))
509 }
510
511 pub fn set_widget_tree(&mut self, widget_tree: WidgetInfoTree) {
516 *self.0.as_mut().unwrap().widget_tree.write() = Some(widget_tree);
517 }
518
519 pub fn id(&self) -> WindowId {
521 self.0.as_ref().unwrap().id
522 }
523
524 pub fn mode(&self) -> WindowMode {
526 self.0.as_ref().unwrap().mode
527 }
528
529 pub fn widget_tree(&self) -> WidgetInfoTree {
531 self.0.as_ref().unwrap().widget_tree.read().as_ref().unwrap().clone()
532 }
533
534 pub fn with_state<R>(&mut self, f: impl FnOnce(&mut OwnedStateMap<WINDOW>) -> R) -> R {
536 f(&mut self.0.as_mut().unwrap().state.write())
537 }
538
539 pub fn share(&mut self) -> Self {
543 Self(self.0.clone())
544 }
545}
546
547struct WindowCtxData {
548 id: WindowId,
549 mode: WindowMode,
550 state: RwLock<OwnedStateMap<WINDOW>>,
551 widget_tree: RwLock<Option<WidgetInfoTree>>,
552
553 #[cfg(any(test, doc, feature = "test_util"))]
554 frame_id: atomic::Atomic<zng_view_api::window::FrameId>,
555}
556impl WindowCtxData {
557 #[track_caller]
558 fn no_context() -> Self {
559 panic!("no window in context")
560 }
561}
562
563#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
565pub enum WindowMode {
566 Headed,
568 Headless,
573 HeadlessWithRenderer,
576}
577impl fmt::Debug for WindowMode {
578 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
579 if f.alternate() {
580 write!(f, "WindowMode::")?;
581 }
582 match self {
583 WindowMode::Headed => write!(f, "Headed"),
584 WindowMode::Headless => write!(f, "Headless"),
585 WindowMode::HeadlessWithRenderer => write!(f, "HeadlessWithRenderer"),
586 }
587 }
588}
589impl WindowMode {
590 pub fn is_headed(self) -> bool {
592 match self {
593 WindowMode::Headed => true,
594 WindowMode::Headless | WindowMode::HeadlessWithRenderer => false,
595 }
596 }
597
598 pub fn is_headless(self) -> bool {
600 match self {
601 WindowMode::Headless | WindowMode::HeadlessWithRenderer => true,
602 WindowMode::Headed => false,
603 }
604 }
605
606 pub fn has_renderer(self) -> bool {
608 match self {
609 WindowMode::Headed | WindowMode::HeadlessWithRenderer => true,
610 WindowMode::Headless => false,
611 }
612 }
613}