Skip to main content

iced/application/
timed.rs

1//! An [`Application`] that receives an [`Instant`] in update logic.
2use crate::application::{Application, BootFn, ViewFn};
3use crate::program;
4use crate::theme;
5use crate::time::Instant;
6use crate::window;
7use crate::{Element, Program, Settings, Subscription, Task};
8
9/// Creates an [`Application`] with an `update` function that also
10/// takes the [`Instant`] of each `Message`.
11///
12/// This constructor is useful to create animated applications that
13/// are _pure_ (e.g. without relying on side-effect calls like [`Instant::now`]).
14///
15/// Purity is needed when you want your application to end up in the
16/// same exact state given the same history of messages. This property
17/// enables proper time traveling debugging with [`comet`].
18///
19/// [`comet`]: https://github.com/iced-rs/comet
20pub fn timed<State, Message, Theme, Renderer>(
21    boot: impl BootFn<State, Message>,
22    update: impl UpdateFn<State, Message>,
23    subscription: impl Fn(&State) -> Subscription<Message>,
24    view: impl for<'a> ViewFn<'a, State, Message, Theme, Renderer>,
25) -> Application<impl Program<State = State, Message = (Message, Instant), Theme = Theme>>
26where
27    State: 'static,
28    Message: Send + 'static,
29    Theme: theme::Base + 'static,
30    Renderer: program::Renderer + 'static,
31{
32    use std::marker::PhantomData;
33
34    struct Instance<State, Message, Theme, Renderer, Boot, Update, Subscription, View> {
35        boot: Boot,
36        update: Update,
37        subscription: Subscription,
38        view: View,
39        _state: PhantomData<State>,
40        _message: PhantomData<Message>,
41        _theme: PhantomData<Theme>,
42        _renderer: PhantomData<Renderer>,
43    }
44
45    impl<State, Message, Theme, Renderer, Boot, Update, Subscription, View> Program
46        for Instance<State, Message, Theme, Renderer, Boot, Update, Subscription, View>
47    where
48        Message: Send + 'static,
49        Theme: theme::Base + 'static,
50        Renderer: program::Renderer + 'static,
51        Boot: self::BootFn<State, Message>,
52        Update: self::UpdateFn<State, Message>,
53        Subscription: Fn(&State) -> self::Subscription<Message>,
54        View: for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>,
55    {
56        type State = State;
57        type Message = (Message, Instant);
58        type Theme = Theme;
59        type Renderer = Renderer;
60        type Executor = iced_futures::backend::default::Executor;
61
62        fn name() -> &'static str {
63            let name = std::any::type_name::<State>();
64
65            name.split("::").next().unwrap_or("a_cool_application")
66        }
67
68        fn settings(&self) -> Settings {
69            Settings::default()
70        }
71
72        fn window(&self) -> Option<iced_core::window::Settings> {
73            Some(window::Settings::default())
74        }
75
76        fn boot(&self) -> (State, Task<Self::Message>) {
77            let (state, task) = self.boot.boot();
78
79            (state, task.map(|message| (message, Instant::now())))
80        }
81
82        #[inline]
83        fn update(
84            &self,
85            state: &mut Self::State,
86            (message, now): Self::Message,
87        ) -> Task<Self::Message> {
88            self.update
89                .update(state, message, now)
90                .into()
91                .map(|message| (message, Instant::now()))
92        }
93
94        #[inline]
95        fn view<'a>(
96            &self,
97            state: &'a Self::State,
98            _window: window::Id,
99        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
100            self.view
101                .view(state)
102                .map(|message| (message, Instant::now()))
103        }
104
105        #[inline]
106        fn subscription(&self, state: &Self::State) -> self::Subscription<Self::Message> {
107            (self.subscription)(state).map(|message| (message, Instant::now()))
108        }
109    }
110
111    Application {
112        raw: Instance {
113            boot,
114            update,
115            subscription,
116            view,
117            _state: PhantomData,
118            _message: PhantomData,
119            _theme: PhantomData,
120            _renderer: PhantomData,
121        },
122        settings: Settings::default(),
123        window: window::Settings::default(),
124        presets: Vec::new(),
125    }
126}
127
128/// The update logic of some timed [`Application`].
129///
130/// This is like [`application::UpdateFn`](super::UpdateFn),
131/// but it also takes an [`Instant`].
132pub trait UpdateFn<State, Message> {
133    /// Processes the message and updates the state of the [`Application`].
134    fn update(&self, state: &mut State, message: Message, now: Instant)
135    -> impl Into<Task<Message>>;
136}
137
138impl<State, Message> UpdateFn<State, Message> for () {
139    fn update(
140        &self,
141        _state: &mut State,
142        _message: Message,
143        _now: Instant,
144    ) -> impl Into<Task<Message>> {
145    }
146}
147
148impl<T, State, Message, C> UpdateFn<State, Message> for T
149where
150    T: Fn(&mut State, Message, Instant) -> C,
151    C: Into<Task<Message>>,
152{
153    fn update(
154        &self,
155        state: &mut State,
156        message: Message,
157        now: Instant,
158    ) -> impl Into<Task<Message>> {
159        self(state, message, now)
160    }
161}