1use std::{any::Any, borrow::Cow, fmt, sync::Arc};
4
5use crate::{
6 update::{InfoUpdates, LayoutUpdates, RenderUpdates, UpdatesTrace, WidgetUpdates},
7 widget::{
8 WidgetId,
9 info::{WidgetInfo, WidgetInfoTree},
10 },
11};
12use parking_lot::RwLock;
13use zng_app_context::context_local;
14use zng_state_map::{OwnedStateMap, StateId, StateMapMut, StateMapRef, StateValue};
15use zng_txt::Txt;
16
17zng_unique_id::unique_id_32! {
18 pub struct WindowId;
30}
31zng_unique_id::impl_unique_id_name!(WindowId);
32zng_unique_id::impl_unique_id_fmt!(WindowId);
33zng_unique_id::impl_unique_id_bytemuck!(WindowId);
34
35zng_var::impl_from_and_into_var! {
36 fn from(name: &'static str) -> WindowId {
38 WindowId::named(name)
39 }
40 fn from(name: String) -> WindowId {
42 WindowId::named(name)
43 }
44 fn from(name: Cow<'static, str>) -> WindowId {
46 WindowId::named(name)
47 }
48 fn from(name: char) -> WindowId {
50 WindowId::named(name)
51 }
52 fn from(name: Txt) -> WindowId {
54 WindowId::named(name)
55 }
56
57 fn from(some: WindowId) -> Option<WindowId>;
58}
59impl serde::Serialize for WindowId {
60 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
61 where
62 S: serde::Serializer,
63 {
64 let name = self.name();
65 if name.is_empty() {
66 use serde::ser::Error;
67 return Err(S::Error::custom("cannot serialize unnamed `WindowId`"));
68 }
69 name.serialize(serializer)
70 }
71}
72impl<'de> serde::Deserialize<'de> for WindowId {
73 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
74 where
75 D: serde::Deserializer<'de>,
76 {
77 let name = Txt::deserialize(deserializer)?;
78 Ok(WindowId::named(name))
79 }
80}
81
82zng_unique_id::unique_id_32! {
83 pub struct MonitorId;
85}
86zng_unique_id::impl_unique_id_bytemuck!(MonitorId);
87impl fmt::Debug for MonitorId {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 if f.alternate() {
90 f.debug_struct("MonitorId")
91 .field("id", &self.get())
92 .field("sequential", &self.sequential())
93 .finish()
94 } else {
95 write!(f, "MonitorId({})", self.sequential())
96 }
97 }
98}
99impl fmt::Display for MonitorId {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 write!(f, "MonitorId({})", self.sequential())
102 }
103}
104impl MonitorId {
105 pub fn fallback() -> MonitorId {
107 static FALLBACK: once_cell::sync::Lazy<MonitorId> = once_cell::sync::Lazy::new(MonitorId::new_unique);
108 *FALLBACK
109 }
110}
111
112pub struct WINDOW;
121impl WINDOW {
122 pub fn is_in_window(&self) -> bool {
124 !WINDOW_CTX.is_default()
125 }
126
127 pub fn try_id(&self) -> Option<WindowId> {
129 if WINDOW_CTX.is_default() { None } else { Some(WINDOW_CTX.get().id) }
130 }
131
132 pub fn try_info(&self) -> Option<WidgetInfoTree> {
134 if !WINDOW_CTX.is_default() {
135 return WINDOW_CTX.get().widget_tree.read().clone();
136 }
137 None
138 }
139
140 pub fn id(&self) -> WindowId {
142 WINDOW_CTX.get().id
143 }
144
145 pub fn mode(&self) -> WindowMode {
147 WINDOW_CTX.get().mode
148 }
149
150 pub fn info(&self) -> WidgetInfoTree {
154 WINDOW_CTX.get().widget_tree.read().clone().expect("window not init")
155 }
156
157 pub fn with_state<R>(&self, f: impl FnOnce(StateMapRef<WINDOW>) -> R) -> R {
159 f(WINDOW_CTX.get().state.read().borrow())
160 }
161
162 pub fn with_state_mut<R>(&self, f: impl FnOnce(StateMapMut<WINDOW>) -> R) -> R {
164 f(WINDOW_CTX.get().state.write().borrow_mut())
165 }
166
167 pub fn get_state<T: StateValue + Clone>(&self, id: impl Into<StateId<T>>) -> Option<T> {
169 let id = id.into();
170 self.with_state(|s| s.get_clone(id))
171 }
172
173 pub fn req_state<T: StateValue + Clone>(&self, id: impl Into<StateId<T>>) -> T {
177 let id = id.into();
178 self.with_state(|s| s.req(id).clone())
179 }
180
181 pub fn set_state<T: StateValue>(&self, id: impl Into<StateId<T>>, value: impl Into<T>) -> Option<T> {
185 let id = id.into();
186 let value = value.into();
187 self.with_state_mut(|mut s| s.set(id, value))
188 }
189
190 pub fn flag_state(&self, id: impl Into<StateId<()>>) -> bool {
194 let id = id.into();
195 self.with_state_mut(|mut s| s.flag(id))
196 }
197
198 pub fn init_state<T: StateValue>(&self, id: impl Into<StateId<T>>, init: impl FnOnce() -> T) {
200 let id = id.into();
201 self.with_state_mut(|mut s| {
202 s.entry(id).or_insert_with(init);
203 });
204 }
205
206 pub fn init_state_default<T: StateValue + Default>(&self, id: impl Into<StateId<T>>) {
208 self.init_state(id.into(), Default::default)
209 }
210
211 pub fn contains_state<T: StateValue>(&self, id: impl Into<StateId<T>>) -> bool {
213 let id = id.into();
214 self.with_state(|s| s.contains(id))
215 }
216
217 #[inline(always)]
219 pub fn with_context<R>(&self, ctx: &mut WindowCtx, f: impl FnOnce() -> R) -> R {
220 fn pre(ctx: &mut WindowCtx) -> tracing::span::EnteredSpan {
221 match ctx.0.as_mut() {
222 Some(c) => UpdatesTrace::window_span(c.id),
223 None => panic!("window is required"),
224 }
225 }
226 let _span = pre(ctx);
227 WINDOW_CTX.with_context(&mut ctx.0, f)
228 }
229
230 #[inline(always)]
232 pub fn with_no_context<R>(&self, f: impl FnOnce() -> R) -> R {
233 WINDOW_CTX.with_default(f)
234 }
235}
236
237#[cfg(any(test, doc, feature = "test_util"))]
239mod _impl {
240 use zng_color::colors;
241 use zng_layout::{
242 context::{InlineConstraints, InlineConstraintsLayout, InlineConstraintsMeasure, LAYOUT, LayoutMetrics},
243 unit::{FactorUnits, Length, Px, PxConstraints2d, PxSize, PxTransform},
244 };
245 use zng_state_map::{StateId, static_id};
246 use zng_view_api::config::FontAntiAliasing;
247
248 use super::*;
249 use crate::{
250 render::FrameValueKey,
251 update::{ContextUpdates, LayoutUpdates, UPDATES, UpdateDeliveryList, WidgetUpdates},
252 widget::{
253 WIDGET, WIDGET_CTX, WidgetCtx, WidgetId, WidgetUpdateMode,
254 info::{WidgetBorderInfo, WidgetBoundsInfo, WidgetPath},
255 node::UiNode,
256 },
257 };
258 use atomic::Ordering::Relaxed;
259
260 static_id! {
261 static ref TEST_WINDOW_CFG: StateId<TestWindowCfg>;
262 }
263
264 struct TestWindowCfg {
265 size: PxSize,
266 }
267
268 impl WINDOW {
276 pub fn with_test_context<R>(&self, update_mode: WidgetUpdateMode, f: impl FnOnce() -> R) -> R {
278 let window_id = WindowId::new_unique();
279 let root_id = WidgetId::new_unique();
280 let mut ctx = WindowCtx::new(window_id, WindowMode::Headless);
281 ctx.set_widget_tree(WidgetInfoTree::wgt(window_id, root_id));
282 WINDOW.with_context(&mut ctx, || {
283 WINDOW.set_state(
284 *TEST_WINDOW_CFG,
285 TestWindowCfg {
286 size: PxSize::new(Px(1132), Px(956)),
287 },
288 );
289
290 let mut ctx = WidgetCtx::new(root_id);
291 WIDGET.with_context(&mut ctx, update_mode, f)
292 })
293 }
294
295 pub fn test_window_size(&self) -> PxSize {
299 WINDOW.with_state_mut(|mut s| s.get_mut(*TEST_WINDOW_CFG).expect("not in test window").size)
300 }
301
302 pub fn set_test_window_size(&self, size: PxSize) {
304 WINDOW.with_state_mut(|mut s| {
305 s.get_mut(*TEST_WINDOW_CFG).expect("not in test window").size = size;
306 })
307 }
308
309 pub fn test_init(&self, content: &mut UiNode) -> ContextUpdates {
313 content.init();
314 WIDGET.test_root_updates();
315 UPDATES.apply()
316 }
317
318 pub fn test_deinit(&self, content: &mut UiNode) -> ContextUpdates {
322 content.deinit();
323 WIDGET.test_root_updates();
324 UPDATES.apply()
325 }
326
327 pub fn test_info(&self, content: &mut UiNode) -> ContextUpdates {
331 let l_size = self.test_window_size();
332 let mut info = crate::widget::info::WidgetInfoBuilder::new(
333 Arc::default(),
334 WINDOW.id(),
335 crate::widget::info::access::AccessEnabled::APP,
336 WIDGET.id(),
337 WidgetBoundsInfo::new_size(l_size, l_size),
338 WidgetBorderInfo::new(),
339 1.fct(),
340 );
341 content.info(&mut info);
342 let tree = info.finalize(Some(self.info()), false);
343 *WINDOW_CTX.get().widget_tree.write() = Some(tree);
344 WIDGET.test_root_updates();
345 UPDATES.apply()
346 }
347
348 pub fn test_update(&self, content: &mut UiNode, updates: Option<&mut WidgetUpdates>) -> ContextUpdates {
354 if let Some(updates) = updates {
355 updates.delivery_list_mut().fulfill_search([&WINDOW.info()].into_iter());
356 content.update(updates)
357 } else {
358 let target = if let Some(mut wgt) = content.as_widget() {
359 let content_id = wgt.id();
360 WidgetPath::new(WINDOW.id(), vec![WIDGET.id(), content_id].into())
361 } else {
362 WidgetPath::new(WINDOW.id(), vec![WIDGET.id()].into())
363 };
364
365 let mut updates = WidgetUpdates::new(UpdateDeliveryList::new());
366 updates.delivery_list.insert_wgt(&target);
367
368 content.update(&updates);
369 }
370 WIDGET.test_root_updates();
371 UPDATES.apply()
372 }
373
374 pub fn test_layout(&self, content: &mut UiNode, constraints: Option<PxConstraints2d>) -> (PxSize, ContextUpdates) {
378 let font_size = Length::pt_to_px(14.0, 1.0.fct());
379 let viewport = self.test_window_size();
380 let mut metrics = LayoutMetrics::new(1.fct(), viewport, font_size);
381 if let Some(c) = constraints {
382 metrics = metrics.with_constraints(c);
383 }
384 let mut updates = LayoutUpdates::new(UpdateDeliveryList::new());
385 updates.delivery_list.insert_updates_root(WINDOW.id(), WIDGET.id());
386 let size = LAYOUT.with_context(metrics, || {
387 crate::widget::info::WidgetLayout::with_root_widget(Arc::new(updates), |wl| content.layout(wl))
388 });
389 WIDGET.test_root_updates();
390 (size, UPDATES.apply())
391 }
392
393 pub fn test_layout_inline(
399 &self,
400 content: &mut UiNode,
401 measure_constraints: (PxConstraints2d, InlineConstraintsMeasure),
402 layout_constraints: (PxConstraints2d, InlineConstraintsLayout),
403 ) -> ((PxSize, PxSize), ContextUpdates) {
404 let font_size = Length::pt_to_px(14.0, 1.0.fct());
405 let viewport = self.test_window_size();
406
407 let metrics = LayoutMetrics::new(1.fct(), viewport, font_size)
408 .with_constraints(measure_constraints.0)
409 .with_inline_constraints(Some(InlineConstraints::Measure(measure_constraints.1)));
410 let measure_size = LAYOUT.with_context(metrics, || {
411 content.measure(&mut crate::widget::info::WidgetMeasure::new(Arc::default()))
412 });
413
414 let metrics = LayoutMetrics::new(1.fct(), viewport, font_size)
415 .with_constraints(layout_constraints.0)
416 .with_inline_constraints(Some(InlineConstraints::Layout(layout_constraints.1)));
417
418 let mut updates = LayoutUpdates::new(UpdateDeliveryList::new());
419 updates.delivery_list.insert_updates_root(WINDOW.id(), WIDGET.id());
420
421 let layout_size = LAYOUT.with_context(metrics, || {
422 crate::widget::info::WidgetLayout::with_root_widget(Arc::new(updates), |wl| content.layout(wl))
423 });
424 WIDGET.test_root_updates();
425 ((measure_size, layout_size), UPDATES.apply())
426 }
427
428 pub fn test_render(&self, content: &mut UiNode) -> (crate::render::BuiltFrame, ContextUpdates) {
432 use crate::render::*;
433
434 let mut frame = {
435 let win = WINDOW_CTX.get();
436 let wgt = WIDGET_CTX.get();
437
438 let frame_id = win.frame_id.load(Relaxed);
439 win.frame_id.store(frame_id.next(), Relaxed);
440
441 FrameBuilder::new_renderless(
442 Arc::default(),
443 Arc::default(),
444 frame_id,
445 wgt.id,
446 &wgt.bounds.lock(),
447 win.widget_tree.read().as_ref().unwrap(),
448 1.fct(),
449 FontAntiAliasing::Default,
450 )
451 };
452
453 frame.push_inner(self.test_root_translation_key(), false, |frame| {
454 content.render(frame);
455 });
456
457 let tree = WINDOW_CTX.get().widget_tree.read().as_ref().unwrap().clone();
458 let f = frame.finalize(&tree);
459 WIDGET.test_root_updates();
460 (f, UPDATES.apply())
461 }
462
463 pub fn test_render_update(&self, content: &mut UiNode) -> (crate::render::BuiltFrameUpdate, ContextUpdates) {
467 use crate::render::*;
468
469 let mut update = {
470 let win = WINDOW_CTX.get();
471 let wgt = WIDGET_CTX.get();
472
473 let frame_id = win.frame_id.load(Relaxed);
474 win.frame_id.store(frame_id.next_update(), Relaxed);
475
476 FrameUpdate::new(Arc::default(), frame_id, wgt.id, wgt.bounds.lock().clone(), colors::BLACK)
477 };
478
479 update.update_inner(self.test_root_translation_key(), false, |update| {
480 content.render_update(update);
481 });
482 let tree = WINDOW_CTX.get().widget_tree.read().as_ref().unwrap().clone();
483 let f = update.finalize(&tree);
484 WIDGET.test_root_updates();
485 (f, UPDATES.apply())
486 }
487
488 fn test_root_translation_key(&self) -> FrameValueKey<PxTransform> {
489 static_id! {
490 static ref ID: StateId<FrameValueKey<PxTransform>>;
491 }
492 WINDOW.with_state_mut(|mut s| *s.entry(*ID).or_insert_with(FrameValueKey::new_unique))
493 }
494 }
495}
496
497context_local! {
498 static WINDOW_CTX: WindowCtxData = WindowCtxData::no_context();
499}
500
501pub struct WindowCtx(Option<Arc<WindowCtxData>>);
505impl WindowCtx {
506 pub fn new(id: WindowId, mode: WindowMode) -> Self {
508 Self(Some(Arc::new(WindowCtxData {
509 id,
510 mode,
511 state: RwLock::new(OwnedStateMap::default()),
512 widget_tree: RwLock::new(None),
513
514 #[cfg(any(test, doc, feature = "test_util"))]
515 frame_id: atomic::Atomic::new(zng_view_api::window::FrameId::first()),
516 })))
517 }
518
519 pub fn set_widget_tree(&mut self, widget_tree: WidgetInfoTree) {
524 *self.0.as_mut().unwrap().widget_tree.write() = Some(widget_tree);
525 }
526
527 pub fn id(&self) -> WindowId {
529 self.0.as_ref().unwrap().id
530 }
531
532 pub fn mode(&self) -> WindowMode {
534 self.0.as_ref().unwrap().mode
535 }
536
537 pub fn widget_tree(&self) -> WidgetInfoTree {
539 self.0.as_ref().unwrap().widget_tree.read().as_ref().unwrap().clone()
540 }
541
542 pub fn with_state<R>(&mut self, f: impl FnOnce(&mut OwnedStateMap<WINDOW>) -> R) -> R {
544 f(&mut self.0.as_mut().unwrap().state.write())
545 }
546
547 pub fn share(&mut self) -> Self {
551 Self(self.0.clone())
552 }
553}
554
555struct WindowCtxData {
556 id: WindowId,
557 mode: WindowMode,
558 state: RwLock<OwnedStateMap<WINDOW>>,
559 widget_tree: RwLock<Option<WidgetInfoTree>>,
560
561 #[cfg(any(test, doc, feature = "test_util"))]
562 frame_id: atomic::Atomic<zng_view_api::window::FrameId>,
563}
564impl WindowCtxData {
565 #[track_caller]
566 fn no_context() -> Self {
567 panic!("no window in context")
568 }
569}
570
571#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
573pub enum WindowMode {
574 Headed,
576 Headless,
581 HeadlessWithRenderer,
584}
585impl fmt::Debug for WindowMode {
586 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587 if f.alternate() {
588 write!(f, "WindowMode::")?;
589 }
590 match self {
591 WindowMode::Headed => write!(f, "Headed"),
592 WindowMode::Headless => write!(f, "Headless"),
593 WindowMode::HeadlessWithRenderer => write!(f, "HeadlessWithRenderer"),
594 }
595 }
596}
597impl WindowMode {
598 pub fn is_headed(self) -> bool {
600 match self {
601 WindowMode::Headed => true,
602 WindowMode::Headless | WindowMode::HeadlessWithRenderer => false,
603 }
604 }
605
606 pub fn is_headless(self) -> bool {
608 match self {
609 WindowMode::Headless | WindowMode::HeadlessWithRenderer => true,
610 WindowMode::Headed => false,
611 }
612 }
613
614 pub fn has_renderer(self) -> bool {
616 match self {
617 WindowMode::Headed | WindowMode::HeadlessWithRenderer => true,
618 WindowMode::Headless => false,
619 }
620 }
621}
622
623#[allow(non_camel_case_types)]
625pub struct WINDOWS_APP;
626impl WINDOWS_APP {
627 pub fn init_info_provider(&self, service: Box<dyn WindowsService>) {
629 *WINDOWS_APP_SV.write() = Some(service);
630 }
631
632 fn with<R>(&self, f: impl FnOnce(&dyn WindowsService) -> R) -> Option<R> {
633 if let Some(s) = WINDOWS_APP_SV.read().as_ref() {
634 Some(f(&**s))
635 } else {
636 tracing::debug!("no WINDOWS_APP");
637 None
638 }
639 }
640
641 pub fn widget_tree(&self, id: WindowId) -> Option<WidgetInfoTree> {
643 self.with(|s| s.widget_tree(id)).flatten()
644 }
645
646 pub fn widget_info(&self, id: WidgetId) -> Option<WidgetInfo> {
648 self.with(|s| s.widget_info(id)).flatten()
649 }
650
651 pub fn update_info(&self, info_widgets: &mut InfoUpdates) {
653 self.with(|s| s.update_info(info_widgets));
654 }
655
656 pub fn update_widgets(&self, update_widgets: &mut WidgetUpdates) {
658 self.with(|s| s.update_widgets(update_widgets));
659 }
660
661 pub fn update_layout(&self, layout_widgets: &mut LayoutUpdates) {
663 self.with(|s| s.update_layout(layout_widgets));
664 }
665
666 pub fn update_render(&self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
668 self.with(|s| s.update_render(render_widgets, render_update_widgets));
669 }
670}
671
672pub trait WindowsService: Any + Send + Sync {
674 fn widget_tree(&self, id: WindowId) -> Option<WidgetInfoTree>;
676 fn widget_info(&self, id: WidgetId) -> Option<WidgetInfo>;
678 fn update_info(&self, updates: &mut InfoUpdates);
680 fn update_widgets(&self, updates: &mut WidgetUpdates);
682 fn update_layout(&self, updates: &mut LayoutUpdates);
684 fn update_render(&self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates);
686}
687
688zng_app_context::app_local! {
689 static WINDOWS_APP_SV: Option<Box<dyn WindowsService>> = const { None };
690}