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: Send + std::fmt::Debug + '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: Send + std::fmt::Debug + '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 = iced_devtools::attach(self.raw);
122
123        #[cfg(any(not(feature = "debug"), target_arch = "wasm32"))]
124        let program = self.raw;
125
126        Ok(shell::run(program, self.settings, None)?)
127    }
128
129    /// Sets the [`Settings`] that will be used to run the [`Daemon`].
130    pub fn settings(self, settings: Settings) -> Self {
131        Self { settings, ..self }
132    }
133
134    /// Sets the [`Settings::antialiasing`] of the [`Daemon`].
135    pub fn antialiasing(self, antialiasing: bool) -> Self {
136        Self {
137            settings: Settings {
138                antialiasing,
139                ..self.settings
140            },
141            ..self
142        }
143    }
144
145    /// Sets the default [`Font`] of the [`Daemon`].
146    pub fn default_font(self, default_font: Font) -> Self {
147        Self {
148            settings: Settings {
149                default_font,
150                ..self.settings
151            },
152            ..self
153        }
154    }
155
156    /// Adds a font to the list of fonts that will be loaded at the start of the [`Daemon`].
157    pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self {
158        self.settings.fonts.push(font.into());
159        self
160    }
161
162    /// Sets the [`Title`] of the [`Daemon`].
163    pub fn title(
164        self,
165        title: impl Title<P::State>,
166    ) -> Daemon<
167        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
168    > {
169        Daemon {
170            raw: program::with_title(self.raw, move |state, window| {
171                title.title(state, window)
172            }),
173            settings: self.settings,
174        }
175    }
176
177    /// Sets the subscription logic of the [`Daemon`].
178    pub fn subscription(
179        self,
180        f: impl Fn(&P::State) -> Subscription<P::Message>,
181    ) -> Daemon<
182        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
183    > {
184        Daemon {
185            raw: program::with_subscription(self.raw, f),
186            settings: self.settings,
187        }
188    }
189
190    /// Sets the theme logic of the [`Daemon`].
191    pub fn theme(
192        self,
193        f: impl Fn(&P::State, window::Id) -> P::Theme,
194    ) -> Daemon<
195        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
196    > {
197        Daemon {
198            raw: program::with_theme(self.raw, f),
199            settings: self.settings,
200        }
201    }
202
203    /// Sets the style logic of the [`Daemon`].
204    pub fn style(
205        self,
206        f: impl Fn(&P::State, &P::Theme) -> theme::Style,
207    ) -> Daemon<
208        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
209    > {
210        Daemon {
211            raw: program::with_style(self.raw, f),
212            settings: self.settings,
213        }
214    }
215
216    /// Sets the scale factor of the [`Daemon`].
217    pub fn scale_factor(
218        self,
219        f: impl Fn(&P::State, window::Id) -> f64,
220    ) -> Daemon<
221        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
222    > {
223        Daemon {
224            raw: program::with_scale_factor(self.raw, f),
225            settings: self.settings,
226        }
227    }
228
229    /// Sets the executor of the [`Daemon`].
230    pub fn executor<E>(
231        self,
232    ) -> Daemon<
233        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
234    >
235    where
236        E: Executor,
237    {
238        Daemon {
239            raw: program::with_executor::<P, E>(self.raw),
240            settings: self.settings,
241        }
242    }
243}
244
245/// The title logic of some [`Daemon`].
246///
247/// This trait is implemented both for `&static str` and
248/// any closure `Fn(&State, window::Id) -> String`.
249///
250/// This trait allows the [`daemon`] builder to take any of them.
251pub trait Title<State> {
252    /// Produces the title of the [`Daemon`].
253    fn title(&self, state: &State, window: window::Id) -> String;
254}
255
256impl<State> Title<State> for &'static str {
257    fn title(&self, _state: &State, _window: window::Id) -> String {
258        self.to_string()
259    }
260}
261
262impl<T, State> Title<State> for T
263where
264    T: Fn(&State, window::Id) -> String,
265{
266    fn title(&self, state: &State, window: window::Id) -> String {
267        self(state, window)
268    }
269}
270
271/// The view logic of some [`Daemon`].
272///
273/// This trait allows the [`daemon`] builder to take any closure that
274/// returns any `Into<Element<'_, Message>>`.
275pub trait View<'a, State, Message, Theme, Renderer> {
276    /// Produces the widget of the [`Daemon`].
277    fn view(
278        &self,
279        state: &'a State,
280        window: window::Id,
281    ) -> impl Into<Element<'a, Message, Theme, Renderer>>;
282}
283
284impl<'a, T, State, Message, Theme, Renderer, Widget>
285    View<'a, State, Message, Theme, Renderer> for T
286where
287    T: Fn(&'a State, window::Id) -> Widget,
288    State: 'static,
289    Widget: Into<Element<'a, Message, Theme, Renderer>>,
290{
291    fn view(
292        &self,
293        state: &'a State,
294        window: window::Id,
295    ) -> impl Into<Element<'a, Message, Theme, Renderer>> {
296        self(state, window)
297    }
298}