Skip to main content

iced/
daemon.rs

1//! Create and run daemons that run in the background.
2use crate::application;
3use crate::core;
4use crate::message;
5use crate::program::{self, Program};
6use crate::shell;
7use crate::theme;
8use crate::window;
9use crate::{Executor, Font, Preset, Result, Settings, Subscription, Task, Theme};
10
11#[cfg(feature = "hot")]
12use crate::hot::Hot;
13
14use std::borrow::Cow;
15
16/// Creates an iced [`Daemon`] given its boot, update, and view logic.
17///
18/// A [`Daemon`] will not open a window by default, but will run silently
19/// instead until a [`Task`] from [`window::open`] is returned by its update logic.
20///
21/// Furthermore, a [`Daemon`] will not stop running when all its windows are closed.
22/// In order to completely terminate a [`Daemon`], its process must be interrupted or
23/// its update logic must produce a [`Task`] from [`exit`].
24///
25/// [`exit`]: crate::exit
26pub fn daemon<State, Message, Theme, Renderer>(
27    boot: impl application::BootFn<State, Message>,
28    update: impl application::UpdateFn<State, Message>,
29    view: impl for<'a> ViewFn<'a, State, Message, Theme, Renderer>,
30) -> Daemon<impl Program<State = State, Message = Message, Theme = Theme>>
31where
32    State: 'static,
33    Message: Send + 'static,
34    Theme: theme::Base,
35    Renderer: program::Renderer,
36{
37    use std::marker::PhantomData;
38
39    struct Instance<State, Message, Theme, Renderer, Boot, Update, View> {
40        boot: Boot,
41        update: Update,
42        view: View,
43        _state: PhantomData<State>,
44        _message: PhantomData<Message>,
45        _theme: PhantomData<Theme>,
46        _renderer: PhantomData<Renderer>,
47    }
48
49    impl<State, Message, Theme, Renderer, Boot, Update, View> Program
50        for Instance<State, Message, Theme, Renderer, Boot, Update, View>
51    where
52        Message: Send + 'static,
53        Theme: theme::Base,
54        Renderer: program::Renderer,
55        Boot: application::BootFn<State, Message>,
56        Update: application::UpdateFn<State, Message>,
57        View: for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>,
58    {
59        type State = State;
60        type Message = Message;
61        type Theme = Theme;
62        type Renderer = Renderer;
63        type Executor = iced_futures::backend::default::Executor;
64
65        fn name() -> &'static str {
66            let name = std::any::type_name::<State>();
67
68            name.split("::").next().unwrap_or("a_cool_daemon")
69        }
70
71        fn settings(&self) -> Settings {
72            Settings::default()
73        }
74
75        fn window(&self) -> Option<iced_core::window::Settings> {
76            None
77        }
78
79        fn boot(&self) -> (Self::State, Task<Self::Message>) {
80            self.boot.boot()
81        }
82
83        fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
84            self.update.update(state, message)
85        }
86
87        fn view<'a>(
88            &self,
89            state: &'a Self::State,
90            window: window::Id,
91        ) -> impl core::Widget<Self::Message, Self::Theme, Self::Renderer> + 'a {
92            self.view.view(state, window)
93        }
94    }
95
96    Daemon {
97        raw: Instance {
98            boot,
99            update,
100            view,
101            _state: PhantomData,
102            _message: PhantomData,
103            _theme: PhantomData,
104            _renderer: PhantomData,
105        },
106        settings: Settings::default(),
107        presets: Vec::new(),
108    }
109}
110
111/// The underlying definition and configuration of an iced daemon.
112///
113/// You can use this API to create and run iced applications
114/// step by step—without coupling your logic to a trait
115/// or a specific type.
116///
117/// You can create a [`Daemon`] with the [`daemon`] helper.
118#[derive(Debug)]
119pub struct Daemon<P: Program> {
120    raw: P,
121    settings: Settings,
122    presets: Vec<Preset<P::State, P::Message>>,
123}
124
125impl<P: Program> Daemon<P> {
126    /// Runs the [`Daemon`].
127    pub fn run(self) -> Result
128    where
129        Self: 'static,
130        P::Message: message::MaybeDebug + message::MaybeClone,
131    {
132        #[cfg(feature = "debug")]
133        iced_debug::init(iced_debug::Metadata {
134            name: P::name(),
135            theme: None,
136            can_time_travel: cfg!(feature = "time-travel"),
137        });
138
139        #[cfg(feature = "tester")]
140        let program = iced_tester::attach(self);
141
142        #[cfg(all(feature = "debug", not(feature = "tester")))]
143        let program = iced_devtools::attach(self);
144
145        #[cfg(not(any(feature = "tester", feature = "debug")))]
146        let program = self;
147
148        #[cfg(feature = "hot")]
149        let program = Hot::new(program);
150
151        Ok(shell::run(program)?)
152    }
153
154    /// Sets the [`Settings`] that will be used to run the [`Daemon`].
155    pub fn settings(self, settings: Settings) -> Self {
156        Self { settings, ..self }
157    }
158
159    /// Sets the [`Settings::antialiasing`] of the [`Daemon`].
160    pub fn antialiasing(self, antialiasing: bool) -> Self {
161        Self {
162            settings: Settings {
163                antialiasing,
164                ..self.settings
165            },
166            ..self
167        }
168    }
169
170    /// Sets the default [`Font`] of the [`Daemon`].
171    pub fn default_font(self, default_font: Font) -> Self {
172        Self {
173            settings: Settings {
174                font: default_font,
175                ..self.settings
176            },
177            ..self
178        }
179    }
180
181    /// Adds a font to the list of fonts that will be loaded at the start of the [`Daemon`].
182    pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self {
183        self.settings.fonts.push(font.into());
184        self
185    }
186
187    /// Sets the title of the [`Daemon`].
188    pub fn title(
189        self,
190        title: impl TitleFn<P::State>,
191    ) -> Daemon<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
192        Daemon {
193            raw: WithTitle {
194                program: self.raw,
195                title,
196            },
197            settings: self.settings,
198            presets: self.presets,
199        }
200    }
201
202    /// Sets the subscription logic of the [`Daemon`].
203    pub fn subscription(
204        self,
205        f: impl Fn(&P::State) -> Subscription<P::Message>,
206    ) -> Daemon<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
207        Daemon {
208            raw: program::with_subscription(self.raw, f),
209            settings: self.settings,
210            presets: self.presets,
211        }
212    }
213
214    /// Sets the theme logic of the [`Daemon`].
215    pub fn theme(
216        self,
217        f: impl ThemeFn<P::State, P::Theme>,
218    ) -> Daemon<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
219        Daemon {
220            raw: WithTheme {
221                program: self.raw,
222                theme: f,
223            },
224            settings: self.settings,
225            presets: self.presets,
226        }
227    }
228
229    /// Sets the style logic of the [`Daemon`].
230    pub fn style(
231        self,
232        f: impl Fn(&P::State, &P::Theme) -> theme::Style,
233    ) -> Daemon<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
234        Daemon {
235            raw: program::with_style(self.raw, f),
236            settings: self.settings,
237            presets: self.presets,
238        }
239    }
240
241    /// Sets the scale factor of the [`Daemon`].
242    pub fn scale_factor(
243        self,
244        f: impl Fn(&P::State, window::Id) -> f32,
245    ) -> Daemon<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
246        Daemon {
247            raw: WithScaleFactor {
248                program: self.raw,
249                scale_factor: f,
250            },
251            settings: self.settings,
252            presets: self.presets,
253        }
254    }
255
256    /// Sets the executor of the [`Daemon`].
257    pub fn executor<E>(
258        self,
259    ) -> Daemon<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>>
260    where
261        E: Executor,
262    {
263        Daemon {
264            raw: program::with_executor::<P, E>(self.raw),
265            settings: self.settings,
266            presets: self.presets,
267        }
268    }
269
270    /// Sets the boot presets of the [`Daemon`].
271    ///
272    /// Presets can be used to override the default booting strategy
273    /// of your application during testing to create reproducible
274    /// environments.
275    pub fn presets(self, presets: impl IntoIterator<Item = Preset<P::State, P::Message>>) -> Self {
276        Self {
277            presets: presets.into_iter().collect(),
278            ..self
279        }
280    }
281}
282
283impl<P: Program> Program for Daemon<P> {
284    type State = P::State;
285    type Message = P::Message;
286    type Theme = P::Theme;
287    type Renderer = P::Renderer;
288    type Executor = P::Executor;
289
290    fn name() -> &'static str {
291        P::name()
292    }
293
294    fn settings(&self) -> Settings {
295        self.settings.clone()
296    }
297
298    fn window(&self) -> Option<window::Settings> {
299        None
300    }
301
302    fn boot(&self) -> (Self::State, Task<Self::Message>) {
303        self.raw.boot()
304    }
305
306    #[inline]
307    fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
308        self.raw.update(state, message)
309    }
310
311    #[inline]
312    fn view<'a>(
313        &self,
314        state: &'a Self::State,
315        window: window::Id,
316    ) -> impl core::Widget<Self::Message, Self::Theme, Self::Renderer> + 'a {
317        self.raw.view(state, window)
318    }
319
320    #[inline]
321    fn title(&self, state: &Self::State, window: window::Id) -> String {
322        self.raw.title(state, window)
323    }
324
325    #[inline]
326    fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
327        self.raw.subscription(state)
328    }
329
330    #[inline]
331    fn theme(&self, state: &Self::State, window: iced_core::window::Id) -> Option<Self::Theme> {
332        self.raw.theme(state, window)
333    }
334
335    #[inline]
336    fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
337        self.raw.style(state, theme)
338    }
339
340    #[inline]
341    fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
342        self.raw.scale_factor(state, window)
343    }
344
345    fn presets(&self) -> &[Preset<Self::State, Self::Message>] {
346        &self.presets
347    }
348}
349
350/// The title logic of some [`Daemon`].
351///
352/// This trait is implemented both for `&static str` and
353/// any closure `Fn(&State, window::Id) -> String`.
354///
355/// This trait allows the [`daemon`] builder to take any of them.
356pub trait TitleFn<State> {
357    /// Produces the title of the [`Daemon`].
358    fn title(&self, state: &State, window: window::Id) -> String;
359}
360
361impl<State> TitleFn<State> for &'static str {
362    fn title(&self, _state: &State, _window: window::Id) -> String {
363        self.to_string()
364    }
365}
366
367impl<T, State> TitleFn<State> for T
368where
369    T: Fn(&State, window::Id) -> String,
370{
371    fn title(&self, state: &State, window: window::Id) -> String {
372        self(state, window)
373    }
374}
375
376/// The view logic of some [`Daemon`].
377///
378/// This trait allows the [`daemon`] builder to take any closure that
379/// returns any widget.
380pub trait ViewFn<'a, State, Message, Theme, Renderer> {
381    /// Produces the widget of the [`Daemon`].
382    fn view(
383        &self,
384        state: &'a State,
385        window: window::Id,
386    ) -> impl core::Widget<Message, Theme, Renderer> + 'a;
387}
388
389impl<'a, T, State, Message, Theme, Renderer, W> ViewFn<'a, State, Message, Theme, Renderer> for T
390where
391    T: Fn(&'a State, window::Id) -> W,
392    State: 'static,
393    W: core::Widget<Message, Theme, Renderer> + 'a,
394{
395    #[inline]
396    fn view(
397        &self,
398        state: &'a State,
399        window: window::Id,
400    ) -> impl core::Widget<Message, Theme, Renderer> + 'a {
401        self(state, window)
402    }
403}
404
405/// The theme logic of some [`Daemon`].
406///
407/// Any implementors of this trait can be provided as an argument to
408/// [`Daemon::theme`].
409///
410/// `iced` provides two implementors:
411/// - the built-in [`Theme`] itself
412/// - and any `Fn(&State, window::Id) -> impl Into<Option<Theme>>`.
413pub trait ThemeFn<State, Theme> {
414    /// Returns the theme of the [`Daemon`] for the current state and window.
415    ///
416    /// If `None` is returned, `iced` will try to use a theme that
417    /// matches the system color scheme.
418    fn theme(&self, state: &State, window: window::Id) -> Option<Theme>;
419}
420
421impl<State> ThemeFn<State, Theme> for Theme {
422    fn theme(&self, _state: &State, _window: window::Id) -> Option<Theme> {
423        Some(self.clone())
424    }
425}
426
427impl<F, T, State, Theme> ThemeFn<State, Theme> for F
428where
429    F: Fn(&State, window::Id) -> T,
430    T: Into<Option<Theme>>,
431{
432    fn theme(&self, state: &State, window: window::Id) -> Option<Theme> {
433        (self)(state, window).into()
434    }
435}
436
437/// Decorates a [`Program`] with a window-aware [`TitleFn`].
438struct WithTitle<P, Title> {
439    program: P,
440    title: Title,
441}
442
443impl<P, Title> Program for WithTitle<P, Title>
444where
445    P: Program,
446    Title: TitleFn<P::State>,
447{
448    type State = P::State;
449    type Message = P::Message;
450    type Theme = P::Theme;
451    type Renderer = P::Renderer;
452    type Executor = P::Executor;
453
454    fn name() -> &'static str {
455        P::name()
456    }
457
458    #[inline]
459    fn settings(&self) -> Settings {
460        self.program.settings()
461    }
462
463    #[inline]
464    fn window(&self) -> Option<window::Settings> {
465        self.program.window()
466    }
467
468    #[inline]
469    fn boot(&self) -> (Self::State, Task<Self::Message>) {
470        self.program.boot()
471    }
472
473    #[inline]
474    fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
475        self.program.update(state, message)
476    }
477
478    #[inline]
479    fn view<'a>(
480        &self,
481        state: &'a Self::State,
482        window: window::Id,
483    ) -> impl core::Widget<Self::Message, Self::Theme, Self::Renderer> + 'a {
484        self.program.view(state, window)
485    }
486
487    #[inline]
488    fn title(&self, state: &Self::State, window: window::Id) -> String {
489        self.title.title(state, window)
490    }
491
492    #[inline]
493    fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
494        self.program.subscription(state)
495    }
496
497    #[inline]
498    fn theme(&self, state: &Self::State, window: window::Id) -> Option<Self::Theme> {
499        self.program.theme(state, window)
500    }
501
502    #[inline]
503    fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
504        self.program.style(state, theme)
505    }
506
507    #[inline]
508    fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
509        self.program.scale_factor(state, window)
510    }
511}
512
513/// Decorates a [`Program`] with a window-aware [`ThemeFn`].
514struct WithTheme<P, Theme> {
515    program: P,
516    theme: Theme,
517}
518
519impl<P, Theme> Program for WithTheme<P, Theme>
520where
521    P: Program,
522    Theme: ThemeFn<P::State, P::Theme>,
523{
524    type State = P::State;
525    type Message = P::Message;
526    type Theme = P::Theme;
527    type Renderer = P::Renderer;
528    type Executor = P::Executor;
529
530    fn name() -> &'static str {
531        P::name()
532    }
533
534    #[inline]
535    fn settings(&self) -> Settings {
536        self.program.settings()
537    }
538
539    #[inline]
540    fn window(&self) -> Option<window::Settings> {
541        self.program.window()
542    }
543
544    #[inline]
545    fn boot(&self) -> (Self::State, Task<Self::Message>) {
546        self.program.boot()
547    }
548
549    #[inline]
550    fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
551        self.program.update(state, message)
552    }
553
554    #[inline]
555    fn view<'a>(
556        &self,
557        state: &'a Self::State,
558        window: window::Id,
559    ) -> impl core::Widget<Self::Message, Self::Theme, Self::Renderer> + 'a {
560        self.program.view(state, window)
561    }
562
563    #[inline]
564    fn title(&self, state: &Self::State, window: window::Id) -> String {
565        self.program.title(state, window)
566    }
567
568    #[inline]
569    fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
570        self.program.subscription(state)
571    }
572
573    #[inline]
574    fn theme(&self, state: &Self::State, window: window::Id) -> Option<Self::Theme> {
575        self.theme.theme(state, window)
576    }
577
578    #[inline]
579    fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
580        self.program.style(state, theme)
581    }
582
583    #[inline]
584    fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
585        self.program.scale_factor(state, window)
586    }
587}
588
589/// Decorates a [`Program`] with a window-aware scale factor function.
590struct WithScaleFactor<P, F> {
591    program: P,
592    scale_factor: F,
593}
594
595impl<P, F> Program for WithScaleFactor<P, F>
596where
597    P: Program,
598    F: Fn(&P::State, window::Id) -> f32,
599{
600    type State = P::State;
601    type Message = P::Message;
602    type Theme = P::Theme;
603    type Renderer = P::Renderer;
604    type Executor = P::Executor;
605
606    fn name() -> &'static str {
607        P::name()
608    }
609
610    #[inline]
611    fn settings(&self) -> Settings {
612        self.program.settings()
613    }
614
615    #[inline]
616    fn window(&self) -> Option<window::Settings> {
617        self.program.window()
618    }
619
620    #[inline]
621    fn boot(&self) -> (Self::State, Task<Self::Message>) {
622        self.program.boot()
623    }
624
625    #[inline]
626    fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
627        self.program.update(state, message)
628    }
629
630    #[inline]
631    fn view<'a>(
632        &self,
633        state: &'a Self::State,
634        window: window::Id,
635    ) -> impl core::Widget<Self::Message, Self::Theme, Self::Renderer> + 'a {
636        self.program.view(state, window)
637    }
638
639    #[inline]
640    fn title(&self, state: &Self::State, window: window::Id) -> String {
641        self.program.title(state, window)
642    }
643
644    #[inline]
645    fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
646        self.program.subscription(state)
647    }
648
649    #[inline]
650    fn theme(&self, state: &Self::State, window: window::Id) -> Option<Self::Theme> {
651        self.program.theme(state, window)
652    }
653
654    #[inline]
655    fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
656        self.program.style(state, theme)
657    }
658
659    #[inline]
660    fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
661        (self.scale_factor)(state, window)
662    }
663}