1use 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
10use iced_debug as debug;
11
12use std::borrow::Cow;
13
14pub fn daemon<State, Message, Theme, Renderer>(
25 boot: impl application::BootFn<State, Message>,
26 update: impl application::UpdateFn<State, Message>,
27 view: impl for<'a> ViewFn<'a, State, Message, Theme, Renderer>,
28) -> Daemon<impl Program<State = State, Message = Message, Theme = Theme>>
29where
30 State: 'static,
31 Message: Send + 'static,
32 Theme: theme::Base,
33 Renderer: program::Renderer,
34{
35 use std::marker::PhantomData;
36
37 struct Instance<State, Message, Theme, Renderer, Boot, Update, View> {
38 boot: Boot,
39 update: Update,
40 view: View,
41 _state: PhantomData<State>,
42 _message: PhantomData<Message>,
43 _theme: PhantomData<Theme>,
44 _renderer: PhantomData<Renderer>,
45 }
46
47 impl<State, Message, Theme, Renderer, Boot, Update, View> Program
48 for Instance<State, Message, Theme, Renderer, Boot, Update, View>
49 where
50 Message: Send + 'static,
51 Theme: theme::Base,
52 Renderer: program::Renderer,
53 Boot: application::BootFn<State, Message>,
54 Update: application::UpdateFn<State, Message>,
55 View: for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>,
56 {
57 type State = State;
58 type Message = Message;
59 type Theme = Theme;
60 type Renderer = Renderer;
61 type Executor = iced_futures::backend::default::Executor;
62
63 fn name() -> &'static str {
64 let name = std::any::type_name::<State>();
65
66 name.split("::").next().unwrap_or("a_cool_daemon")
67 }
68
69 fn settings(&self) -> Settings {
70 Settings::default()
71 }
72
73 fn window(&self) -> Option<iced_core::window::Settings> {
74 None
75 }
76
77 fn boot(&self) -> (Self::State, Task<Self::Message>) {
78 self.boot.boot()
79 }
80
81 fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
82 self.update.update(state, message)
83 }
84
85 fn view<'a>(
86 &self,
87 state: &'a Self::State,
88 window: window::Id,
89 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
90 self.view.view(state, window)
91 }
92 }
93
94 Daemon {
95 raw: Instance {
96 boot,
97 update,
98 view,
99 _state: PhantomData,
100 _message: PhantomData,
101 _theme: PhantomData,
102 _renderer: PhantomData,
103 },
104 settings: Settings::default(),
105 presets: Vec::new(),
106 }
107}
108
109#[derive(Debug)]
117pub struct Daemon<P: Program> {
118 raw: P,
119 settings: Settings,
120 presets: Vec<Preset<P::State, P::Message>>,
121}
122
123impl<P: Program> Daemon<P> {
124 pub fn run(self) -> Result
126 where
127 Self: 'static,
128 P::Message: message::MaybeDebug + message::MaybeClone,
129 {
130 #[cfg(feature = "debug")]
131 iced_debug::init(iced_debug::Metadata {
132 name: P::name(),
133 theme: None,
134 can_time_travel: cfg!(feature = "time-travel"),
135 });
136
137 #[cfg(feature = "tester")]
138 let program = iced_tester::attach(self);
139
140 #[cfg(all(feature = "debug", not(feature = "tester")))]
141 let program = iced_devtools::attach(self);
142
143 #[cfg(not(any(feature = "tester", feature = "debug")))]
144 let program = self;
145
146 Ok(shell::run(program)?)
147 }
148
149 pub fn settings(self, settings: Settings) -> Self {
151 Self { settings, ..self }
152 }
153
154 pub fn antialiasing(self, antialiasing: bool) -> Self {
156 Self {
157 settings: Settings {
158 antialiasing,
159 ..self.settings
160 },
161 ..self
162 }
163 }
164
165 pub fn default_font(self, default_font: Font) -> Self {
167 Self {
168 settings: Settings {
169 default_font,
170 ..self.settings
171 },
172 ..self
173 }
174 }
175
176 pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self {
178 self.settings.fonts.push(font.into());
179 self
180 }
181
182 pub fn title(
184 self,
185 title: impl TitleFn<P::State>,
186 ) -> Daemon<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
187 Daemon {
188 raw: program::with_title(self.raw, move |state, window| title.title(state, window)),
189 settings: self.settings,
190 presets: self.presets,
191 }
192 }
193
194 pub fn subscription(
196 self,
197 f: impl Fn(&P::State) -> Subscription<P::Message>,
198 ) -> Daemon<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
199 Daemon {
200 raw: program::with_subscription(self.raw, f),
201 settings: self.settings,
202 presets: self.presets,
203 }
204 }
205
206 pub fn theme(
208 self,
209 f: impl ThemeFn<P::State, P::Theme>,
210 ) -> Daemon<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
211 Daemon {
212 raw: program::with_theme(self.raw, move |state, window| f.theme(state, window)),
213 settings: self.settings,
214 presets: self.presets,
215 }
216 }
217
218 pub fn style(
220 self,
221 f: impl Fn(&P::State, &P::Theme) -> theme::Style,
222 ) -> Daemon<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
223 Daemon {
224 raw: program::with_style(self.raw, f),
225 settings: self.settings,
226 presets: self.presets,
227 }
228 }
229
230 pub fn scale_factor(
232 self,
233 f: impl Fn(&P::State, window::Id) -> f32,
234 ) -> Daemon<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
235 Daemon {
236 raw: program::with_scale_factor(self.raw, f),
237 settings: self.settings,
238 presets: self.presets,
239 }
240 }
241
242 pub fn executor<E>(
244 self,
245 ) -> Daemon<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>>
246 where
247 E: Executor,
248 {
249 Daemon {
250 raw: program::with_executor::<P, E>(self.raw),
251 settings: self.settings,
252 presets: self.presets,
253 }
254 }
255
256 pub fn presets(self, presets: impl IntoIterator<Item = Preset<P::State, P::Message>>) -> Self {
262 Self {
263 presets: presets.into_iter().collect(),
264 ..self
265 }
266 }
267}
268
269impl<P: Program> Program for Daemon<P> {
270 type State = P::State;
271 type Message = P::Message;
272 type Theme = P::Theme;
273 type Renderer = P::Renderer;
274 type Executor = P::Executor;
275
276 fn name() -> &'static str {
277 P::name()
278 }
279
280 fn settings(&self) -> Settings {
281 self.settings.clone()
282 }
283
284 fn window(&self) -> Option<window::Settings> {
285 None
286 }
287
288 fn boot(&self) -> (Self::State, Task<Self::Message>) {
289 self.raw.boot()
290 }
291
292 fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
293 debug::hot(|| self.raw.update(state, message))
294 }
295
296 fn view<'a>(
297 &self,
298 state: &'a Self::State,
299 window: window::Id,
300 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
301 debug::hot(|| self.raw.view(state, window))
302 }
303
304 fn title(&self, state: &Self::State, window: window::Id) -> String {
305 debug::hot(|| self.raw.title(state, window))
306 }
307
308 fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
309 debug::hot(|| self.raw.subscription(state))
310 }
311
312 fn theme(&self, state: &Self::State, window: iced_core::window::Id) -> Option<Self::Theme> {
313 debug::hot(|| self.raw.theme(state, window))
314 }
315
316 fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
317 debug::hot(|| self.raw.style(state, theme))
318 }
319
320 fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
321 debug::hot(|| self.raw.scale_factor(state, window))
322 }
323
324 fn presets(&self) -> &[Preset<Self::State, Self::Message>] {
325 &self.presets
326 }
327}
328
329pub trait TitleFn<State> {
336 fn title(&self, state: &State, window: window::Id) -> String;
338}
339
340impl<State> TitleFn<State> for &'static str {
341 fn title(&self, _state: &State, _window: window::Id) -> String {
342 self.to_string()
343 }
344}
345
346impl<T, State> TitleFn<State> for T
347where
348 T: Fn(&State, window::Id) -> String,
349{
350 fn title(&self, state: &State, window: window::Id) -> String {
351 self(state, window)
352 }
353}
354
355pub trait ViewFn<'a, State, Message, Theme, Renderer> {
360 fn view(&self, state: &'a State, window: window::Id) -> Element<'a, Message, Theme, Renderer>;
362}
363
364impl<'a, T, State, Message, Theme, Renderer, Widget> ViewFn<'a, State, Message, Theme, Renderer>
365 for T
366where
367 T: Fn(&'a State, window::Id) -> Widget,
368 State: 'static,
369 Widget: Into<Element<'a, Message, Theme, Renderer>>,
370{
371 fn view(&self, state: &'a State, window: window::Id) -> Element<'a, Message, Theme, Renderer> {
372 self(state, window).into()
373 }
374}
375
376pub trait ThemeFn<State, Theme> {
385 fn theme(&self, state: &State, window: window::Id) -> Option<Theme>;
390}
391
392impl<State> ThemeFn<State, Theme> for Theme {
393 fn theme(&self, _state: &State, _window: window::Id) -> Option<Theme> {
394 Some(self.clone())
395 }
396}
397
398impl<F, T, State, Theme> ThemeFn<State, Theme> for F
399where
400 F: Fn(&State, window::Id) -> T,
401 T: Into<Option<Theme>>,
402{
403 fn theme(&self, state: &State, window: window::Id) -> Option<Theme> {
404 (self)(state, window).into()
405 }
406}