zng_app/
lib.rs

1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3//!
4//! App process implementation.
5//!
6//! # Widget Instantiation
7//!
8//! See [`enable_widget_macros!`] if you want to instantiate widgets without depending on the `zng` crate.
9//!
10//! # Crate
11//!
12#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
13#![recursion_limit = "256"]
14// suppress nag about very simple boxed closure signatures.
15#![expect(clippy::type_complexity)]
16#![warn(unused_extern_crates)]
17#![warn(missing_docs)]
18
19use std::{
20    any::{TypeId, type_name},
21    collections::HashMap,
22    fmt, ops,
23    path::PathBuf,
24    sync::Arc,
25};
26
27pub mod access;
28pub mod crash_handler;
29pub mod event;
30pub mod handler;
31pub mod memory_profiler;
32pub mod render;
33pub mod shortcut;
34pub mod third_party;
35pub mod timer;
36pub mod trace_recorder;
37pub mod update;
38pub mod view_process;
39pub mod widget;
40pub mod window;
41
42mod tests;
43
44use view_process::VIEW_PROCESS;
45use widget::UiTaskWidget;
46#[doc(hidden)]
47pub use zng_layout as layout;
48use zng_txt::Txt;
49#[doc(hidden)]
50pub use zng_var as var;
51use zng_var::Var;
52
53pub use zng_time::{DInstant, Deadline, INSTANT, InstantMode};
54
55use update::{EventUpdate, InfoUpdates, LayoutUpdates, RenderUpdates, UPDATES, UpdatesTrace, WidgetUpdates};
56use window::WindowMode;
57use zng_app_context::{AppId, AppScope, LocalContext};
58use zng_task::UiTask;
59
60pub use zng_unique_id::static_id;
61
62/// Enable widget instantiation in crates that can't depend on the `zng` crate.
63///
64/// This must be called at the top of the crate:
65///
66/// ```
67/// // in lib.rs or main.rs
68/// # use zng_app::*;
69/// enable_widget_macros!();
70/// ```
71#[macro_export]
72macro_rules! enable_widget_macros {
73    () => {
74        #[doc(hidden)]
75        #[allow(unused_extern_crates)]
76        extern crate self as zng;
77
78        #[doc(hidden)]
79        pub use $crate::__proc_macro_util;
80    };
81}
82
83#[doc(hidden)]
84#[allow(unused_extern_crates)]
85extern crate self as zng;
86
87#[doc(hidden)]
88#[allow(unused_extern_crates)]
89extern crate self as zng_app; // for doc-tests
90
91#[doc(hidden)]
92pub mod __proc_macro_util {
93    // * don't add glob re-exports, the types leak in rust-analyzer even if all is doc(hidden).
94    // * don't use macro_rules! macros that use $crate , they will fail with "unresolved import" when used from the re-exports.
95
96    #[doc(hidden)]
97    pub use zng_unique_id::static_id;
98
99    #[doc(hidden)]
100    pub mod widget {
101        #[doc(hidden)]
102        pub mod builder {
103            #[doc(hidden)]
104            pub use crate::widget::builder::{
105                AnyArcHandler, HandlerInWhenExprError, Importance, InputKind, PropertyArgs, PropertyId, PropertyInfo, PropertyInput,
106                PropertyInputTypes, PropertyNewArgs, SourceLocation, UiNodeInWhenExprError, WgtInfo, WhenInput, WhenInputMember,
107                WhenInputVar, WidgetBuilding, WidgetType, handler_to_args, iter_input_attributes, nest_group_items, new_dyn_handler,
108                new_dyn_other, new_dyn_ui_node, new_dyn_var, panic_input, ui_node_to_args, value_to_args, var_getter, var_state,
109                var_to_args,
110            };
111        }
112
113        #[doc(hidden)]
114        pub mod base {
115            pub use crate::widget::base::{NonWidgetBase, WidgetBase, WidgetExt, WidgetImpl};
116        }
117
118        #[doc(hidden)]
119        pub mod node {
120            pub use crate::widget::node::{ArcNode, IntoUiNode, UiNode};
121        }
122
123        #[doc(hidden)]
124        pub mod info {
125            pub use crate::widget::info::{WidgetInfoBuilder, WidgetLayout, WidgetMeasure};
126        }
127
128        #[doc(hidden)]
129        pub use crate::widget::{easing_property, widget_new};
130
131        #[doc(hidden)]
132        pub use crate::widget::WIDGET;
133    }
134
135    #[doc(hidden)]
136    pub mod update {
137        pub use crate::update::{EventUpdate, WidgetUpdates};
138    }
139
140    #[doc(hidden)]
141    pub mod layout {
142        #[doc(hidden)]
143        pub mod unit {
144            #[doc(hidden)]
145            pub use crate::layout::unit::{PxSize, TimeUnits};
146        }
147
148        #[doc(hidden)]
149        pub mod context {
150            #[doc(hidden)]
151            pub use crate::layout::context::LAYOUT;
152        }
153    }
154
155    #[doc(hidden)]
156    pub mod render {
157        pub use crate::render::{FrameBuilder, FrameUpdate};
158    }
159
160    #[doc(hidden)]
161    pub mod handler {
162        #[doc(hidden)]
163        pub use crate::handler::{ArcHandler, hn};
164    }
165
166    #[doc(hidden)]
167    pub mod var {
168        #[doc(hidden)]
169        pub use crate::var::{AnyVar, AnyVarValue, Var, expr_var};
170
171        #[doc(hidden)]
172        pub mod animation {
173            #[doc(hidden)]
174            pub mod easing {
175                #[doc(hidden)]
176                pub use crate::var::animation::easing::{
177                    back, bounce, circ, cubic, cubic_bezier, ease_in, ease_in_out, ease_out, ease_out_in, elastic, expo, linear, none,
178                    quad, quart, quint, reverse, reverse_out, sine, step_ceil, step_floor,
179                };
180            }
181        }
182    }
183}
184
185/// An app extension.
186///
187/// App extensions setup and update core features such as services and events. App instances
188/// are fully composed of app extensions.
189///
190/// See the `zng::app` module level documentation for more details, including the call order of methods
191/// of this trait.
192pub trait AppExtension: 'static {
193    /// Register info abound this extension on the info list.
194    #[inline(always)]
195    fn register(&self, info: &mut AppExtensionsInfo)
196    where
197        Self: Sized,
198    {
199        info.push::<Self>()
200    }
201
202    /// Initializes this extension.
203    #[inline(always)]
204    fn init(&mut self) {}
205
206    /// Called just before [`event_ui`](Self::event_ui) when an event notifies.
207    ///
208    /// Extensions can handle this method to intercept event updates before the UI.
209    ///
210    /// Note that this is not related to the `on_event_preview` properties, all UI events
211    /// happen in `event_ui`.
212    #[inline(always)]
213    fn event_preview(&mut self, update: &mut EventUpdate) {
214        let _ = update;
215    }
216
217    /// Called just before [`event`](Self::event).
218    ///
219    /// Only extensions that generate windows should handle this method. The [`UiNode::event`](crate::widget::node::UiNode::event)
220    /// method is called here.
221    #[inline(always)]
222    fn event_ui(&mut self, update: &mut EventUpdate) {
223        let _ = update;
224    }
225
226    /// Called after [`event_ui`](Self::event_ui).
227    ///
228    /// This is the general extensions event handler, it gives the chance for the UI to signal stop propagation.
229    #[inline(always)]
230    fn event(&mut self, update: &mut EventUpdate) {
231        let _ = update;
232    }
233
234    /// Called when info rebuild is requested for windows and widgets.
235    ///
236    /// The [`UiNode::info`] method is called here.
237    ///
238    /// [`UiNode::info`]: crate::widget::node::UiNode::info
239    #[inline(always)]
240    fn info(&mut self, info_widgets: &mut InfoUpdates) {
241        let _ = info_widgets;
242    }
243
244    /// Called just before [`update_ui`](Self::update_ui).
245    ///
246    /// Extensions can handle this method to react to updates before the UI.
247    ///
248    /// Note that this is not related to the `on_event_preview` properties, all UI events
249    /// happen in `update_ui`.
250    #[inline(always)]
251    fn update_preview(&mut self) {}
252
253    /// Called just before [`update`](Self::update).
254    ///
255    /// Only extensions that manage windows should handle this method.
256    ///
257    /// The [`UiNode::update`] method is called here.
258    ///
259    /// [`UiNode::update`]: crate::widget::node::UiNode::update
260    #[inline(always)]
261    fn update_ui(&mut self, update_widgets: &mut WidgetUpdates) {
262        let _ = update_widgets;
263    }
264
265    /// Called after every [`update_ui`](Self::update_ui) and [`info`](Self::info).
266    ///
267    /// This is the general extensions update, it gives the chance for
268    /// the UI to make service requests.
269    #[inline(always)]
270    fn update(&mut self) {}
271
272    /// Called when layout is requested for windows and widgets.
273    ///
274    /// The [`UiNode::layout`] method is called here.
275    ///
276    /// [`UiNode::layout`]: crate::widget::node::UiNode::layout
277    #[inline(always)]
278    fn layout(&mut self, layout_widgets: &mut LayoutUpdates) {
279        let _ = layout_widgets;
280    }
281
282    /// Called when render is requested for windows and widgets.
283    ///
284    /// The [`UiNode::render`] and [`UiNode::render_update`] methods are called here.
285    ///
286    /// [`UiNode::render`]: crate::widget::node::UiNode::render
287    /// [`UiNode::render_update`]: crate::widget::node::UiNode::render_update
288    #[inline(always)]
289    fn render(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
290        let _ = (render_widgets, render_update_widgets);
291    }
292
293    /// Called when the application is exiting.
294    ///
295    /// Update requests and event notifications generated during this call are ignored,
296    /// the extensions will be dropped after every extension received this call.
297    #[inline(always)]
298    fn deinit(&mut self) {}
299
300    /// Gets the extension boxed.
301    ///
302    /// Boxed app extensions also implement `AppExtension`, this method does not double box.
303    #[inline(always)]
304    fn boxed(self) -> Box<dyn AppExtensionBoxed>
305    where
306        Self: Sized,
307    {
308        Box::new(self)
309    }
310}
311
312/// Boxed version of [`AppExtension`].
313#[doc(hidden)]
314pub trait AppExtensionBoxed: 'static {
315    fn register_boxed(&self, info: &mut AppExtensionsInfo);
316    fn init_boxed(&mut self);
317    fn update_preview_boxed(&mut self);
318    fn update_ui_boxed(&mut self, updates: &mut WidgetUpdates);
319    fn update_boxed(&mut self);
320    fn event_preview_boxed(&mut self, update: &mut EventUpdate);
321    fn event_ui_boxed(&mut self, update: &mut EventUpdate);
322    fn event_boxed(&mut self, update: &mut EventUpdate);
323    fn info_boxed(&mut self, info_widgets: &mut InfoUpdates);
324    fn layout_boxed(&mut self, layout_widgets: &mut LayoutUpdates);
325    fn render_boxed(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates);
326    fn deinit_boxed(&mut self);
327}
328impl<T: AppExtension> AppExtensionBoxed for T {
329    fn register_boxed(&self, info: &mut AppExtensionsInfo) {
330        self.register(info);
331    }
332
333    fn init_boxed(&mut self) {
334        self.init();
335    }
336
337    fn update_preview_boxed(&mut self) {
338        self.update_preview();
339    }
340
341    fn update_ui_boxed(&mut self, updates: &mut WidgetUpdates) {
342        self.update_ui(updates);
343    }
344
345    fn info_boxed(&mut self, info_widgets: &mut InfoUpdates) {
346        self.info(info_widgets);
347    }
348
349    fn update_boxed(&mut self) {
350        self.update();
351    }
352
353    fn event_preview_boxed(&mut self, update: &mut EventUpdate) {
354        self.event_preview(update);
355    }
356
357    fn event_ui_boxed(&mut self, update: &mut EventUpdate) {
358        self.event_ui(update);
359    }
360
361    fn event_boxed(&mut self, update: &mut EventUpdate) {
362        self.event(update);
363    }
364
365    fn layout_boxed(&mut self, layout_widgets: &mut LayoutUpdates) {
366        self.layout(layout_widgets);
367    }
368
369    fn render_boxed(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
370        self.render(render_widgets, render_update_widgets);
371    }
372
373    fn deinit_boxed(&mut self) {
374        self.deinit();
375    }
376}
377impl AppExtension for Box<dyn AppExtensionBoxed> {
378    fn register(&self, info: &mut AppExtensionsInfo) {
379        self.as_ref().register_boxed(info);
380    }
381
382    fn init(&mut self) {
383        self.as_mut().init_boxed();
384    }
385
386    fn update_preview(&mut self) {
387        self.as_mut().update_preview_boxed();
388    }
389
390    fn update_ui(&mut self, update_widgets: &mut WidgetUpdates) {
391        self.as_mut().update_ui_boxed(update_widgets);
392    }
393
394    fn update(&mut self) {
395        self.as_mut().update_boxed();
396    }
397
398    fn event_preview(&mut self, update: &mut EventUpdate) {
399        self.as_mut().event_preview_boxed(update);
400    }
401
402    fn event_ui(&mut self, update: &mut EventUpdate) {
403        self.as_mut().event_ui_boxed(update);
404    }
405
406    fn event(&mut self, update: &mut EventUpdate) {
407        self.as_mut().event_boxed(update);
408    }
409
410    fn info(&mut self, info_widgets: &mut InfoUpdates) {
411        self.as_mut().info_boxed(info_widgets);
412    }
413
414    fn layout(&mut self, layout_widgets: &mut LayoutUpdates) {
415        self.as_mut().layout_boxed(layout_widgets);
416    }
417
418    fn render(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
419        self.as_mut().render_boxed(render_widgets, render_update_widgets);
420    }
421
422    fn deinit(&mut self) {
423        self.as_mut().deinit_boxed();
424    }
425
426    fn boxed(self) -> Box<dyn AppExtensionBoxed>
427    where
428        Self: Sized,
429    {
430        self
431    }
432}
433
434struct TraceAppExt<E: AppExtension>(E);
435impl<E: AppExtension> AppExtension for TraceAppExt<E> {
436    fn register(&self, info: &mut AppExtensionsInfo) {
437        self.0.register(info)
438    }
439
440    fn init(&mut self) {
441        let _span = UpdatesTrace::extension_span::<E>("init");
442        self.0.init();
443    }
444
445    fn event_preview(&mut self, update: &mut EventUpdate) {
446        let _span = UpdatesTrace::extension_span::<E>("event_preview");
447        self.0.event_preview(update);
448    }
449
450    fn event_ui(&mut self, update: &mut EventUpdate) {
451        let _span = UpdatesTrace::extension_span::<E>("event_ui");
452        self.0.event_ui(update);
453    }
454
455    fn event(&mut self, update: &mut EventUpdate) {
456        let _span = UpdatesTrace::extension_span::<E>("event");
457        self.0.event(update);
458    }
459
460    fn update_preview(&mut self) {
461        let _span = UpdatesTrace::extension_span::<E>("update_preview");
462        self.0.update_preview();
463    }
464
465    fn update_ui(&mut self, update_widgets: &mut WidgetUpdates) {
466        let _span = UpdatesTrace::extension_span::<E>("update_ui");
467        self.0.update_ui(update_widgets);
468    }
469
470    fn update(&mut self) {
471        let _span = UpdatesTrace::extension_span::<E>("update");
472        self.0.update();
473    }
474
475    fn info(&mut self, info_widgets: &mut InfoUpdates) {
476        let _span = UpdatesTrace::extension_span::<E>("info");
477        self.0.info(info_widgets);
478    }
479
480    fn layout(&mut self, layout_widgets: &mut LayoutUpdates) {
481        let _span = UpdatesTrace::extension_span::<E>("layout");
482        self.0.layout(layout_widgets);
483    }
484
485    fn render(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
486        let _span = UpdatesTrace::extension_span::<E>("render");
487        self.0.render(render_widgets, render_update_widgets);
488    }
489
490    fn deinit(&mut self) {
491        let _span = UpdatesTrace::extension_span::<E>("deinit");
492        self.0.deinit();
493    }
494
495    fn boxed(self) -> Box<dyn AppExtensionBoxed>
496    where
497        Self: Sized,
498    {
499        Box::new(self)
500    }
501}
502
503/// Info about an app-extension.
504///
505/// See [`APP::extensions`] for more details.
506#[derive(Clone, Copy)]
507#[non_exhaustive]
508pub struct AppExtensionInfo {
509    /// Extension type ID.
510    pub type_id: TypeId,
511    /// Extension type name.
512    pub type_name: &'static str,
513}
514impl PartialEq for AppExtensionInfo {
515    fn eq(&self, other: &Self) -> bool {
516        self.type_id == other.type_id
517    }
518}
519impl fmt::Debug for AppExtensionInfo {
520    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
521        write!(f, "{}", self.type_name)
522    }
523}
524impl Eq for AppExtensionInfo {}
525impl AppExtensionInfo {
526    /// New info for `E`.
527    pub fn new<E: AppExtension>() -> Self {
528        Self {
529            type_id: TypeId::of::<E>(),
530            type_name: type_name::<E>(),
531        }
532    }
533}
534
535/// List of app-extensions that are part of an app.
536#[derive(Clone, PartialEq)]
537pub struct AppExtensionsInfo {
538    infos: Vec<AppExtensionInfo>,
539}
540impl fmt::Debug for AppExtensionsInfo {
541    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
542        f.debug_list().entries(&self.infos).finish()
543    }
544}
545impl AppExtensionsInfo {
546    pub(crate) fn start() -> Self {
547        Self { infos: vec![] }
548    }
549
550    /// Push the extension info.
551    pub fn push<E: AppExtension>(&mut self) {
552        let info = AppExtensionInfo::new::<E>();
553        assert!(!self.contains::<E>(), "app-extension `{info:?}` is already in the list");
554        self.infos.push(info);
555    }
556
557    /// Gets if the extension `E` is in the list.
558    pub fn contains<E: AppExtension>(&self) -> bool {
559        self.contains_info(AppExtensionInfo::new::<E>())
560    }
561
562    /// Gets i the extension is in the list.
563    pub fn contains_info(&self, info: AppExtensionInfo) -> bool {
564        self.infos.iter().any(|e| e.type_id == info.type_id)
565    }
566
567    /// Panics if the extension `E` is not present.
568    #[track_caller]
569    pub fn require<E: AppExtension>(&self) {
570        let info = AppExtensionInfo::new::<E>();
571        if !self.contains_info(info) {
572            let mut note = "";
573            if !APP.is_running() {
574                if APP.is_started() {
575                    note = "\nnote: the app is not running yet";
576                } else {
577                    note = "\nnote: no app is started in the current thread";
578                }
579            }
580            panic!("app-extension `{info:?}` is required{note}")
581        }
582    }
583}
584impl ops::Deref for AppExtensionsInfo {
585    type Target = [AppExtensionInfo];
586
587    fn deref(&self) -> &Self::Target {
588        &self.infos
589    }
590}
591
592/// Desired next step of app main loop.
593#[derive(Copy, Clone, Debug, PartialEq, Eq)]
594#[must_use = "methods that return `AppControlFlow` expect to be inside a controlled loop"]
595pub enum AppControlFlow {
596    /// Immediately try to receive more app events.
597    Poll,
598    /// Sleep until an app event is received.
599    ///
600    /// Note that a deadline might be set in case a timer is running.
601    Wait,
602    /// Exit the loop and drop the app.
603    Exit,
604}
605impl AppControlFlow {
606    /// Assert that the value is [`AppControlFlow::Wait`].
607    #[track_caller]
608    pub fn assert_wait(self) {
609        assert_eq!(AppControlFlow::Wait, self)
610    }
611
612    /// Assert that the value is [`AppControlFlow::Exit`].
613    #[track_caller]
614    pub fn assert_exit(self) {
615        assert_eq!(AppControlFlow::Exit, self)
616    }
617}
618
619/// A headless app controller.
620///
621/// Headless apps don't cause external side-effects like visible windows and don't listen to system events.
622/// They can be used for creating apps like a command line app that renders widgets, or for creating integration tests.
623///
624/// You can start a headless app using [`AppExtended::run_headless`].
625pub struct HeadlessApp {
626    app: RunningApp<Box<dyn AppExtensionBoxed>>,
627}
628impl HeadlessApp {
629    /// If headless rendering is enabled.
630    ///
631    /// When enabled windows are still not visible but frames will be rendered and the frame
632    /// image can be requested.
633    ///
634    /// Note that [`UiNode::render`] is still called when a renderer is disabled and you can still
635    /// query the latest frame from `WINDOWS.widget_tree`. The only thing that
636    /// is disabled is the actual renderer that converts display lists to pixels.
637    ///
638    /// [`UiNode::render`]: crate::widget::node::UiNode::render
639    pub fn renderer_enabled(&mut self) -> bool {
640        VIEW_PROCESS.is_available()
641    }
642
643    /// Does updates unobserved.
644    ///
645    /// See [`update_observed`] for more details.
646    ///
647    /// [`update_observed`]: HeadlessApp::update
648    pub fn update(&mut self, wait_app_event: bool) -> AppControlFlow {
649        self.update_observed(&mut (), wait_app_event)
650    }
651
652    /// Does updates observing [`update`] only.
653    ///
654    /// See [`update_observed`] for more details.
655    ///
656    /// [`update`]: AppEventObserver::update
657    /// [`update_observed`]: HeadlessApp::update
658    pub fn update_observe(&mut self, on_update: impl FnMut(), wait_app_event: bool) -> AppControlFlow {
659        struct Observer<F>(F);
660        impl<F: FnMut()> AppEventObserver for Observer<F> {
661            fn update(&mut self) {
662                (self.0)()
663            }
664        }
665        let mut observer = Observer(on_update);
666
667        self.update_observed(&mut observer, wait_app_event)
668    }
669
670    /// Does updates observing [`event`] only.
671    ///
672    /// See [`update_observed`] for more details.
673    ///
674    /// [`event`]: AppEventObserver::event
675    /// [`update_observed`]: HeadlessApp::update
676    pub fn update_observe_event(&mut self, on_event: impl FnMut(&mut EventUpdate), wait_app_event: bool) -> AppControlFlow {
677        struct Observer<F>(F);
678        impl<F: FnMut(&mut EventUpdate)> AppEventObserver for Observer<F> {
679            fn event(&mut self, update: &mut EventUpdate) {
680                (self.0)(update);
681            }
682        }
683        let mut observer = Observer(on_event);
684        self.update_observed(&mut observer, wait_app_event)
685    }
686
687    /// Does updates with an [`AppEventObserver`].
688    ///
689    /// If `wait_app_event` is `true` the thread sleeps until at least one app event is received or a timer elapses,
690    /// if it is `false` only responds to app events already in the buffer.
691    pub fn update_observed<O: AppEventObserver>(&mut self, observer: &mut O, mut wait_app_event: bool) -> AppControlFlow {
692        if self.app.has_exited() {
693            return AppControlFlow::Exit;
694        }
695
696        loop {
697            match self.app.poll(wait_app_event, observer) {
698                AppControlFlow::Poll => {
699                    wait_app_event = false;
700                    continue;
701                }
702                flow => return flow,
703            }
704        }
705    }
706
707    /// Execute the async `task` in the UI thread, updating the app until it finishes or the app shuts-down.
708    ///
709    /// Returns the task result if the app has not shut-down.
710    pub fn run_task<R, T>(&mut self, task: impl IntoFuture<IntoFuture = T>) -> Option<R>
711    where
712        R: 'static,
713        T: Future<Output = R> + Send + Sync + 'static,
714    {
715        let mut task = UiTask::new(None, task);
716
717        let mut flow = self.update_observe(
718            || {
719                task.update();
720            },
721            false,
722        );
723
724        if task.update().is_some() {
725            let r = task.into_result().ok();
726            debug_assert!(r.is_some());
727            return r;
728        }
729
730        let mut n = 0;
731        while flow != AppControlFlow::Exit {
732            flow = self.update_observe(
733                || {
734                    task.update();
735                },
736                true,
737            );
738
739            if n == 10_000 {
740                tracing::error!("excessive future awaking, run_task ran 10_000 update cycles without finishing");
741            } else if n == 100_000 {
742                panic!("run_task stuck, ran 100_000 update cycles without finishing");
743            }
744            n += 1;
745
746            match task.into_result() {
747                Ok(r) => return Some(r),
748                Err(t) => task = t,
749            }
750        }
751        task.cancel();
752
753        None
754    }
755
756    /// Requests and wait for app exit.
757    ///
758    /// Forces deinit if exit is cancelled.
759    pub fn exit(mut self) {
760        self.run_task(async move {
761            let req = APP.exit();
762            req.wait_rsp().await;
763        });
764    }
765
766    /// If the app has exited.
767    ///
768    /// Exited apps cannot update anymore. The app should be dropped to unload the app scope.
769    pub fn has_exited(&self) -> bool {
770        self.app.has_exited()
771    }
772}
773
774/// Observer for [`HeadlessApp::update_observed`].
775///
776/// This works like a temporary app extension that runs only for the update call.
777pub trait AppEventObserver {
778    /// Called for each raw event received.
779    fn raw_event(&mut self, ev: &zng_view_api::Event) {
780        let _ = ev;
781    }
782
783    /// Called just after [`AppExtension::event_preview`].
784    fn event_preview(&mut self, update: &mut EventUpdate) {
785        let _ = update;
786    }
787
788    /// Called just after [`AppExtension::event_ui`].
789    fn event_ui(&mut self, update: &mut EventUpdate) {
790        let _ = update;
791    }
792
793    /// Called just after [`AppExtension::event`].
794    fn event(&mut self, update: &mut EventUpdate) {
795        let _ = update;
796    }
797
798    /// Called just after [`AppExtension::update_preview`].
799    fn update_preview(&mut self) {}
800
801    /// Called just after [`AppExtension::update_ui`].
802    fn update_ui(&mut self, update_widgets: &mut WidgetUpdates) {
803        let _ = update_widgets;
804    }
805
806    /// Called just after [`AppExtension::update`].
807    fn update(&mut self) {}
808
809    /// Called just after [`AppExtension::info`].
810    fn info(&mut self, info_widgets: &mut InfoUpdates) {
811        let _ = info_widgets;
812    }
813
814    /// Called just after [`AppExtension::layout`].
815    fn layout(&mut self, layout_widgets: &mut LayoutUpdates) {
816        let _ = layout_widgets;
817    }
818
819    /// Called just after [`AppExtension::render`].
820    fn render(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
821        let _ = (render_widgets, render_update_widgets);
822    }
823
824    /// Cast to dynamically dispatched observer, this can help avoid code bloat.
825    ///
826    /// The app methods that accept observers automatically use this method if the feature `"dyn_app_extension"` is active.
827    fn as_dyn(&mut self) -> DynAppEventObserver<'_>
828    where
829        Self: Sized,
830    {
831        DynAppEventObserver(self)
832    }
833}
834/// Nil observer, does nothing.
835impl AppEventObserver for () {}
836
837#[doc(hidden)]
838pub struct DynAppEventObserver<'a>(&'a mut dyn AppEventObserverDyn);
839
840trait AppEventObserverDyn {
841    fn raw_event_dyn(&mut self, ev: &zng_view_api::Event);
842    fn event_preview_dyn(&mut self, update: &mut EventUpdate);
843    fn event_ui_dyn(&mut self, update: &mut EventUpdate);
844    fn event_dyn(&mut self, update: &mut EventUpdate);
845    fn update_preview_dyn(&mut self);
846    fn update_ui_dyn(&mut self, updates: &mut WidgetUpdates);
847    fn update_dyn(&mut self);
848    fn info_dyn(&mut self, info_widgets: &mut InfoUpdates);
849    fn layout_dyn(&mut self, layout_widgets: &mut LayoutUpdates);
850    fn render_dyn(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates);
851}
852impl<O: AppEventObserver> AppEventObserverDyn for O {
853    fn raw_event_dyn(&mut self, ev: &zng_view_api::Event) {
854        self.raw_event(ev)
855    }
856
857    fn event_preview_dyn(&mut self, update: &mut EventUpdate) {
858        self.event_preview(update)
859    }
860
861    fn event_ui_dyn(&mut self, update: &mut EventUpdate) {
862        self.event_ui(update)
863    }
864
865    fn event_dyn(&mut self, update: &mut EventUpdate) {
866        self.event(update)
867    }
868
869    fn update_preview_dyn(&mut self) {
870        self.update_preview()
871    }
872
873    fn update_ui_dyn(&mut self, update_widgets: &mut WidgetUpdates) {
874        self.update_ui(update_widgets)
875    }
876
877    fn update_dyn(&mut self) {
878        self.update()
879    }
880
881    fn info_dyn(&mut self, info_widgets: &mut InfoUpdates) {
882        self.info(info_widgets)
883    }
884
885    fn layout_dyn(&mut self, layout_widgets: &mut LayoutUpdates) {
886        self.layout(layout_widgets)
887    }
888
889    fn render_dyn(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
890        self.render(render_widgets, render_update_widgets)
891    }
892}
893impl AppEventObserver for DynAppEventObserver<'_> {
894    fn raw_event(&mut self, ev: &zng_view_api::Event) {
895        self.0.raw_event_dyn(ev)
896    }
897
898    fn event_preview(&mut self, update: &mut EventUpdate) {
899        self.0.event_preview_dyn(update)
900    }
901
902    fn event_ui(&mut self, update: &mut EventUpdate) {
903        self.0.event_ui_dyn(update)
904    }
905
906    fn event(&mut self, update: &mut EventUpdate) {
907        self.0.event_dyn(update)
908    }
909
910    fn update_preview(&mut self) {
911        self.0.update_preview_dyn()
912    }
913
914    fn update_ui(&mut self, update_widgets: &mut WidgetUpdates) {
915        self.0.update_ui_dyn(update_widgets)
916    }
917
918    fn update(&mut self) {
919        self.0.update_dyn()
920    }
921
922    fn info(&mut self, info_widgets: &mut InfoUpdates) {
923        self.0.info_dyn(info_widgets)
924    }
925
926    fn layout(&mut self, layout_widgets: &mut LayoutUpdates) {
927        self.0.layout_dyn(layout_widgets)
928    }
929
930    fn render(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
931        self.0.render_dyn(render_widgets, render_update_widgets)
932    }
933
934    fn as_dyn(&mut self) -> DynAppEventObserver<'_> {
935        DynAppEventObserver(self.0)
936    }
937}
938
939impl AppExtension for () {
940    fn register(&self, _: &mut AppExtensionsInfo) {}
941}
942impl<A: AppExtension, B: AppExtension> AppExtension for (A, B) {
943    fn init(&mut self) {
944        self.0.init();
945        self.1.init();
946    }
947
948    fn register(&self, info: &mut AppExtensionsInfo) {
949        self.0.register(info);
950        self.1.register(info);
951    }
952
953    fn update_preview(&mut self) {
954        self.0.update_preview();
955        self.1.update_preview();
956    }
957
958    fn update_ui(&mut self, update_widgets: &mut WidgetUpdates) {
959        self.0.update_ui(update_widgets);
960        self.1.update_ui(update_widgets);
961    }
962
963    fn update(&mut self) {
964        self.0.update();
965        self.1.update();
966    }
967
968    fn info(&mut self, info_widgets: &mut InfoUpdates) {
969        self.0.info(info_widgets);
970        self.1.info(info_widgets);
971    }
972
973    fn layout(&mut self, layout_widgets: &mut LayoutUpdates) {
974        self.0.layout(layout_widgets);
975        self.1.layout(layout_widgets);
976    }
977
978    fn render(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
979        self.0.render(render_widgets, render_update_widgets);
980        self.1.render(render_widgets, render_update_widgets);
981    }
982
983    fn event_preview(&mut self, update: &mut EventUpdate) {
984        self.0.event_preview(update);
985        self.1.event_preview(update);
986    }
987
988    fn event_ui(&mut self, update: &mut EventUpdate) {
989        self.0.event_ui(update);
990        self.1.event_ui(update);
991    }
992
993    fn event(&mut self, update: &mut EventUpdate) {
994        self.0.event(update);
995        self.1.event(update);
996    }
997
998    fn deinit(&mut self) {
999        self.1.deinit();
1000        self.0.deinit();
1001    }
1002}
1003
1004#[cfg(feature = "dyn_app_extension")]
1005impl AppExtension for Vec<Box<dyn AppExtensionBoxed>> {
1006    fn init(&mut self) {
1007        for ext in self {
1008            ext.init();
1009        }
1010    }
1011
1012    fn register(&self, info: &mut AppExtensionsInfo) {
1013        for ext in self {
1014            ext.register(info);
1015        }
1016    }
1017
1018    fn update_preview(&mut self) {
1019        for ext in self {
1020            ext.update_preview();
1021        }
1022    }
1023
1024    fn update_ui(&mut self, update_widgets: &mut WidgetUpdates) {
1025        for ext in self {
1026            ext.update_ui(update_widgets);
1027        }
1028    }
1029
1030    fn update(&mut self) {
1031        for ext in self {
1032            ext.update();
1033        }
1034    }
1035
1036    fn event_preview(&mut self, update: &mut EventUpdate) {
1037        for ext in self {
1038            ext.event_preview(update);
1039        }
1040    }
1041
1042    fn event_ui(&mut self, update: &mut EventUpdate) {
1043        for ext in self {
1044            ext.event_ui(update);
1045        }
1046    }
1047
1048    fn event(&mut self, update: &mut EventUpdate) {
1049        for ext in self {
1050            ext.event(update);
1051        }
1052    }
1053
1054    fn info(&mut self, info_widgets: &mut InfoUpdates) {
1055        for ext in self {
1056            ext.info(info_widgets);
1057        }
1058    }
1059
1060    fn layout(&mut self, layout_widgets: &mut LayoutUpdates) {
1061        for ext in self {
1062            ext.layout(layout_widgets);
1063        }
1064    }
1065
1066    fn render(&mut self, render_widgets: &mut RenderUpdates, render_update_widgets: &mut RenderUpdates) {
1067        for ext in self {
1068            ext.render(render_widgets, render_update_widgets);
1069        }
1070    }
1071
1072    fn deinit(&mut self) {
1073        for ext in self.iter_mut().rev() {
1074            ext.deinit();
1075        }
1076    }
1077}
1078
1079/// Start and manage an app process.
1080pub struct APP;
1081impl APP {
1082    /// If the crate was built with `feature="multi_app"`.
1083    ///
1084    /// If `true` multiple apps can run in the same process, but only one app per thread at a time.
1085    pub fn multi_app_enabled(&self) -> bool {
1086        cfg!(feature = "multi_app")
1087    }
1088
1089    /// If an app started building or is running in the current thread.
1090    ///
1091    /// This is `true` as soon as `APP.minimal()` or `APP.defaults()` is called.
1092    ///
1093    /// You can use [`app_local!`] to create *static* resources that live for the app lifetime, these statics can be used
1094    /// as soon as this is `true`.
1095    ///
1096    /// [`app_local!`]: zng_app_context::app_local
1097    pub fn is_started(&self) -> bool {
1098        LocalContext::current_app().is_some()
1099    }
1100
1101    /// If an app is running in the current thread.
1102    ///
1103    /// Apps are *running* as soon as [`run`], [`run_headless`] or `run_window` are called.
1104    /// This will remain `true` until run returns or the [`HeadlessApp`] is dropped.
1105    ///
1106    /// [`run`]: AppExtended::run
1107    /// [`run_headless`]: AppExtended::run_headless
1108    pub fn is_running(&self) -> bool {
1109        self.is_started() && APP_PROCESS_SV.read().is_running()
1110    }
1111
1112    /// Gets the unique ID of the current app.
1113    ///
1114    /// This ID usually does not change as most apps only run once per process, but it can change often during tests.
1115    /// Resources that interact with [`app_local!`] values can use this ID to ensure that they are still operating in the same
1116    /// app.
1117    ///
1118    /// [`app_local!`]: zng_app_context::app_local
1119    pub fn id(&self) -> Option<AppId> {
1120        LocalContext::current_app()
1121    }
1122
1123    #[cfg(not(feature = "multi_app"))]
1124    fn assert_can_run_single() {
1125        use std::sync::atomic::*;
1126        static CAN_RUN: AtomicBool = AtomicBool::new(true);
1127
1128        if !CAN_RUN.swap(false, Ordering::SeqCst) {
1129            panic!("only one app is allowed per process")
1130        }
1131    }
1132
1133    fn assert_can_run() {
1134        #[cfg(not(feature = "multi_app"))]
1135        Self::assert_can_run_single();
1136        if APP.is_running() {
1137            panic!("only one app is allowed per thread")
1138        }
1139    }
1140
1141    /// Returns a [`WindowMode`] value that indicates if the app is headless, headless with renderer or headed.
1142    ///
1143    /// Note that specific windows can be in headless mode even if the app is headed.
1144    pub fn window_mode(&self) -> WindowMode {
1145        if VIEW_PROCESS.is_available() {
1146            if VIEW_PROCESS.is_headless_with_render() {
1147                WindowMode::HeadlessWithRenderer
1148            } else {
1149                WindowMode::Headed
1150            }
1151        } else {
1152            WindowMode::Headless
1153        }
1154    }
1155    /// List of app extensions that are part of the current app.
1156    pub fn extensions(&self) -> Arc<AppExtensionsInfo> {
1157        APP_PROCESS_SV.read().extensions()
1158    }
1159
1160    /// Defines what raw device events the view-process instance should monitor and notify.
1161    ///
1162    /// Raw device events are global and can be received even when the app has no visible window.
1163    ///
1164    /// These events are disabled by default as they can impact performance or may require special security clearance,
1165    /// depending on the view-process implementation and operating system.
1166    pub fn device_events_filter(&self) -> Var<DeviceEventsFilter> {
1167        APP_PROCESS_SV.read().device_events_filter.clone()
1168    }
1169}
1170
1171impl APP {
1172    /// Starts building an application with no extensions.
1173    #[cfg(feature = "dyn_app_extension")]
1174    pub fn minimal(&self) -> AppExtended<Vec<Box<dyn AppExtensionBoxed>>> {
1175        zng_env::init_process_name("app-process");
1176
1177        #[cfg(debug_assertions)]
1178        print_tracing(tracing::Level::INFO);
1179        assert_not_view_process();
1180        Self::assert_can_run();
1181        spawn_deadlock_detection();
1182
1183        let _ = INSTANT.now();
1184        let scope = LocalContext::start_app(AppId::new_unique());
1185        AppExtended {
1186            extensions: vec![],
1187            view_process_exe: None,
1188            view_process_env: HashMap::new(),
1189            _cleanup: scope,
1190        }
1191    }
1192
1193    /// Starts building an application with no extensions.
1194    #[cfg(not(feature = "dyn_app_extension"))]
1195    pub fn minimal(&self) -> AppExtended<()> {
1196        #[cfg(debug_assertions)]
1197        print_tracing(tracing::Level::INFO);
1198        assert_not_view_process();
1199        Self::assert_can_run();
1200        spawn_deadlock_detection();
1201        let scope = LocalContext::start_app(AppId::new_unique());
1202        AppExtended {
1203            extensions: (),
1204            view_process_exe: None,
1205            view_process_env: HashMap::new(),
1206            _cleanup: scope,
1207        }
1208    }
1209}
1210
1211/// Application builder.
1212///
1213/// You can use `APP` to start building the app.
1214pub struct AppExtended<E: AppExtension> {
1215    extensions: E,
1216    view_process_exe: Option<PathBuf>,
1217    view_process_env: HashMap<Txt, Txt>,
1218
1219    // cleanup on drop.
1220    _cleanup: AppScope,
1221}
1222#[cfg(feature = "dyn_app_extension")]
1223impl AppExtended<Vec<Box<dyn AppExtensionBoxed>>> {
1224    /// Includes an application extension.
1225    pub fn extend<F: AppExtension>(mut self, extension: F) -> AppExtended<Vec<Box<dyn AppExtensionBoxed>>> {
1226        self.extensions.push(TraceAppExt(extension).boxed());
1227        self
1228    }
1229
1230    fn run_dyn(self, start: std::pin::Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
1231        let app = RunningApp::start(
1232            self._cleanup,
1233            self.extensions,
1234            true,
1235            true,
1236            self.view_process_exe,
1237            self.view_process_env,
1238        );
1239
1240        UPDATES.run(start).perm();
1241
1242        app.run_headed();
1243    }
1244
1245    fn run_headless_dyn(self, with_renderer: bool) -> HeadlessApp {
1246        let app = RunningApp::start(
1247            self._cleanup,
1248            self.extensions.boxed(),
1249            false,
1250            with_renderer,
1251            self.view_process_exe,
1252            self.view_process_env,
1253        );
1254
1255        HeadlessApp { app }
1256    }
1257}
1258
1259// Monomorphize dyn app. Without this the entire RunningApp code is generic that must build on the dependent crates.
1260#[cfg(feature = "dyn_app_extension")]
1261impl<E: AppExtension> AppExtended<E> {
1262    fn cast_app(self) -> AppExtended<Vec<Box<dyn AppExtensionBoxed>>> {
1263        let app: Box<dyn std::any::Any> = Box::new(self);
1264        match app.downcast::<AppExtended<Vec<Box<dyn AppExtensionBoxed>>>>() {
1265            Ok(ok) => *ok,
1266            Err(e) => {
1267                let app = *e.downcast::<Self>().unwrap();
1268                AppExtended {
1269                    extensions: vec![app.extensions.boxed()],
1270                    view_process_exe: app.view_process_exe,
1271                    view_process_env: app.view_process_env,
1272                    _cleanup: app._cleanup,
1273                }
1274            }
1275        }
1276    }
1277
1278    fn run_impl(self, start: impl Future<Output = ()> + Send + 'static) {
1279        self.cast_app().run_dyn(Box::pin(start))
1280    }
1281
1282    fn run_headless_impl(self, with_renderer: bool) -> HeadlessApp {
1283        self.cast_app().run_headless_dyn(with_renderer)
1284    }
1285}
1286
1287#[cfg(not(feature = "dyn_app_extension"))]
1288impl<E: AppExtension> AppExtended<E> {
1289    /// Includes an application extension.
1290    pub fn extend<F: AppExtension>(self, extension: F) -> AppExtended<impl AppExtension> {
1291        AppExtended {
1292            _cleanup: self._cleanup,
1293            extensions: (self.extensions, TraceAppExt(extension)),
1294            view_process_exe: self.view_process_exe,
1295            view_process_env: self.view_process_env,
1296        }
1297    }
1298
1299    fn run_impl(self, start: impl Future<Output = ()> + Send + 'static) {
1300        let app = RunningApp::start(
1301            self._cleanup,
1302            self.extensions,
1303            true,
1304            true,
1305            self.view_process_exe,
1306            self.view_process_env,
1307        );
1308
1309        UPDATES.run(start).perm();
1310
1311        app.run_headed();
1312    }
1313
1314    fn run_headless_impl(self, with_renderer: bool) -> HeadlessApp {
1315        let app = RunningApp::start(
1316            self._cleanup,
1317            self.extensions.boxed(),
1318            false,
1319            with_renderer,
1320            self.view_process_exe,
1321            self.view_process_env,
1322        );
1323
1324        HeadlessApp { app }
1325    }
1326}
1327impl<E: AppExtension> AppExtended<E> {
1328    /// Set the path to the executable for the *View Process*.
1329    ///
1330    /// By the default the current executable is started again as a *View Process*, you can use
1331    /// two executables instead, by setting this value.
1332    ///
1333    /// Note that the `view_process_exe` must start a view server and both
1334    /// executables must be build using the same exact [`VERSION`].
1335    ///
1336    /// [`VERSION`]: zng_view_api::VERSION  
1337    pub fn view_process_exe(mut self, view_process_exe: impl Into<PathBuf>) -> Self {
1338        self.view_process_exe = Some(view_process_exe.into());
1339        self
1340    }
1341
1342    /// Set an env variable for the view-process.
1343    pub fn view_process_env(mut self, name: impl Into<Txt>, value: impl Into<Txt>) -> Self {
1344        self.view_process_env.insert(name.into(), value.into());
1345        self
1346    }
1347
1348    /// Starts the app, then starts polling `start` to run.
1349    ///
1350    /// This method only returns when the app has exited.
1351    ///
1352    /// The `start` task runs in a [`UiTask`] in the app context, note that it only needs to start the app, usually
1353    /// by opening a window, the app will keep running after `start` is finished.
1354    pub fn run<F: Future<Output = ()> + Send + 'static>(self, start: impl IntoFuture<IntoFuture = F>) {
1355        let start = start.into_future();
1356        self.run_impl(start)
1357    }
1358
1359    /// Initializes extensions in headless mode and returns an [`HeadlessApp`].
1360    ///
1361    /// If `with_renderer` is `true` spawns a renderer process for headless rendering. See [`HeadlessApp::renderer_enabled`]
1362    /// for more details.
1363    pub fn run_headless(self, with_renderer: bool) -> HeadlessApp {
1364        self.run_headless_impl(with_renderer)
1365    }
1366}
1367
1368// this module is declared here on purpose so that advanced `impl APP` blocks show later in the docs.
1369mod running;
1370pub use running::*;
1371use zng_view_api::DeviceEventsFilter;
1372
1373mod private {
1374    // https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
1375    pub trait Sealed {}
1376}
1377
1378/// Enables [`tracing`] events printing if a subscriber is not already set.
1379///
1380/// All non-fatal errors in the Zng project are logged using tracing.
1381///
1382/// In debug builds this function is called automatically with level INFO on app start.
1383///
1384/// In `"wasm32"` builds logs to the browser console.
1385///
1386/// In `"android"` builds logs to logcat.
1387///
1388/// See also [`test_log`] to enable panicking on error log.
1389///
1390/// See also [`print_tracing_filter`] for the filter used by this.
1391///
1392/// [`tracing`]: https://docs.rs/tracing
1393pub fn print_tracing(max: tracing::Level) -> bool {
1394    use tracing_subscriber::prelude::*;
1395
1396    let layers = tracing_subscriber::registry().with(FilterLayer(max));
1397
1398    #[cfg(target_os = "android")]
1399    let layers = layers.with(tracing_android::layer(&zng_env::about().pkg_name).unwrap());
1400
1401    #[cfg(not(target_os = "android"))]
1402    let layers = {
1403        let fmt_layer = tracing_subscriber::fmt::layer().without_time();
1404
1405        #[cfg(target_arch = "wasm32")]
1406        let fmt_layer = fmt_layer.with_ansi(false).with_writer(tracing_web::MakeWebConsoleWriter::new());
1407
1408        layers.with(fmt_layer)
1409    };
1410
1411    layers.try_init().is_ok()
1412}
1413
1414struct FilterLayer(tracing::Level);
1415impl<S: tracing::Subscriber> tracing_subscriber::Layer<S> for FilterLayer {
1416    fn enabled(&self, metadata: &tracing::Metadata<'_>, _: tracing_subscriber::layer::Context<'_, S>) -> bool {
1417        print_tracing_filter(&self.0, metadata)
1418    }
1419
1420    fn max_level_hint(&self) -> Option<tracing::metadata::LevelFilter> {
1421        Some(self.0.into())
1422    }
1423
1424    #[cfg(any(test, feature = "test_util"))]
1425    fn on_event(&self, event: &tracing::Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) {
1426        if event.metadata().level() == &tracing::Level::ERROR && APP.is_running() && TEST_LOG.get() {
1427            struct MsgCollector<'a>(&'a mut String);
1428            impl tracing::field::Visit for MsgCollector<'_> {
1429                fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn fmt::Debug) {
1430                    use std::fmt::Write;
1431                    write!(self.0, "\n  {} = {:?}", field.name(), value).unwrap();
1432                }
1433            }
1434
1435            let meta = event.metadata();
1436            let file = meta.file().unwrap_or("");
1437            let line = meta.line().unwrap_or(0);
1438
1439            let mut msg = format!("[{file}:{line}]");
1440            event.record(&mut MsgCollector(&mut msg));
1441
1442            panic!("[LOG-ERROR]{msg}")
1443        }
1444    }
1445}
1446/// Filter used by [`print_tracing`], removes some log noise from dependencies.
1447///
1448/// Use `tracing_subscriber::filter::FilterFn` plug this filter into a tracing setup.
1449pub fn print_tracing_filter(level: &tracing::Level, metadata: &tracing::Metadata) -> bool {
1450    if metadata.level() > level {
1451        return false;
1452    }
1453
1454    if metadata.level() == &tracing::Level::INFO {
1455        // suppress large info about texture cache
1456        if metadata.target() == "zng_webrender::device::gl" {
1457            return false;
1458        }
1459        // suppress config dump
1460        if metadata.target() == "zng_webrender::renderer::init" {
1461            return false;
1462        }
1463    } else if metadata.level() == &tracing::Level::WARN {
1464        // suppress webrender warnings:
1465        //
1466        if metadata.target() == "zng_webrender::device::gl" {
1467            // Suppress "Cropping texture upload Box2D((0, 0), (0, 1)) to None"
1468            // This happens when an empty frame is rendered.
1469            if metadata.line() == Some(4647) {
1470                return false;
1471            }
1472        }
1473
1474        // suppress font-kit warnings:
1475        //
1476        if metadata.target() == "font_kit::loaders::freetype" {
1477            // Suppress "$fn(): found invalid platform ID $n"
1478            // This does not look fully implemented and generates a lot of warns
1479            // with the default Ubuntu font set all with valid platform IDs.
1480            if metadata.line() == Some(734) {
1481                return false;
1482            }
1483        }
1484    }
1485
1486    true
1487}
1488
1489/// Modifies the [`print_tracing`] subscriber to panic for error logs in the current app.
1490#[cfg(any(test, feature = "test_util"))]
1491pub fn test_log() {
1492    TEST_LOG.set(true);
1493}
1494
1495#[cfg(any(test, feature = "test_util"))]
1496zng_app_context::app_local! {
1497    static TEST_LOG: bool = false;
1498}
1499
1500#[doc(hidden)]
1501pub fn name_from_pkg_name(name: &'static str) -> Txt {
1502    let mut n = String::new();
1503    let mut sep = "";
1504    for part in name.split(&['-', '_']) {
1505        n.push_str(sep);
1506        let mut chars = part.char_indices();
1507        let (_, c) = chars.next().unwrap();
1508        c.to_uppercase().for_each(|c| n.push(c));
1509        if let Some((i, _)) = chars.next() {
1510            n.push_str(&part[i..]);
1511        }
1512        sep = " ";
1513    }
1514    n.into()
1515}
1516
1517#[doc(hidden)]
1518pub fn txt_from_pkg_meta(value: &'static str) -> Txt {
1519    value.into()
1520}