//! An [`Application`] that receives an [`Instant`] in update logic. use crate::application::{Application, BootFn, ViewFn}; use crate::program; use crate::theme; use crate::time::Instant; use crate::window; use crate::{Element, Settings, Subscription, task::Task}; use iced_debug as debug; use iced_program::Program; /// Creates an [`Application`] with an `update` function that also /// takes the [`Instant`] of each `Message`. /// /// This constructor is useful to create animated applications that /// are _pure_ (e.g. without relying on side-effect calls like [`Instant::now`]). /// /// Purity is needed when you want your application to end up in the /// same exact state given the same history of messages. This property /// enables proper time traveling debugging with [`comet`]. /// /// [`comet`]: https://github.com/iced-rs/comet pub fn timed( boot: impl BootFn, update: impl UpdateFn, subscription: impl Fn(&State) -> Subscription, view: impl for<'a> ViewFn<'a, State, Message, Theme, Renderer>, ) -> Application< impl Program, > where State: 'static, Message: Send + 'static, Theme: theme::Base + 'static, Renderer: program::Renderer + 'static, { use std::marker::PhantomData; struct Instance< State, Message, Theme, Renderer, Boot, Update, Subscription, View, > { boot: Boot, update: Update, subscription: Subscription, view: View, _state: PhantomData, _message: PhantomData, _theme: PhantomData, _renderer: PhantomData, } impl Program for Instance< State, Message, Theme, Renderer, Boot, Update, Subscription, View, > where Message: Send + 'static, Theme: theme::Base + 'static, Renderer: program::Renderer + 'static, Boot: self::BootFn, Update: self::UpdateFn, Subscription: Fn(&State) -> self::Subscription, View: for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>, { type State = State; type Message = (Message, Instant); type Theme = Theme; type Renderer = Renderer; type Executor = iced_futures::backend::default::Executor; fn name() -> &'static str { let name = std::any::type_name::(); name.split("::").next().unwrap_or("a_cool_application") } fn settings(&self) -> Settings { Settings::default() } fn window(&self) -> Option { Some(window::Settings::default()) } fn boot(&self) -> (State, Task) { let (state, task) = self.boot.boot(); (state, task.map(|message| (message, Instant::now()))) } fn update( &self, state: &mut ::State, (message, now): ::Message, ) -> Task<::Message> { debug::hot(move || { self.update .update(state, message, now) .into() .map(|message| (message, Instant::now())) }) } fn view<'a>( &self, state: &'a ::State, _window: window::Id, ) -> Element< 'a, ::Message, ::Theme, ::Renderer, > { debug::hot(|| { self.view .view(state) .map(|message| (message, Instant::now())) }) } fn subscription( &self, state: &::State, ) -> self::Subscription<::Message> { debug::hot(|| { (self.subscription)(state) .map(|message| (message, Instant::now())) }) } } Application { raw: Instance { boot, update, subscription, view, _state: PhantomData, _message: PhantomData, _theme: PhantomData, _renderer: PhantomData, }, settings: Settings::default(), window: window::Settings::default(), presets: Vec::new(), } } /// The update logic of some timed [`Application`]. /// /// This is like [`application::UpdateFn`](super::UpdateFn), /// but it also takes an [`Instant`]. pub trait UpdateFn { /// Processes the message and updates the state of the [`Application`]. fn update( &self, state: &mut State, message: Message, now: Instant, ) -> impl Into>; } impl UpdateFn for () { fn update( &self, _state: &mut State, _message: Message, _now: Instant, ) -> impl Into> { } } impl UpdateFn for T where T: Fn(&mut State, Message, Instant) -> C, C: Into>, { fn update( &self, state: &mut State, message: Message, now: Instant, ) -> impl Into> { self(state, message, now) } }