zng_app/
window.rs

1//! Window context API.
2
3use 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    /// Unique identifier of an open window.
19    ///
20    /// Can be obtained from [`WINDOW.id`] inside a window.
21    ///
22    /// # Name
23    ///
24    /// IDs are only unique for the same process.
25    /// You can associate a [`name`] with an ID to give it a persistent identifier.
26    ///
27    /// [`WINDOW.id`]: crate::window::WINDOW::id
28    /// [`name`]: WindowId::name
29    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    /// Calls [`WindowId::named`].
37    fn from(name: &'static str) -> WindowId {
38        WindowId::named(name)
39    }
40    /// Calls [`WindowId::named`].
41    fn from(name: String) -> WindowId {
42        WindowId::named(name)
43    }
44    /// Calls [`WindowId::named`].
45    fn from(name: Cow<'static, str>) -> WindowId {
46        WindowId::named(name)
47    }
48    /// Calls [`WindowId::named`].
49    fn from(name: char) -> WindowId {
50        WindowId::named(name)
51    }
52    /// Calls [`WindowId::named`].
53    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    /// Unique identifier of a monitor screen.
84    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    /// ID of a fake monitor for cases where no monitor is available.
106    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
112/// Current context window.
113///
114/// This represents the minimum features required for a window context, see `WINDOW_Ext` for more
115/// features provided by the default window implementation.
116///
117/// # Panics
118///
119/// Most of the methods on this service panic if not called inside a window context.
120pub struct WINDOW;
121impl WINDOW {
122    /// Returns `true` if called inside a window.
123    pub fn is_in_window(&self) -> bool {
124        !WINDOW_CTX.is_default()
125    }
126
127    /// Gets the window ID, if called inside a window.
128    pub fn try_id(&self) -> Option<WindowId> {
129        if WINDOW_CTX.is_default() { None } else { Some(WINDOW_CTX.get().id) }
130    }
131
132    /// Gets the window info tree if called in a window context and the window has already inited.
133    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    /// Gets the window ID.
141    pub fn id(&self) -> WindowId {
142        WINDOW_CTX.get().id
143    }
144
145    /// Gets the window mode.
146    pub fn mode(&self) -> WindowMode {
147        WINDOW_CTX.get().mode
148    }
149
150    /// Gets the window info tree.
151    ///
152    /// Panics if called before the window future yields the window.
153    pub fn info(&self) -> WidgetInfoTree {
154        WINDOW_CTX.get().widget_tree.read().clone().expect("window not init")
155    }
156
157    /// Calls `f` with a read lock on the current window state map.
158    pub fn with_state<R>(&self, f: impl FnOnce(StateMapRef<WINDOW>) -> R) -> R {
159        f(WINDOW_CTX.get().state.read().borrow())
160    }
161
162    /// Calls `f` with a write lock on the current window state map.
163    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    /// Get the window state `id`, if it is set.
168    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    /// Require the window state `id`.
174    ///
175    /// Panics if the `id` is not set.
176    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    /// Set the window state `id` to `value`.
182    ///
183    /// Returns the previous set value.
184    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    /// Sets the window state `id` without value.
191    ///
192    /// Returns if the state `id` was already flagged.
193    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    /// Calls `init` and sets `id` if the `id` is not already set in the widget.
199    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    /// Sets the `id` to the default value if it is not already set.
207    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    /// Returns `true` if the `id` is set or flagged in the window.
212    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    /// Calls `f` while the window is set to `ctx`.
218    #[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    /// Calls `f` while no window is available in the context.
231    #[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/// Test only methods.
238#[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    /// Window test helpers.
269    ///
270    /// # Panics
271    ///
272    /// Most of the test methods panic if not called inside [`with_test_context`].
273    ///
274    /// [`with_test_context`]: WINDOW::with_test_context
275    impl WINDOW {
276        /// Calls `f` inside a new headless window and root widget.
277        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        /// Get the test window size.
296        ///
297        /// This size is used by the `test_*` methods that need a window size.
298        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        /// Set test window `size`.
303        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        /// Call inside [`with_test_context`] to init the `content` as a child of the test window root.
310        ///
311        /// [`with_test_context`]: Self::with_test_context
312        pub fn test_init(&self, content: &mut UiNode) -> ContextUpdates {
313            content.init();
314            WIDGET.test_root_updates();
315            UPDATES.apply()
316        }
317
318        /// Call inside [`with_test_context`] to deinit the `content` as a child of the test window root.
319        ///
320        /// [`with_test_context`]: Self::with_test_context
321        pub fn test_deinit(&self, content: &mut UiNode) -> ContextUpdates {
322            content.deinit();
323            WIDGET.test_root_updates();
324            UPDATES.apply()
325        }
326
327        /// Call inside [`with_test_context`] to rebuild info the `content` as a child of the test window root.
328        ///
329        /// [`with_test_context`]: Self::with_test_context
330        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        /// Call inside [`with_test_context`] to update the `content` as a child of the test window root.
349        ///
350        /// The `updates` can be set to a custom delivery list, otherwise window root and `content` widget are flagged for update.
351        ///
352        /// [`with_test_context`]: Self::with_test_context
353        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        /// Call inside [`with_test_context`] to layout the `content` as a child of the test window root.
375        ///
376        /// [`with_test_context`]: Self::with_test_context
377        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        /// Call inside [`with_test_context`] to layout the `content` as a child of the test window root.
394        ///
395        /// Returns the measure and layout size, and the requested updates.
396        ///
397        /// [`with_test_context`]: Self::with_test_context
398        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        /// Call inside [`with_test_context`] to render the `content` as a child of the test window root.
429        ///
430        /// [`with_test_context`]: Self::with_test_context
431        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        /// Call inside [`with_test_context`] to render_update the `content` as a child of the test window root.
464        ///
465        /// [`with_test_context`]: Self::with_test_context
466        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
501/// Defines the backing data of [`WINDOW`].
502///
503/// Each window owns this data and calls [`WINDOW.with_context`](WINDOW::with_context) to delegate to it's child node.
504pub struct WindowCtx(Option<Arc<WindowCtxData>>);
505impl WindowCtx {
506    /// New window context.
507    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    /// Sets the widget tree, must be called after every info update.
520    ///
521    /// Window contexts are partially available in the window new closure, but values like the `widget_tree` is
522    /// available on init, so a [`WidgetInfoTree::wgt`] must be set as soon as the window and widget ID are available.
523    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    /// Gets the window ID.
528    pub fn id(&self) -> WindowId {
529        self.0.as_ref().unwrap().id
530    }
531
532    /// Gets the window mode.
533    pub fn mode(&self) -> WindowMode {
534        self.0.as_ref().unwrap().mode
535    }
536
537    /// Gets the window tree.
538    pub fn widget_tree(&self) -> WidgetInfoTree {
539        self.0.as_ref().unwrap().widget_tree.read().as_ref().unwrap().clone()
540    }
541
542    /// Call `f` with an exclusive lock to the window state.
543    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    /// Clone a reference to the window context.
548    ///
549    /// This must be used only if the window implementation is split.
550    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/// Mode of an open window.
572#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
573pub enum WindowMode {
574    /// Normal mode, shows a system window with content rendered.
575    Headed,
576    /// Headless mode, no system window and no renderer. The window does layout and calls [`UiNode::render`], but
577    /// it does not actually generates frame pixels.
578    ///
579    /// [`UiNode::render`]: crate::widget::node::UiNode::render
580    Headless,
581    /// Headless mode, no visible system window but with a renderer. The window does everything a [`Headed`](WindowMode::Headed)
582    /// window does, except presenting the frame in a system window.
583    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    /// If it is the [`Headed`](WindowMode::Headed) mode.
599    pub fn is_headed(self) -> bool {
600        match self {
601            WindowMode::Headed => true,
602            WindowMode::Headless | WindowMode::HeadlessWithRenderer => false,
603        }
604    }
605
606    /// If it is the [`Headless`](WindowMode::Headed) or [`HeadlessWithRenderer`](WindowMode::Headed) modes.
607    pub fn is_headless(self) -> bool {
608        match self {
609            WindowMode::Headless | WindowMode::HeadlessWithRenderer => true,
610            WindowMode::Headed => false,
611        }
612    }
613
614    /// If it is the [`Headed`](WindowMode::Headed) or [`HeadlessWithRenderer`](WindowMode::HeadlessWithRenderer) modes.
615    pub fn has_renderer(self) -> bool {
616        match self {
617            WindowMode::Headed | WindowMode::HeadlessWithRenderer => true,
618            WindowMode::Headless => false,
619        }
620    }
621}
622
623/// Integration with the `WINDOWS` service implementer.
624#[allow(non_camel_case_types)]
625pub struct WINDOWS_APP;
626impl WINDOWS_APP {
627    /// Connect with the `WINDOWS` info storage.
628    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    /// Get the latest info tree for the window.
642    pub fn widget_tree(&self, id: WindowId) -> Option<WidgetInfoTree> {
643        self.with(|s| s.widget_tree(id)).flatten()
644    }
645
646    /// Search for the widget in the latest info tree of each open window.
647    pub fn widget_info(&self, id: WidgetId) -> Option<WidgetInfo> {
648        self.with(|s| s.widget_info(id)).flatten()
649    }
650
651    /// Rebuild info trees.
652    pub fn update_info(&self, info_widgets: &mut InfoUpdates) {
653        self.with(|s| s.update_info(info_widgets));
654    }
655
656    /// Update widgets.
657    pub fn update_widgets(&self, update_widgets: &mut WidgetUpdates) {
658        self.with(|s| s.update_widgets(update_widgets));
659    }
660
661    /// Layout windows and widgets.
662    pub fn update_layout(&self, layout_widgets: &mut LayoutUpdates) {
663        self.with(|s| s.update_layout(layout_widgets));
664    }
665
666    /// Update frames.
667    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
672/// Represents the `WINDOWS` service.
673pub trait WindowsService: Any + Send + Sync {
674    /// Get the latest info tree for the window.
675    fn widget_tree(&self, id: WindowId) -> Option<WidgetInfoTree>;
676    /// Search for the widget in the latest info tree of each open window.
677    fn widget_info(&self, id: WidgetId) -> Option<WidgetInfo>;
678    /// Rebuild info trees.
679    fn update_info(&self, updates: &mut InfoUpdates);
680    /// Update window and widgets.
681    fn update_widgets(&self, updates: &mut WidgetUpdates);
682    /// Layout windows and widgets.
683    fn update_layout(&self, updates: &mut LayoutUpdates);
684    /// Update frames.
685    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}