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 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 };
442
443 frame.push_inner(self.test_root_translation_key(), false, |frame| {
444 content.render(frame);
445 });
446
447 let tree = WINDOW_CTX.get().widget_tree.read().as_ref().unwrap().clone();
448 let f = frame.finalize(&tree);
449 WIDGET.test_root_updates();
450 (f, UPDATES.apply())
451 }
452
453 pub fn test_render_update(&self, content: &mut impl UiNode) -> (crate::render::BuiltFrameUpdate, ContextUpdates) {
457 use crate::render::*;
458
459 let mut update = {
460 let win = WINDOW_CTX.get();
461 let wgt = WIDGET_CTX.get();
462
463 let frame_id = win.frame_id.load(Relaxed);
464 win.frame_id.store(frame_id.next_update(), Relaxed);
465
466 FrameUpdate::new(Arc::default(), frame_id, wgt.id, wgt.bounds.lock().clone(), colors::BLACK)
467 };
468
469 update.update_inner(self.test_root_translation_key(), false, |update| {
470 content.render_update(update);
471 });
472 let tree = WINDOW_CTX.get().widget_tree.read().as_ref().unwrap().clone();
473 let f = update.finalize(&tree);
474 WIDGET.test_root_updates();
475 (f, UPDATES.apply())
476 }
477
478 fn test_root_translation_key(&self) -> FrameValueKey<PxTransform> {
479 static_id! {
480 static ref ID: StateId<FrameValueKey<PxTransform>>;
481 }
482 WINDOW.with_state_mut(|mut s| *s.entry(*ID).or_insert_with(FrameValueKey::new_unique))
483 }
484 }
485}
486
487context_local! {
488 static WINDOW_CTX: WindowCtxData = WindowCtxData::no_context();
489}
490
491pub struct WindowCtx(Option<Arc<WindowCtxData>>);
495impl WindowCtx {
496 pub fn new(id: WindowId, mode: WindowMode) -> Self {
498 Self(Some(Arc::new(WindowCtxData {
499 id,
500 mode,
501 state: RwLock::new(OwnedStateMap::default()),
502 widget_tree: RwLock::new(None),
503
504 #[cfg(any(test, doc, feature = "test_util"))]
505 frame_id: atomic::Atomic::new(zng_view_api::window::FrameId::first()),
506 })))
507 }
508
509 pub fn set_widget_tree(&mut self, widget_tree: WidgetInfoTree) {
514 *self.0.as_mut().unwrap().widget_tree.write() = Some(widget_tree);
515 }
516
517 pub fn id(&self) -> WindowId {
519 self.0.as_ref().unwrap().id
520 }
521
522 pub fn mode(&self) -> WindowMode {
524 self.0.as_ref().unwrap().mode
525 }
526
527 pub fn widget_tree(&self) -> WidgetInfoTree {
529 self.0.as_ref().unwrap().widget_tree.read().as_ref().unwrap().clone()
530 }
531
532 pub fn with_state<R>(&mut self, f: impl FnOnce(&mut OwnedStateMap<WINDOW>) -> R) -> R {
534 f(&mut self.0.as_mut().unwrap().state.write())
535 }
536
537 pub fn share(&mut self) -> Self {
541 Self(self.0.clone())
542 }
543}
544
545struct WindowCtxData {
546 id: WindowId,
547 mode: WindowMode,
548 state: RwLock<OwnedStateMap<WINDOW>>,
549 widget_tree: RwLock<Option<WidgetInfoTree>>,
550
551 #[cfg(any(test, doc, feature = "test_util"))]
552 frame_id: atomic::Atomic<zng_view_api::window::FrameId>,
553}
554impl WindowCtxData {
555 #[track_caller]
556 fn no_context() -> Self {
557 panic!("no window in context")
558 }
559}
560
561#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
563pub enum WindowMode {
564 Headed,
566 Headless,
571 HeadlessWithRenderer,
574}
575impl fmt::Debug for WindowMode {
576 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
577 if f.alternate() {
578 write!(f, "WindowMode::")?;
579 }
580 match self {
581 WindowMode::Headed => write!(f, "Headed"),
582 WindowMode::Headless => write!(f, "Headless"),
583 WindowMode::HeadlessWithRenderer => write!(f, "HeadlessWithRenderer"),
584 }
585 }
586}
587impl WindowMode {
588 pub fn is_headed(self) -> bool {
590 match self {
591 WindowMode::Headed => true,
592 WindowMode::Headless | WindowMode::HeadlessWithRenderer => false,
593 }
594 }
595
596 pub fn is_headless(self) -> bool {
598 match self {
599 WindowMode::Headless | WindowMode::HeadlessWithRenderer => true,
600 WindowMode::Headed => false,
601 }
602 }
603
604 pub fn has_renderer(self) -> bool {
606 match self {
607 WindowMode::Headed | WindowMode::HeadlessWithRenderer => true,
608 WindowMode::Headless => false,
609 }
610 }
611}