iced/
daemon.rs

1//! Create and run daemons that run in the background.
2use crate::application;
3use crate::program::{self, Program};
4use crate::shell;
5use crate::theme;
6use crate::window;
7use crate::{Element, Executor, Font, Result, Settings, Subscription, Task};
8
9use iced_debug as debug;
10
11use std::borrow::Cow;
12
13/// Creates an iced [`Daemon`] given its boot, update, and view logic.
14///
15/// A [`Daemon`] will not open a window by default, but will run silently
16/// instead until a [`Task`] from [`window::open`] is returned by its update logic.
17///
18/// Furthermore, a [`Daemon`] will not stop running when all its windows are closed.
19/// In order to completely terminate a [`Daemon`], its process must be interrupted or
20/// its update logic must produce a [`Task`] from [`exit`].
21///
22/// [`exit`]: crate::exit
23pub fn daemon<State, Message, Theme, Renderer>(
24    boot: impl application::Boot<State, Message>,
25    update: impl application::Update<State, Message>,
26    view: impl for<'a> View<'a, State, Message, Theme, Renderer>,
27) -> Daemon<impl Program<State = State, Message = Message, Theme = Theme>>
28where
29    State: 'static,
30    Message: program::Message + 'static,
31    Theme: Default + theme::Base,
32    Renderer: program::Renderer,
33{
34    use std::marker::PhantomData;
35
36    struct Instance<State, Message, Theme, Renderer, Boot, Update, View> {
37        boot: Boot,
38        update: Update,
39        view: View,
40        _state: PhantomData<State>,
41        _message: PhantomData<Message>,
42        _theme: PhantomData<Theme>,
43        _renderer: PhantomData<Renderer>,
44    }
45
46    impl<State, Message, Theme, Renderer, Boot, Update, View> Program
47        for Instance<State, Message, Theme, Renderer, Boot, Update, View>
48    where
49        Message: program::Message + 'static,
50        Theme: Default + theme::Base,
51        Renderer: program::Renderer,
52        Boot: application::Boot<State, Message>,
53        Update: application::Update<State, Message>,
54        View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
55    {
56        type State = State;
57        type Message = Message;
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_daemon")
66        }
67
68        fn boot(&self) -> (Self::State, Task<Self::Message>) {
69            self.boot.boot()
70        }
71
72        fn update(
73            &self,
74            state: &mut Self::State,
75            message: Self::Message,
76        ) -> Task<Self::Message> {
77            debug::hot(|| self.update.update(state, message))
78        }
79
80        fn view<'a>(
81            &self,
82            state: &'a Self::State,
83            window: window::Id,
84        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
85            debug::hot(|| self.view.view(state, window))
86        }
87    }
88
89    Daemon {
90        raw: Instance {
91            boot,
92            update,
93            view,
94            _state: PhantomData,
95            _message: PhantomData,
96            _theme: PhantomData,
97            _renderer: PhantomData,
98        },
99        settings: Settings::default(),
100    }
101}
102
103/// The underlying definition and configuration of an iced daemon.
104///
105/// You can use this API to create and run iced applications
106/// step by step—without coupling your logic to a trait
107/// or a specific type.
108///
109/// You can create a [`Daemon`] with the [`daemon`] helper.
110#[derive(Debug)]
111pub struct Daemon<P: Program> {
112    raw: P,
113    settings: Settings,
114}
115
116impl<P: Program> Daemon<P> {
117    /// Runs the [`Daemon`].
118    pub fn run(self) -> Result
119    where
120        Self: 'static,
121    {
122        #[cfg(all(feature = "debug", not(target_arch = "wasm32")))]
123        let program = {
124            iced_debug::init(iced_debug::Metadata {
125                name: P::name(),
126                theme: None,
127                can_time_travel: cfg!(feature = "time-travel"),
128            });
129
130            iced_devtools::attach(self.raw)
131        };
132
133        #[cfg(any(not(feature = "debug"), target_arch = "wasm32"))]
134        let program = self.raw;
135
136        Ok(shell::run(program, self.settings, None)?)
137    }
138
139    /// Sets the [`Settings`] that will be used to run the [`Daemon`].
140    pub fn settings(self, settings: Settings) -> Self {
141        Self { settings, ..self }
142    }
143
144    /// Sets the [`Settings::antialiasing`] of the [`Daemon`].
145    pub fn antialiasing(self, antialiasing: bool) -> Self {
146        Self {
147            settings: Settings {
148                antialiasing,
149                ..self.settings
150            },
151            ..self
152        }
153    }
154
155    /// Sets the default [`Font`] of the [`Daemon`].
156    pub fn default_font(self, default_font: Font) -> Self {
157        Self {
158            settings: Settings {
159                default_font,
160                ..self.settings
161            },
162            ..self
163        }
164    }
165
166    /// Adds a font to the list of fonts that will be loaded at the start of the [`Daemon`].
167    pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self {
168        self.settings.fonts.push(font.into());
169        self
170    }
171
172    /// Sets the [`Title`] of the [`Daemon`].
173    pub fn title(
174        self,
175        title: impl Title<P::State>,
176    ) -> Daemon<
177        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
178    > {
179        Daemon {
180            raw: program::with_title(self.raw, move |state, window| {
181                debug::hot(|| title.title(state, window))
182            }),
183            settings: self.settings,
184        }
185    }
186
187    /// Sets the subscription logic of the [`Daemon`].
188    pub fn subscription(
189        self,
190        f: impl Fn(&P::State) -> Subscription<P::Message>,
191    ) -> Daemon<
192        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
193    > {
194        Daemon {
195            raw: program::with_subscription(self.raw, move |state| {
196                debug::hot(|| f(state))
197            }),
198            settings: self.settings,
199        }
200    }
201
202    /// Sets the theme logic of the [`Daemon`].
203    pub fn theme(
204        self,
205        f: impl Fn(&P::State, window::Id) -> P::Theme,
206    ) -> Daemon<
207        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
208    > {
209        Daemon {
210            raw: program::with_theme(self.raw, move |state, window| {
211                debug::hot(|| f(state, window))
212            }),
213            settings: self.settings,
214        }
215    }
216
217    /// Sets the style logic of the [`Daemon`].
218    pub fn style(
219        self,
220        f: impl Fn(&P::State, &P::Theme) -> theme::Style,
221    ) -> Daemon<
222        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
223    > {
224        Daemon {
225            raw: program::with_style(self.raw, move |state, theme| {
226                debug::hot(|| f(state, theme))
227            }),
228            settings: self.settings,
229        }
230    }
231
232    /// Sets the scale factor of the [`Daemon`].
233    pub fn scale_factor(
234        self,
235        f: impl Fn(&P::State, window::Id) -> f64,
236    ) -> Daemon<
237        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
238    > {
239        Daemon {
240            raw: program::with_scale_factor(self.raw, move |state, window| {
241                debug::hot(|| f(state, window))
242            }),
243            settings: self.settings,
244        }
245    }
246
247    /// Sets the executor of the [`Daemon`].
248    pub fn executor<E>(
249        self,
250    ) -> Daemon<
251        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
252    >
253    where
254        E: Executor,
255    {
256        Daemon {
257            raw: program::with_executor::<P, E>(self.raw),
258            settings: self.settings,
259        }
260    }
261}
262
263/// The title logic of some [`Daemon`].
264///
265/// This trait is implemented both for `&static str` and
266/// any closure `Fn(&State, window::Id) -> String`.
267///
268/// This trait allows the [`daemon`] builder to take any of them.
269pub trait Title<State> {
270    /// Produces the title of the [`Daemon`].
271    fn title(&self, state: &State, window: window::Id) -> String;
272}
273
274impl<State> Title<State> for &'static str {
275    fn title(&self, _state: &State, _window: window::Id) -> String {
276        self.to_string()
277    }
278}
279
280impl<T, State> Title<State> for T
281where
282    T: Fn(&State, window::Id) -> String,
283{
284    fn title(&self, state: &State, window: window::Id) -> String {
285        self(state, window)
286    }
287}
288
289/// The view logic of some [`Daemon`].
290///
291/// This trait allows the [`daemon`] builder to take any closure that
292/// returns any `Into<Element<'_, Message>>`.
293pub trait View<'a, State, Message, Theme, Renderer> {
294    /// Produces the widget of the [`Daemon`].
295    fn view(
296        &self,
297        state: &'a State,
298        window: window::Id,
299    ) -> Element<'a, Message, Theme, Renderer>;
300}
301
302impl<'a, T, State, Message, Theme, Renderer, Widget>
303    View<'a, State, Message, Theme, Renderer> for T
304where
305    T: Fn(&'a State, window::Id) -> Widget,
306    State: 'static,
307    Widget: Into<Element<'a, Message, Theme, Renderer>>,
308{
309    fn view(
310        &self,
311        state: &'a State,
312        window: window::Id,
313    ) -> Element<'a, Message, Theme, Renderer> {
314        self(state, window).into()
315    }
316}