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 try_info(&self) -> Option<WidgetInfoTree> {
128 if !WINDOW_CTX.is_default() {
129 return WINDOW_CTX.get().widget_tree.read().clone();
130 }
131 None
132 }
133
134 pub fn id(&self) -> WindowId {
136 WINDOW_CTX.get().id
137 }
138
139 pub fn mode(&self) -> WindowMode {
141 WINDOW_CTX.get().mode
142 }
143
144 pub fn info(&self) -> WidgetInfoTree {
148 WINDOW_CTX.get().widget_tree.read().clone().expect("window not init")
149 }
150
151 pub fn with_state<R>(&self, f: impl FnOnce(StateMapRef<WINDOW>) -> R) -> R {
153 f(WINDOW_CTX.get().state.read().borrow())
154 }
155
156 pub fn with_state_mut<R>(&self, f: impl FnOnce(StateMapMut<WINDOW>) -> R) -> R {
158 f(WINDOW_CTX.get().state.write().borrow_mut())
159 }
160
161 pub fn get_state<T: StateValue + Clone>(&self, id: impl Into<StateId<T>>) -> Option<T> {
163 let id = id.into();
164 self.with_state(|s| s.get_clone(id))
165 }
166
167 pub fn req_state<T: StateValue + Clone>(&self, id: impl Into<StateId<T>>) -> T {
171 let id = id.into();
172 self.with_state(|s| s.req(id).clone())
173 }
174
175 pub fn set_state<T: StateValue>(&self, id: impl Into<StateId<T>>, value: impl Into<T>) -> Option<T> {
179 let id = id.into();
180 let value = value.into();
181 self.with_state_mut(|mut s| s.set(id, value))
182 }
183
184 pub fn flag_state(&self, id: impl Into<StateId<()>>) -> bool {
188 let id = id.into();
189 self.with_state_mut(|mut s| s.flag(id))
190 }
191
192 pub fn init_state<T: StateValue>(&self, id: impl Into<StateId<T>>, init: impl FnOnce() -> T) {
194 let id = id.into();
195 self.with_state_mut(|mut s| {
196 s.entry(id).or_insert_with(init);
197 });
198 }
199
200 pub fn init_state_default<T: StateValue + Default>(&self, id: impl Into<StateId<T>>) {
202 self.init_state(id.into(), Default::default)
203 }
204
205 pub fn contains_state<T: StateValue>(&self, id: impl Into<StateId<T>>) -> bool {
207 let id = id.into();
208 self.with_state(|s| s.contains(id))
209 }
210
211 #[inline(always)]
213 pub fn with_context<R>(&self, ctx: &mut WindowCtx, f: impl FnOnce() -> R) -> R {
214 fn pre(ctx: &mut WindowCtx) -> tracing::span::EnteredSpan {
215 match ctx.0.as_mut() {
216 Some(c) => UpdatesTrace::window_span(c.id),
217 None => panic!("window is required"),
218 }
219 }
220 let _span = pre(ctx);
221 WINDOW_CTX.with_context(&mut ctx.0, f)
222 }
223
224 #[inline(always)]
226 pub fn with_no_context<R>(&self, f: impl FnOnce() -> R) -> R {
227 WINDOW_CTX.with_default(f)
228 }
229}
230
231#[cfg(any(test, doc, feature = "test_util"))]
233mod _impl {
234 use zng_color::colors;
235 use zng_layout::{
236 context::{InlineConstraints, InlineConstraintsLayout, InlineConstraintsMeasure, LAYOUT, LayoutMetrics},
237 unit::{FactorUnits, Length, Px, PxConstraints2d, PxSize, PxTransform},
238 };
239 use zng_state_map::{StateId, static_id};
240 use zng_view_api::config::FontAntiAliasing;
241
242 use super::*;
243 use crate::{
244 render::FrameValueKey,
245 update::{ContextUpdates, EventUpdate, LayoutUpdates, UPDATES, UpdateDeliveryList, WidgetUpdates},
246 widget::{
247 WIDGET, WIDGET_CTX, WidgetCtx, WidgetId, WidgetUpdateMode,
248 info::{WidgetBorderInfo, WidgetBoundsInfo, WidgetPath},
249 node::UiNode,
250 },
251 };
252 use atomic::Ordering::Relaxed;
253
254 static_id! {
255 static ref TEST_WINDOW_CFG: StateId<TestWindowCfg>;
256 }
257
258 struct TestWindowCfg {
259 size: PxSize,
260 }
261
262 impl WINDOW {
270 pub fn with_test_context<R>(&self, update_mode: WidgetUpdateMode, f: impl FnOnce() -> R) -> R {
272 let window_id = WindowId::new_unique();
273 let root_id = WidgetId::new_unique();
274 let mut ctx = WindowCtx::new(window_id, WindowMode::Headless);
275 ctx.set_widget_tree(WidgetInfoTree::wgt(window_id, root_id));
276 WINDOW.with_context(&mut ctx, || {
277 WINDOW.set_state(
278 *TEST_WINDOW_CFG,
279 TestWindowCfg {
280 size: PxSize::new(Px(1132), Px(956)),
281 },
282 );
283
284 let mut ctx = WidgetCtx::new(root_id);
285 WIDGET.with_context(&mut ctx, update_mode, f)
286 })
287 }
288
289 pub fn test_window_size(&self) -> PxSize {
293 WINDOW.with_state_mut(|mut s| s.get_mut(*TEST_WINDOW_CFG).expect("not in test window").size)
294 }
295
296 pub fn set_test_window_size(&self, size: PxSize) {
298 WINDOW.with_state_mut(|mut s| {
299 s.get_mut(*TEST_WINDOW_CFG).expect("not in test window").size = size;
300 })
301 }
302
303 pub fn test_init(&self, content: &mut UiNode) -> ContextUpdates {
307 content.init();
308 WIDGET.test_root_updates();
309 UPDATES.apply()
310 }
311
312 pub fn test_deinit(&self, content: &mut UiNode) -> ContextUpdates {
316 content.deinit();
317 WIDGET.test_root_updates();
318 UPDATES.apply()
319 }
320
321 pub fn test_info(&self, content: &mut UiNode) -> ContextUpdates {
325 let l_size = self.test_window_size();
326 let mut info = crate::widget::info::WidgetInfoBuilder::new(
327 Arc::default(),
328 WINDOW.id(),
329 crate::widget::info::access::AccessEnabled::APP,
330 WIDGET.id(),
331 WidgetBoundsInfo::new_size(l_size, l_size),
332 WidgetBorderInfo::new(),
333 1.fct(),
334 );
335 content.info(&mut info);
336 let tree = info.finalize(Some(self.info()), false);
337 *WINDOW_CTX.get().widget_tree.write() = Some(tree);
338 WIDGET.test_root_updates();
339 UPDATES.apply()
340 }
341
342 pub fn test_event(&self, content: &mut UiNode, update: &mut EventUpdate) -> ContextUpdates {
346 update.delivery_list_mut().fulfill_search([&WINDOW.info()].into_iter());
347 content.event(update);
348 WIDGET.test_root_updates();
349 UPDATES.apply()
350 }
351
352 pub fn test_update(&self, content: &mut UiNode, updates: Option<&mut WidgetUpdates>) -> ContextUpdates {
358 if let Some(updates) = updates {
359 updates.delivery_list_mut().fulfill_search([&WINDOW.info()].into_iter());
360 content.update(updates)
361 } else {
362 let target = if let Some(mut wgt) = content.as_widget() {
363 let content_id = wgt.id();
364 WidgetPath::new(WINDOW.id(), vec![WIDGET.id(), content_id].into())
365 } else {
366 WidgetPath::new(WINDOW.id(), vec![WIDGET.id()].into())
367 };
368
369 let mut updates = WidgetUpdates::new(UpdateDeliveryList::new_any());
370 updates.delivery_list.insert_wgt(&target);
371
372 content.update(&updates);
373 }
374 WIDGET.test_root_updates();
375 UPDATES.apply()
376 }
377
378 pub fn test_layout(&self, content: &mut UiNode, constraints: Option<PxConstraints2d>) -> (PxSize, ContextUpdates) {
382 let font_size = Length::pt_to_px(14.0, 1.0.fct());
383 let viewport = self.test_window_size();
384 let mut metrics = LayoutMetrics::new(1.fct(), viewport, font_size);
385 if let Some(c) = constraints {
386 metrics = metrics.with_constraints(c);
387 }
388 let mut updates = LayoutUpdates::new(UpdateDeliveryList::new_any());
389 updates.delivery_list.insert_updates_root(WINDOW.id(), WIDGET.id());
390 let size = LAYOUT.with_context(metrics, || {
391 crate::widget::info::WidgetLayout::with_root_widget(Arc::new(updates), |wl| content.layout(wl))
392 });
393 WIDGET.test_root_updates();
394 (size, UPDATES.apply())
395 }
396
397 pub fn test_layout_inline(
403 &self,
404 content: &mut UiNode,
405 measure_constraints: (PxConstraints2d, InlineConstraintsMeasure),
406 layout_constraints: (PxConstraints2d, InlineConstraintsLayout),
407 ) -> ((PxSize, PxSize), ContextUpdates) {
408 let font_size = Length::pt_to_px(14.0, 1.0.fct());
409 let viewport = self.test_window_size();
410
411 let metrics = LayoutMetrics::new(1.fct(), viewport, font_size)
412 .with_constraints(measure_constraints.0)
413 .with_inline_constraints(Some(InlineConstraints::Measure(measure_constraints.1)));
414 let measure_size = LAYOUT.with_context(metrics, || {
415 content.measure(&mut crate::widget::info::WidgetMeasure::new(Arc::default()))
416 });
417
418 let metrics = LayoutMetrics::new(1.fct(), viewport, font_size)
419 .with_constraints(layout_constraints.0)
420 .with_inline_constraints(Some(InlineConstraints::Layout(layout_constraints.1)));
421
422 let mut updates = LayoutUpdates::new(UpdateDeliveryList::new_any());
423 updates.delivery_list.insert_updates_root(WINDOW.id(), WIDGET.id());
424
425 let layout_size = LAYOUT.with_context(metrics, || {
426 crate::widget::info::WidgetLayout::with_root_widget(Arc::new(updates), |wl| content.layout(wl))
427 });
428 WIDGET.test_root_updates();
429 ((measure_size, layout_size), UPDATES.apply())
430 }
431
432 pub fn test_render(&self, content: &mut UiNode) -> (crate::render::BuiltFrame, ContextUpdates) {
436 use crate::render::*;
437
438 let mut frame = {
439 let win = WINDOW_CTX.get();
440 let wgt = WIDGET_CTX.get();
441
442 let frame_id = win.frame_id.load(Relaxed);
443 win.frame_id.store(frame_id.next(), Relaxed);
444
445 FrameBuilder::new_renderless(
446 Arc::default(),
447 Arc::default(),
448 frame_id,
449 wgt.id,
450 &wgt.bounds.lock(),
451 win.widget_tree.read().as_ref().unwrap(),
452 1.fct(),
453 FontAntiAliasing::Default,
454 )
455 };
456
457 frame.push_inner(self.test_root_translation_key(), false, |frame| {
458 content.render(frame);
459 });
460
461 let tree = WINDOW_CTX.get().widget_tree.read().as_ref().unwrap().clone();
462 let f = frame.finalize(&tree);
463 WIDGET.test_root_updates();
464 (f, UPDATES.apply())
465 }
466
467 pub fn test_render_update(&self, content: &mut UiNode) -> (crate::render::BuiltFrameUpdate, ContextUpdates) {
471 use crate::render::*;
472
473 let mut update = {
474 let win = WINDOW_CTX.get();
475 let wgt = WIDGET_CTX.get();
476
477 let frame_id = win.frame_id.load(Relaxed);
478 win.frame_id.store(frame_id.next_update(), Relaxed);
479
480 FrameUpdate::new(Arc::default(), frame_id, wgt.id, wgt.bounds.lock().clone(), colors::BLACK)
481 };
482
483 update.update_inner(self.test_root_translation_key(), false, |update| {
484 content.render_update(update);
485 });
486 let tree = WINDOW_CTX.get().widget_tree.read().as_ref().unwrap().clone();
487 let f = update.finalize(&tree);
488 WIDGET.test_root_updates();
489 (f, UPDATES.apply())
490 }
491
492 fn test_root_translation_key(&self) -> FrameValueKey<PxTransform> {
493 static_id! {
494 static ref ID: StateId<FrameValueKey<PxTransform>>;
495 }
496 WINDOW.with_state_mut(|mut s| *s.entry(*ID).or_insert_with(FrameValueKey::new_unique))
497 }
498 }
499}
500
501context_local! {
502 static WINDOW_CTX: WindowCtxData = WindowCtxData::no_context();
503}
504
505pub struct WindowCtx(Option<Arc<WindowCtxData>>);
509impl WindowCtx {
510 pub fn new(id: WindowId, mode: WindowMode) -> Self {
512 Self(Some(Arc::new(WindowCtxData {
513 id,
514 mode,
515 state: RwLock::new(OwnedStateMap::default()),
516 widget_tree: RwLock::new(None),
517
518 #[cfg(any(test, doc, feature = "test_util"))]
519 frame_id: atomic::Atomic::new(zng_view_api::window::FrameId::first()),
520 })))
521 }
522
523 pub fn set_widget_tree(&mut self, widget_tree: WidgetInfoTree) {
528 *self.0.as_mut().unwrap().widget_tree.write() = Some(widget_tree);
529 }
530
531 pub fn id(&self) -> WindowId {
533 self.0.as_ref().unwrap().id
534 }
535
536 pub fn mode(&self) -> WindowMode {
538 self.0.as_ref().unwrap().mode
539 }
540
541 pub fn widget_tree(&self) -> WidgetInfoTree {
543 self.0.as_ref().unwrap().widget_tree.read().as_ref().unwrap().clone()
544 }
545
546 pub fn with_state<R>(&mut self, f: impl FnOnce(&mut OwnedStateMap<WINDOW>) -> R) -> R {
548 f(&mut self.0.as_mut().unwrap().state.write())
549 }
550
551 pub fn share(&mut self) -> Self {
555 Self(self.0.clone())
556 }
557}
558
559struct WindowCtxData {
560 id: WindowId,
561 mode: WindowMode,
562 state: RwLock<OwnedStateMap<WINDOW>>,
563 widget_tree: RwLock<Option<WidgetInfoTree>>,
564
565 #[cfg(any(test, doc, feature = "test_util"))]
566 frame_id: atomic::Atomic<zng_view_api::window::FrameId>,
567}
568impl WindowCtxData {
569 #[track_caller]
570 fn no_context() -> Self {
571 panic!("no window in context")
572 }
573}
574
575#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
577pub enum WindowMode {
578 Headed,
580 Headless,
585 HeadlessWithRenderer,
588}
589impl fmt::Debug for WindowMode {
590 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
591 if f.alternate() {
592 write!(f, "WindowMode::")?;
593 }
594 match self {
595 WindowMode::Headed => write!(f, "Headed"),
596 WindowMode::Headless => write!(f, "Headless"),
597 WindowMode::HeadlessWithRenderer => write!(f, "HeadlessWithRenderer"),
598 }
599 }
600}
601impl WindowMode {
602 pub fn is_headed(self) -> bool {
604 match self {
605 WindowMode::Headed => true,
606 WindowMode::Headless | WindowMode::HeadlessWithRenderer => false,
607 }
608 }
609
610 pub fn is_headless(self) -> bool {
612 match self {
613 WindowMode::Headless | WindowMode::HeadlessWithRenderer => true,
614 WindowMode::Headed => false,
615 }
616 }
617
618 pub fn has_renderer(self) -> bool {
620 match self {
621 WindowMode::Headed | WindowMode::HeadlessWithRenderer => true,
622 WindowMode::Headless => false,
623 }
624 }
625}