Started integrating INSTANT with APP.
This commit is contained in:
parent
b707b40bec
commit
8561d2d1c8
|
@ -1,4 +0,0 @@
|
|||
# Timers TODO
|
||||
|
||||
* Configurable `Instant::now` source, to advance time manually in tests.
|
||||
* Time scale, for recording?
|
|
@ -1,4 +1,5 @@
|
|||
use zero_ui_app_context::app_local;
|
||||
use zero_ui_time::INSTANT_APP;
|
||||
|
||||
use crate::update::{UpdatesTrace, UPDATES};
|
||||
|
||||
|
@ -87,9 +88,14 @@ impl EVENTS {
|
|||
|
||||
let mut updates: Vec<_> = ev.updates.get_mut().drain(..).collect();
|
||||
drop(ev);
|
||||
for u in &mut updates {
|
||||
let ev = u.event;
|
||||
ev.on_update(u);
|
||||
|
||||
if !updates.is_empty() {
|
||||
let _t = INSTANT_APP.pause_for_update();
|
||||
|
||||
for u in &mut updates {
|
||||
let ev = u.event;
|
||||
ev.on_update(u);
|
||||
}
|
||||
}
|
||||
updates
|
||||
}
|
||||
|
|
|
@ -1206,6 +1206,7 @@ impl APP {
|
|||
assert_not_view_process();
|
||||
Self::assert_can_run();
|
||||
check_deadlock();
|
||||
let _ = INSTANT.now();
|
||||
let scope = LocalContext::start_app(AppId::new_unique());
|
||||
AppExtended {
|
||||
extensions: vec![],
|
||||
|
|
|
@ -8,7 +8,8 @@ use std::{
|
|||
|
||||
use crate::Deadline;
|
||||
use zero_ui_app_context::{app_local, AppScope};
|
||||
use zero_ui_var::{response_var, ResponderVar, ResponseVar, VARS, VARS_APP};
|
||||
use zero_ui_time::INSTANT_APP;
|
||||
use zero_ui_var::{response_var, ArcVar, ResponderVar, ResponseVar, Var as _, VARS, VARS_APP};
|
||||
|
||||
use crate::{
|
||||
event::{
|
||||
|
@ -66,7 +67,10 @@ impl<E: AppExtension> RunningApp<E> {
|
|||
VARS_APP.init_modify_trace(UpdatesTrace::log_var);
|
||||
|
||||
let mut info = AppExtensionsInfo::start();
|
||||
extensions.register(&mut info);
|
||||
{
|
||||
let _t = INSTANT_APP.pause_for_update();
|
||||
extensions.register(&mut info);
|
||||
}
|
||||
APP_PROCESS_SV.write().set_extensions(info);
|
||||
|
||||
let device_events = extensions.enable_device_events();
|
||||
|
@ -114,6 +118,8 @@ impl<E: AppExtension> RunningApp<E> {
|
|||
pub fn notify_event<O: AppEventObserver>(&mut self, mut update: EventUpdate, observer: &mut O) {
|
||||
let _scope = tracing::trace_span!("notify_event", event = update.event().name()).entered();
|
||||
|
||||
let _t = INSTANT_APP.pause_for_update();
|
||||
|
||||
update.event().on_update(&mut update);
|
||||
|
||||
self.extensions.event_preview(&mut update);
|
||||
|
@ -712,6 +718,9 @@ impl<E: AppExtension> RunningApp<E> {
|
|||
let _s = tracing::debug_span!("info").entered();
|
||||
|
||||
let mut info_widgets = mem::take(&mut self.pending.info_widgets);
|
||||
|
||||
let _t = INSTANT_APP.pause_for_update();
|
||||
|
||||
{
|
||||
let _s = tracing::debug_span!("ext.info").entered();
|
||||
self.extensions.info(&mut info_widgets);
|
||||
|
@ -730,6 +739,8 @@ impl<E: AppExtension> RunningApp<E> {
|
|||
|
||||
let mut update_widgets = mem::take(&mut self.pending.update_widgets);
|
||||
|
||||
let _t = INSTANT_APP.pause_for_update();
|
||||
|
||||
{
|
||||
let _s = tracing::debug_span!("ext.update_preview").entered();
|
||||
self.extensions.update_preview();
|
||||
|
@ -778,6 +789,8 @@ impl<E: AppExtension> RunningApp<E> {
|
|||
let _s = tracing::debug_span!("update_event", ?update).entered();
|
||||
|
||||
self.loop_monitor.maybe_trace(|| {
|
||||
let _t = INSTANT_APP.pause_for_update();
|
||||
|
||||
{
|
||||
let _s = tracing::debug_span!("ext.event_preview").entered();
|
||||
self.extensions.event_preview(&mut update);
|
||||
|
@ -828,6 +841,8 @@ impl<E: AppExtension> RunningApp<E> {
|
|||
let mut layout_widgets = mem::take(&mut self.pending.layout_widgets);
|
||||
|
||||
self.loop_monitor.maybe_trace(|| {
|
||||
let _t = INSTANT_APP.pause_for_update();
|
||||
|
||||
{
|
||||
let _s = tracing::debug_span!("ext.layout").entered();
|
||||
self.extensions.layout(&mut layout_widgets);
|
||||
|
@ -848,6 +863,8 @@ impl<E: AppExtension> RunningApp<E> {
|
|||
let mut render_widgets = mem::take(&mut self.pending.render_widgets);
|
||||
let mut render_update_widgets = mem::take(&mut self.pending.render_update_widgets);
|
||||
|
||||
let _t = INSTANT_APP.pause_for_update();
|
||||
|
||||
{
|
||||
let _s = tracing::debug_span!("ext.render").entered();
|
||||
self.extensions.render(&mut render_widgets, &mut render_update_widgets);
|
||||
|
@ -1005,6 +1022,18 @@ impl APP {
|
|||
pub fn exit(&self) -> ResponseVar<ExitCancelled> {
|
||||
APP_PROCESS_SV.write().exit()
|
||||
}
|
||||
|
||||
/// Gets a variable that sets if [`INSTANT.now`] is the same exact value during each update pass.
|
||||
///
|
||||
/// Time is paused for each update pass, event notification, layout and render so that all widgets observe
|
||||
/// the same time in the same update.
|
||||
///
|
||||
/// This is enabled by default.
|
||||
///
|
||||
/// [`INSTANT.now`]: crate::INSTANT::now
|
||||
pub fn pause_time_for_update(&self) -> ArcVar<bool> {
|
||||
APP_PROCESS_SV.read().pause_time_for_updates.clone()
|
||||
}
|
||||
}
|
||||
|
||||
command! {
|
||||
|
@ -1041,6 +1070,21 @@ struct PendingExit {
|
|||
impl AppIntrinsic {
|
||||
/// Pre-init intrinsic services and commands, must be called before extensions init.
|
||||
pub(super) fn pre_init(is_headed: bool, with_renderer: bool, view_process_exe: Option<PathBuf>, device_events: bool) -> Self {
|
||||
APP_PROCESS_SV
|
||||
.read()
|
||||
.pause_time_for_updates
|
||||
.hook(|a| {
|
||||
if !matches!(INSTANT.mode(), zero_ui_time::InstantMode::Manual) {
|
||||
if *a.value() {
|
||||
INSTANT_APP.set_mode(zero_ui_time::InstantMode::UpdatePaused);
|
||||
} else {
|
||||
INSTANT_APP.set_mode(zero_ui_time::InstantMode::Now);
|
||||
}
|
||||
}
|
||||
true
|
||||
})
|
||||
.perm();
|
||||
|
||||
if is_headed {
|
||||
debug_assert!(with_renderer);
|
||||
|
||||
|
@ -1154,17 +1198,17 @@ pub(crate) fn check_deadlock() {
|
|||
pub(crate) fn check_deadlock() {}
|
||||
|
||||
app_local! {
|
||||
pub(super) static APP_PROCESS_SV: AppProcessService = const {
|
||||
AppProcessService {
|
||||
exit_requests: None,
|
||||
extensions: None,
|
||||
}
|
||||
pub(super) static APP_PROCESS_SV: AppProcessService =AppProcessService {
|
||||
exit_requests: None,
|
||||
extensions: None,
|
||||
pause_time_for_updates: zero_ui_var::var(true),
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) struct AppProcessService {
|
||||
exit_requests: Option<ResponderVar<ExitCancelled>>,
|
||||
extensions: Option<Arc<AppExtensionsInfo>>,
|
||||
pause_time_for_updates: ArcVar<bool>,
|
||||
}
|
||||
impl AppProcessService {
|
||||
pub(super) fn take_requests(&mut self) -> Option<ResponderVar<ExitCancelled>> {
|
||||
|
|
|
@ -13,7 +13,7 @@ use std::{
|
|||
};
|
||||
use zero_ui_app_context::app_local;
|
||||
use zero_ui_handle::{Handle, HandleOwner, WeakHandle};
|
||||
use zero_ui_time::{DInstant, INSTANT};
|
||||
use zero_ui_time::{DInstant, INSTANT, INSTANT_APP};
|
||||
use zero_ui_var::{types::WeakArcVar, var, ReadOnlyArcVar, Var, WeakVar};
|
||||
|
||||
use crate::{
|
||||
|
@ -243,6 +243,8 @@ impl TimersService {
|
|||
pub(crate) fn notify() {
|
||||
let _s = tracing::trace_span!("TIMERS").entered();
|
||||
|
||||
let _t = INSTANT_APP.pause_for_update();
|
||||
|
||||
// we need to detach the handlers, so we can pass the context for then
|
||||
// so we `mem::take` for the duration of the call. But new timers can be registered inside
|
||||
// the handlers, so we add those handlers using `extend`.
|
||||
|
|
|
@ -13,7 +13,10 @@ pub struct INSTANT;
|
|||
impl INSTANT {
|
||||
/// Returns an instant corresponding to "now" or an instant configured by the app.
|
||||
///
|
||||
/// This method can be called in non-app threads.
|
||||
/// This method can be called in non-app threads. Apps can override this time in app threads,
|
||||
/// by default the time is *paused* for each widget OP pass so that all widgets observe the same
|
||||
/// time on the same pass, you can use [`mode`](Self::mode) to check how `now` updates and you
|
||||
/// can use the `APP.pause_time_for_update` variable to disable pausing.
|
||||
pub fn now(&self) -> DInstant {
|
||||
if zero_ui_app_context::LocalContext::current_app().is_some() {
|
||||
if let Some(now) = INSTANT_SV.read().now {
|
||||
|
@ -88,6 +91,36 @@ impl INSTANT_APP {
|
|||
pub fn custom_now(&self) -> Option<DInstant> {
|
||||
INSTANT_SV.read().now
|
||||
}
|
||||
|
||||
/// If mode is [`InstantMode::UpdatePaused`] sets the app custom_now to the current time and returns
|
||||
/// an object that unsets the custom now on drop.
|
||||
pub fn pause_for_update(&self) -> Option<InstantUpdatePause> {
|
||||
let mut sv = INSTANT_SV.write();
|
||||
match sv.mode {
|
||||
InstantMode::UpdatePaused => {
|
||||
let now = DInstant(INSTANT.epoch().elapsed());
|
||||
sv.now = Some(now);
|
||||
Some(InstantUpdatePause { now })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Unset now on drop.
|
||||
///
|
||||
/// The time is only unset if it is still set to the same pause time.
|
||||
#[must_use = "unset_now on drop"]
|
||||
pub struct InstantUpdatePause {
|
||||
now: DInstant,
|
||||
}
|
||||
impl Drop for InstantUpdatePause {
|
||||
fn drop(&mut self) {
|
||||
let mut sv = INSTANT_SV.write();
|
||||
if sv.now == Some(self.now) {
|
||||
sv.now = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Duration elapsed since an epoch.
|
||||
|
@ -175,7 +208,7 @@ impl From<DInstant> for Instant {
|
|||
pub enum InstantMode {
|
||||
/// Calls during an widget update (or layout, render) pass read the same time.
|
||||
/// Other calls to `now` resamples the time.
|
||||
UpdateLocked,
|
||||
UpdatePaused,
|
||||
/// Every call to `now` resamples the time.
|
||||
Now,
|
||||
/// Time is controlled by the app.
|
||||
|
@ -187,7 +220,7 @@ static EPOCH: RwLock<Option<Instant>> = RwLock::new(None);
|
|||
app_local! {
|
||||
static INSTANT_SV: InstantService = const {
|
||||
InstantService {
|
||||
mode: InstantMode::UpdateLocked,
|
||||
mode: InstantMode::UpdatePaused,
|
||||
now: None,
|
||||
}
|
||||
};
|
||||
|
@ -208,6 +241,8 @@ struct InstantService {
|
|||
///
|
||||
/// ```
|
||||
/// # use zero_ui_time::*;
|
||||
/// # trait TimeUnits { fn secs(self) -> std::time::Duration where Self: Sized { std::time::Duration::ZERO } }
|
||||
/// # impl TimeUnits for i32 { }
|
||||
/// fn timer(deadline: impl Into<Deadline>) { }
|
||||
///
|
||||
/// timer(5.secs());
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::{mem, thread::ThreadId, time::Duration};
|
||||
|
||||
use zero_ui_app_context::{app_local, context_local};
|
||||
use zero_ui_time::INSTANT_APP;
|
||||
|
||||
use crate::animation::AnimationTimer;
|
||||
|
||||
|
@ -318,6 +319,7 @@ impl VARS_APP {
|
|||
/// This must be called by app framework implementers only.
|
||||
pub fn apply_updates(&self) {
|
||||
let _s = tracing::trace_span!("VARS").entered();
|
||||
let _t = INSTANT_APP.pause_for_update();
|
||||
Self::apply_updates_and_after(0)
|
||||
}
|
||||
fn apply_updates_and_after(depth: u8) {
|
||||
|
|
Loading…
Reference in New Issue