Skip to main content

nice_plug_iced/application/
timed.rs

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