iced/application/
timed.rs

1//! An [`Application`] that receives an [`Instant`] in update logic.
2use crate::application::{Application, Boot, View};
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 Boot<State, Message>,
22    update: impl Update<State, Message>,
23    subscription: impl Fn(&State) -> Subscription<Message>,
24    view: impl for<'a> View<'a, State, Message, Theme, Renderer>,
25) -> Application<
26    impl Program<State = State, Message = (Message, Instant), Theme = Theme>,
27>
28where
29    State: 'static,
30    Message: program::Message + 'static,
31    Theme: Default + theme::Base + 'static,
32    Renderer: program::Renderer + 'static,
33{
34    use std::marker::PhantomData;
35
36    struct Instance<
37        State,
38        Message,
39        Theme,
40        Renderer,
41        Boot,
42        Update,
43        Subscription,
44        View,
45    > {
46        boot: Boot,
47        update: Update,
48        subscription: Subscription,
49        view: View,
50        _state: PhantomData<State>,
51        _message: PhantomData<Message>,
52        _theme: PhantomData<Theme>,
53        _renderer: PhantomData<Renderer>,
54    }
55
56    impl<State, Message, Theme, Renderer, Boot, Update, Subscription, View>
57        Program
58        for Instance<
59            State,
60            Message,
61            Theme,
62            Renderer,
63            Boot,
64            Update,
65            Subscription,
66            View,
67        >
68    where
69        Message: program::Message + 'static,
70        Theme: Default + theme::Base + 'static,
71        Renderer: program::Renderer + 'static,
72        Boot: self::Boot<State, Message>,
73        Update: self::Update<State, Message>,
74        Subscription: Fn(&State) -> self::Subscription<Message>,
75        View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
76    {
77        type State = State;
78        type Message = (Message, Instant);
79        type Theme = Theme;
80        type Renderer = Renderer;
81        type Executor = iced_futures::backend::default::Executor;
82
83        fn name() -> &'static str {
84            let name = std::any::type_name::<State>();
85
86            name.split("::").next().unwrap_or("a_cool_application")
87        }
88
89        fn boot(&self) -> (State, Task<Self::Message>) {
90            let (state, task) = self.boot.boot();
91
92            (state, task.map(|message| (message, Instant::now())))
93        }
94
95        fn update(
96            &self,
97            state: &mut Self::State,
98            (message, now): Self::Message,
99        ) -> Task<Self::Message> {
100            self.update
101                .update(state, message, now)
102                .into()
103                .map(|message| (message, Instant::now()))
104        }
105
106        fn view<'a>(
107            &self,
108            state: &'a Self::State,
109            _window: window::Id,
110        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
111            self.view
112                .view(state)
113                .map(|message| (message, Instant::now()))
114        }
115
116        fn subscription(
117            &self,
118            state: &Self::State,
119        ) -> self::Subscription<Self::Message> {
120            (self.subscription)(state).map(|message| (message, Instant::now()))
121        }
122    }
123
124    Application {
125        raw: Instance {
126            boot,
127            update,
128            subscription,
129            view,
130            _state: PhantomData,
131            _message: PhantomData,
132            _theme: PhantomData,
133            _renderer: PhantomData,
134        },
135        settings: Settings::default(),
136        window: window::Settings::default(),
137    }
138}
139
140/// The update logic of some timed [`Application`].
141///
142/// This is like [`application::Update`](super::Update),
143/// but it also takes an [`Instant`].
144pub trait Update<State, Message> {
145    /// Processes the message and updates the state of the [`Application`].
146    fn update(
147        &self,
148        state: &mut State,
149        message: Message,
150        now: Instant,
151    ) -> impl Into<Task<Message>>;
152}
153
154impl<State, Message> Update<State, Message> for () {
155    fn update(
156        &self,
157        _state: &mut State,
158        _message: Message,
159        _now: Instant,
160    ) -> impl Into<Task<Message>> {
161    }
162}
163
164impl<T, State, Message, C> Update<State, Message> for T
165where
166    T: Fn(&mut State, Message, Instant) -> C,
167    C: Into<Task<Message>>,
168{
169    fn update(
170        &self,
171        state: &mut State,
172        message: Message,
173        now: Instant,
174    ) -> impl Into<Task<Message>> {
175        self(state, message, now)
176    }
177}