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>
381where
382    Renderer: core::Renderer,
383{
384    /// Produces the widget of the [`Daemon`].
385    fn view(
386        &self,
387        state: &'a State,
388        window: window::Id,
389    ) -> impl core::Widget<Message, Theme, Renderer> + 'a;
390}
391
392impl<'a, T, State, Message, Theme, Renderer, W> ViewFn<'a, State, Message, Theme, Renderer> for T
393where
394    T: Fn(&'a State, window::Id) -> W,
395    State: 'static,
396    W: core::Widget<Message, Theme, Renderer> + 'a,
397    Renderer: core::Renderer,
398{
399    #[inline]
400    fn view(
401        &self,
402        state: &'a State,
403        window: window::Id,
404    ) -> impl core::Widget<Message, Theme, Renderer> + 'a {
405        self(state, window)
406    }
407}
408
409/// The theme logic of some [`Daemon`].
410///
411/// Any implementors of this trait can be provided as an argument to
412/// [`Daemon::theme`].
413///
414/// `iced` provides two implementors:
415/// - the built-in [`Theme`] itself
416/// - and any `Fn(&State, window::Id) -> impl Into<Option<Theme>>`.
417pub trait ThemeFn<State, Theme> {
418    /// Returns the theme of the [`Daemon`] for the current state and window.
419    ///
420    /// If `None` is returned, `iced` will try to use a theme that
421    /// matches the system color scheme.
422    fn theme(&self, state: &State, window: window::Id) -> Option<Theme>;
423}
424
425impl<State> ThemeFn<State, Theme> for Theme {
426    fn theme(&self, _state: &State, _window: window::Id) -> Option<Theme> {
427        Some(self.clone())
428    }
429}
430
431impl<F, T, State, Theme> ThemeFn<State, Theme> for F
432where
433    F: Fn(&State, window::Id) -> T,
434    T: Into<Option<Theme>>,
435{
436    fn theme(&self, state: &State, window: window::Id) -> Option<Theme> {
437        (self)(state, window).into()
438    }
439}
440
441/// Decorates a [`Program`] with a window-aware [`TitleFn`].
442struct WithTitle<P, Title> {
443    program: P,
444    title: Title,
445}
446
447impl<P, Title> Program for WithTitle<P, Title>
448where
449    P: Program,
450    Title: TitleFn<P::State>,
451{
452    type State = P::State;
453    type Message = P::Message;
454    type Theme = P::Theme;
455    type Renderer = P::Renderer;
456    type Executor = P::Executor;
457
458    fn name() -> &'static str {
459        P::name()
460    }
461
462    #[inline]
463    fn settings(&self) -> Settings {
464        self.program.settings()
465    }
466
467    #[inline]
468    fn window(&self) -> Option<window::Settings> {
469        self.program.window()
470    }
471
472    #[inline]
473    fn boot(&self) -> (Self::State, Task<Self::Message>) {
474        self.program.boot()
475    }
476
477    #[inline]
478    fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
479        self.program.update(state, message)
480    }
481
482    #[inline]
483    fn view<'a>(
484        &self,
485        state: &'a Self::State,
486        window: window::Id,
487    ) -> impl core::Widget<Self::Message, Self::Theme, Self::Renderer> + 'a {
488        self.program.view(state, window)
489    }
490
491    #[inline]
492    fn title(&self, state: &Self::State, window: window::Id) -> String {
493        self.title.title(state, window)
494    }
495
496    #[inline]
497    fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
498        self.program.subscription(state)
499    }
500
501    #[inline]
502    fn theme(&self, state: &Self::State, window: window::Id) -> Option<Self::Theme> {
503        self.program.theme(state, window)
504    }
505
506    #[inline]
507    fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
508        self.program.style(state, theme)
509    }
510
511    #[inline]
512    fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
513        self.program.scale_factor(state, window)
514    }
515}
516
517/// Decorates a [`Program`] with a window-aware [`ThemeFn`].
518struct WithTheme<P, Theme> {
519    program: P,
520    theme: Theme,
521}
522
523impl<P, Theme> Program for WithTheme<P, Theme>
524where
525    P: Program,
526    Theme: ThemeFn<P::State, P::Theme>,
527{
528    type State = P::State;
529    type Message = P::Message;
530    type Theme = P::Theme;
531    type Renderer = P::Renderer;
532    type Executor = P::Executor;
533
534    fn name() -> &'static str {
535        P::name()
536    }
537
538    #[inline]
539    fn settings(&self) -> Settings {
540        self.program.settings()
541    }
542
543    #[inline]
544    fn window(&self) -> Option<window::Settings> {
545        self.program.window()
546    }
547
548    #[inline]
549    fn boot(&self) -> (Self::State, Task<Self::Message>) {
550        self.program.boot()
551    }
552
553    #[inline]
554    fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
555        self.program.update(state, message)
556    }
557
558    #[inline]
559    fn view<'a>(
560        &self,
561        state: &'a Self::State,
562        window: window::Id,
563    ) -> impl core::Widget<Self::Message, Self::Theme, Self::Renderer> + 'a {
564        self.program.view(state, window)
565    }
566
567    #[inline]
568    fn title(&self, state: &Self::State, window: window::Id) -> String {
569        self.program.title(state, window)
570    }
571
572    #[inline]
573    fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
574        self.program.subscription(state)
575    }
576
577    #[inline]
578    fn theme(&self, state: &Self::State, window: window::Id) -> Option<Self::Theme> {
579        self.theme.theme(state, window)
580    }
581
582    #[inline]
583    fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
584        self.program.style(state, theme)
585    }
586
587    #[inline]
588    fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
589        self.program.scale_factor(state, window)
590    }
591}
592
593/// Decorates a [`Program`] with a window-aware scale factor function.
594struct WithScaleFactor<P, F> {
595    program: P,
596    scale_factor: F,
597}
598
599impl<P, F> Program for WithScaleFactor<P, F>
600where
601    P: Program,
602    F: Fn(&P::State, window::Id) -> f32,
603{
604    type State = P::State;
605    type Message = P::Message;
606    type Theme = P::Theme;
607    type Renderer = P::Renderer;
608    type Executor = P::Executor;
609
610    fn name() -> &'static str {
611        P::name()
612    }
613
614    #[inline]
615    fn settings(&self) -> Settings {
616        self.program.settings()
617    }
618
619    #[inline]
620    fn window(&self) -> Option<window::Settings> {
621        self.program.window()
622    }
623
624    #[inline]
625    fn boot(&self) -> (Self::State, Task<Self::Message>) {
626        self.program.boot()
627    }
628
629    #[inline]
630    fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
631        self.program.update(state, message)
632    }
633
634    #[inline]
635    fn view<'a>(
636        &self,
637        state: &'a Self::State,
638        window: window::Id,
639    ) -> impl core::Widget<Self::Message, Self::Theme, Self::Renderer> + 'a {
640        self.program.view(state, window)
641    }
642
643    #[inline]
644    fn title(&self, state: &Self::State, window: window::Id) -> String {
645        self.program.title(state, window)
646    }
647
648    #[inline]
649    fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
650        self.program.subscription(state)
651    }
652
653    #[inline]
654    fn theme(&self, state: &Self::State, window: window::Id) -> Option<Self::Theme> {
655        self.program.theme(state, window)
656    }
657
658    #[inline]
659    fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
660        self.program.style(state, theme)
661    }
662
663    #[inline]
664    fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
665        (self.scale_factor)(state, window)
666    }
667}