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