Skip to main content

iced/
daemon.rs

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