Repository: zng-ui/zng (language: rust)

Issue:
TextInput! crashes
#### I tried this code:

```rust
use window::WindowRoot;
use zng::prelude::*;

fn main() {
  zng::env::init!();
  APP.defaults().run_window(main_window());
}

async fn main_window() -> WindowRoot {
  let title = var(Txt::from_str("title"));
  Window! {
    child = TextInput!{
          txt=title;
        }
  }
}
```

The app crashed after run. 
I tried the `text` folder from examples. It also crashed after entering the editing window.

#### Zng version:

```
zng = { version = "0.10.3", features = ["view_prebuilt"] }
```

#### Cargo version:

```
cargo 1.78.0 (54d8815d0 2024-03-26)
release: 1.78.0
commit-hash: 54d8815d04fa3816edc207bbc4dd36bf18014dbc
commit-date: 2024-03-26
host: x86_64-pc-windows-msvc
libgit2: 1.7.2 (sys:0.18.2 vendored)
libcurl: 8.6.0-DEV (sys:0.4.72+curl-8.6.0 vendored ssl:Schannel)
os: Windows 10.0.19045 (Windows 10 Pro) [64-bit]
```

#### Error log or stack trace:

<details>
  <summary>stderr</summary>

```
thread 'main' panicked at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\core\src\time.rs:1108:31:
   overflow when adding durations
widget path:
   <no-widget>
stack backtrace:
  12: core::panicking::panic_display<ref$<str$> >
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13/library\core\src\panicking.rs:262
  13: core::option::expect_failed
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13/library\core\src\option.rs:1994
  14: enum2$<core::option::Option<core::time::Duration> >::expect
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\core\src\option.rs:895
  15: core::time::impl$2::add
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\core\src\time.rs:1108
  16: zng_time::impl$4::add
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-time-0.2.14\src\lib.rs:207
       205 │ 
       206 │     fn add(self, rhs: Duration) -> Self {
       207 >         Self(self.0 + rhs)
       208 │     }
       209 │ }

  17: zng_var::animation::Animation::sleep
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-var-0.4.7\src\animation.rs:195
       193 │     pub fn sleep(&self, duration: Duration) {
       194 │         let mut me = self.0.lock();
       195 >         me.sleep = Some(Deadline(me.now + duration));
       196 │     }
       197 │ 

  18: zng_ext_input::keyboard::impl$4::caret_animation::closure$0
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-ext-input-0.5.11\src\keyboard.rs:523
       521 │                     vm.set(one);
       522 │                 }
       523 >                 anim.sleep(interval);
       524 │             }
       525 │         })

  19: zng_var::animation::var_animate::closure$0::closure$0<zng_unit::factor::Factor,zng_var::arc::ArcVar<zng_unit::factor::Factor>,zng_ext_input::keyboard::impl$4::caret_animation::closure_env$0>
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-var-0.4.7\src\animation.rs:666
       664 │                     // try update
       665 │                     let r = target.modify(clmv!(animate, args, |value| {
       666 >                         (animate.lock())(&args, value);
       667 │                     }));
       668 │ 

  20: core::ops::function::FnOnce::call_once<zng_var::animation::var_animate::closure$0::closure_env$0<zng_unit::factor::Factor,zng_var::arc::ArcVar<zng_unit::factor::Factor>,zng_ext_input::keyboard::impl$4::caret_animation::closure_env$0>,tuple$<ref_mut$<zng_v
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\core\src\ops\function.rs:250
  21: alloc::boxed::impl$48::call_once<tuple$<ref_mut$<zng_var::VarModify<zng_unit::factor::Factor> > >,dyn$<core::ops::function::FnOnce<tuple$<ref_mut$<zng_var::VarModify<zng_unit::factor::Factor> > >,assoc$<Output,tuple$<> > > >,alloc::alloc::Global>
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\alloc\src\boxed.rs:2022
  22: zng_var::util::impl$1::apply_modify::closure$0<zng_unit::factor::Factor>
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-var-0.4.7\src\util.rs:401
       399 │             Box::new(move |v| {
       400 │                 let mut value = VarModify::new(v.as_any().downcast_ref::<T>().unwrap());
       401 >                 modify(&mut value);
       402 │                 let (notify, new_value, update, tags, importance) = value.finish();
       403 │                 (

  23: core::ops::function::FnOnce::call_once<zng_var::util::impl$1::apply_modify::closure_env$0<zng_unit::factor::Factor>,tuple$<ref$<dyn$<zng_var::AnyVarValue> > > >
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\core\src\ops\function.rs:250
  24: alloc::boxed::impl$48::call_once<tuple$<ref$<dyn$<zng_var::AnyVarValue> > >,dyn$<core::ops::function::FnOnce<tuple$<ref$<dyn$<zng_var::AnyVarValue> > >,assoc$<Output,tuple$<bool,enum2$<core::option::Option<alloc::boxed::Box<dyn$<zng_var::AnyVarValue>,allo
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\alloc\src\boxed.rs:2022
  25: zng_var::util::apply_modify
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-var-0.4.7\src\util.rs:445
       443 │     let data = parking_lot::RwLockWriteGuard::downgrade(data);
       444 │ 
       445 >     let (notify, new_value, update, tags, custom_importance) = modify(&*data.value);
       446 │ 
       447 │     if notify {

  26: zng_var::util::VarData<zng_unit::factor::Factor>::apply_modify<zng_unit::factor::Factor>
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-var-0.4.7\src\util.rs:397
       395 │     #[cfg(feature = "dyn_closure")]
       396 │     pub fn apply_modify(&self, modify: Box<dyn FnOnce(&mut VarModify<T>) + 'static>) {
       397 >         apply_modify(
       398 │             &self.0,
       399 │             Box::new(move |v| {

  27: zng_var::arc::impl$7::modify_impl::closure$0<zng_unit::factor::Factor>
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-var-0.4.7\src\arc.rs:183
       181 │         VARS.schedule_update(
       182 │             Box::new(move || {
       183 >                 me.0.apply_modify(modify);
       184 │             }),
       185 │             std::any::type_name::<T>(),

  28: core::ops::function::FnOnce::call_once<zng_var::arc::impl$7::modify_impl::closure_env$0<zng_unit::factor::Factor>,tuple$<> >
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\core\src\ops\function.rs:250
  29: alloc::boxed::impl$48::call_once<tuple$<>,dyn$<core::ops::function::FnOnce<tuple$<>,assoc$<Output,tuple$<> > >,core::marker::Send>,alloc::alloc::Global>
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\alloc\src\boxed.rs:2022
  30: zng_app_context::impl$23::with_context::closure$0<enum2$<core::option::Option<zng_var::animation::ModifyInfo> >,tuple$<>,alloc::boxed::Box<dyn$<core::ops::function::FnOnce<tuple$<>,assoc$<Output,tuple$<> > >,core::marker::Send>,alloc::alloc::Global> >
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-app-context-0.5.3\src\lib.rs:1035
      1033 │     pub fn with_context<R>(&'static self, value: &mut Option<Arc<T>>, f: impl FnOnce() -> R) -> R {
      1034 │         let mut r = None;
      1035 >         let f = || r = Some(f());
      1036 │         #[cfg(feature = "dyn_closure")]
      1037 │         let f: Box<dyn FnOnce()> = Box::new(f);

  31: core::ops::function::FnOnce::call_once<zng_app_context::impl$23::with_context::closure_env$0<enum2$<core::option::Option<zng_var::animation::ModifyInfo> >,tuple$<>,alloc::boxed::Box<dyn$<core::ops::function::FnOnce<tuple$<>,assoc$<Output,tuple$<> > >,core
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\core\src\ops\function.rs:250
  32: alloc::boxed::impl$48::call_once<tuple$<>,dyn$<core::ops::function::FnOnce<tuple$<>,assoc$<Output,tuple$<> > > >,alloc::alloc::Global>
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\alloc\src\boxed.rs:2022
  33: zng_app_context::LocalContext::with_value_ctx<enum2$<core::option::Option<zng_var::animation::ModifyInfo> >,alloc::boxed::Box<dyn$<core::ops::function::FnOnce<tuple$<>,assoc$<Output,tuple$<> > > >,alloc::alloc::Global> >
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-app-context-0.5.3\src\lib.rs:400
  34: zng_app_context::ContextLocal<enum2$<core::option::Option<zng_var::animation::ModifyInfo> > >::with_context<enum2$<core::option::Option<zng_var::animation::ModifyInfo> >,tuple$<>,alloc::boxed::Box<dyn$<core::ops::function::FnOnce<tuple$<>,assoc$<Output,tu
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-app-context-0.5.3\src\lib.rs:1039
  35: zng_var::vars::impl$4::apply_updates_and_after::update_each_and_bindings
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-var-0.4.7\src\vars.rs:403
  36: zng_var::vars::VARS_APP::apply_updates_and_after
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-var-0.4.7\src\vars.rs:382
  37: zng_var::vars::VARS_APP::apply_updates
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-var-0.4.7\src\vars.rs:351
  38: zng_app::update::UPDATES::apply_updates
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-app-0.9.3\src\update.rs:998
  39: zng_app::running::impl$0::apply_updates::closure$0<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc::Global>,zng_app::DynAppEventObserver>
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-app-0.9.3\src\running.rs:773
  40: zng_app::running::LoopMonitor::update<zng_app::running::impl$0::apply_updates::closure_env$0<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc::Global>,zng_app::DynAppEventObserver> >
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-app-0.9.3\src\running.rs:1034
  41: zng_app::running::RunningApp<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc::Global> >::apply_updates<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::al
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-app-0.9.3\src\running.rs:751
  42: zng_app::running::RunningApp<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc::Global> >::poll_impl<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc:
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-app-0.9.3\src\running.rs:703
  43: zng_app::running::RunningApp<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc::Global> >::run_headed<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-app-0.9.3\src\running.rs:522
  44: zng_app::AppExtended<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc::Global> >::run_dyn
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-app-0.9.3\src\lib.rs:1252
  45: zng_app::AppExtended<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc::Global> >::run_impl<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc::Global>,
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-app-0.9.3\src\lib.rs:1289
  46: zng_app::AppExtended<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc::Global> >::run<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc::Global>,enum2
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-app-0.9.3\src\lib.rs:1382
  47: zng_ext_window::impl$1::run_window<alloc::vec::Vec<alloc::boxed::Box<dyn$<zng_app::AppExtensionBoxed>,alloc::alloc::Global>,alloc::alloc::Global>,enum2$<zng_note::main_window::async_fn_env$0> >
      at C:\Users\[user]\.cargo\registry\src\index.crates.io-6f17d22bba15001f\zng-ext-window-0.3.13\src\lib.rs:170
  48: zng_note::main
      at .\src\main.rs:7
  49: core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\core\src\ops\function.rs:250
  50: core::hint::black_box
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\core\src\hint.rs:338
  51: std::sys_common::backtrace::__rust_begin_short_backtrace<void (*)(),tuple$<> >
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\std\src\sys_common\backtrace.rs:155
  52: std::rt::lang_start::closure$0<tuple$<> >
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\std\src\rt.rs:159
  53: std::rt::lang_start_internal
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13/library\std\src\rt.rs:141
  54: std::rt::lang_start<tuple$<> >
      at /rustc/8f9080db423ca0fb6bef0686ce9a93940cdf1f13\library\std\src\rt.rs:158
  55: main
  56: invoke_main
      at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
  57: __scrt_common_main_seh
      at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
  58: BaseThreadInitThunk
  59: RtlUserThreadStart

```

</details>


Discussion / hints:
Thanks for the report, it looks like an issue with the caret blinking animation having a very large interval, does the caret blink in other apps in your system?

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 814f8fbe7..1fa847689 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
 # Unreleased
 
+* `DInstant` addition now also saturates like subtraction.
 * Add `return_focus_on_deinit`.
 * Add a copy button to the markdown links popup.
 * Add warning for slow event handlers in debug builds.
diff --git a/crates/zng-ext-input/src/keyboard.rs b/crates/zng-ext-input/src/keyboard.rs
index 81bede161..bec2907cc 100644
--- a/crates/zng-ext-input/src/keyboard.rs
+++ b/crates/zng-ext-input/src/keyboard.rs
@@ -507,7 +507,7 @@ impl KeyboardService {
 
         var.animate(move |anim, vm| {
             let (interval, timeout) = cfg.get();
-            if anim.start_time().elapsed() >= timeout {
+            if anim.start_time().elapsed() >= timeout || interval == Duration::MAX {
                 if **vm != one {
                     vm.set(one);
                 }
diff --git a/crates/zng-time/src/lib.rs b/crates/zng-time/src/lib.rs
index 81a7dd25f..6e5e89f95 100644
--- a/crates/zng-time/src/lib.rs
+++ b/crates/zng-time/src/lib.rs
@@ -204,24 +204,24 @@ impl ops::Add<Duration> for DInstant {
     type Output = Self;
 
     fn add(self, rhs: Duration) -> Self {
-        Self(self.0 + rhs)
+        Self(self.0.saturating_add(rhs))
     }
 }
 impl ops::AddAssign<Duration> for DInstant {
     fn add_assign(&mut self, rhs: Duration) {
-        self.0 += rhs;
+        self.0 = self.0.saturating_add(rhs);
     }
 }
 impl ops::Sub<Duration> for DInstant {
     type Output = Self;
 
     fn sub(self, rhs: Duration) -> Self {
-        Self(self.0 - rhs)
+        Self(self.0.saturating_sub(rhs))
     }
 }
 impl ops::SubAssign<Duration> for DInstant {
     fn sub_assign(&mut self, rhs: Duration) {
-        self.0 -= rhs;
+        self.0 = self.0.saturating_sub(rhs);
     }
 }
 impl ops::Sub for DInstant {


Test patch: