1use crate::application;
3use crate::message;
4use crate::program::{self, Program};
5use crate::shell;
6use crate::theme;
7use crate::window;
8use crate::{
9 Element, Executor, Font, Preset, Result, Settings, Subscription, Task,
10 Theme,
11};
12
13use iced_debug as debug;
14
15use std::borrow::Cow;
16
17pub fn daemon<State, Message, Theme, Renderer>(
28 boot: impl application::BootFn<State, Message>,
29 update: impl application::UpdateFn<State, Message>,
30 view: impl for<'a> ViewFn<'a, State, Message, Theme, Renderer>,
31) -> Daemon<impl Program<State = State, Message = Message, Theme = Theme>>
32where
33 State: 'static,
34 Message: Send + 'static,
35 Theme: theme::Base,
36 Renderer: program::Renderer,
37{
38 use std::marker::PhantomData;
39
40 struct Instance<State, Message, Theme, Renderer, Boot, Update, View> {
41 boot: Boot,
42 update: Update,
43 view: View,
44 _state: PhantomData<State>,
45 _message: PhantomData<Message>,
46 _theme: PhantomData<Theme>,
47 _renderer: PhantomData<Renderer>,
48 }
49
50 impl<State, Message, Theme, Renderer, Boot, Update, View> Program
51 for Instance<State, Message, Theme, Renderer, Boot, Update, View>
52 where
53 Message: Send + 'static,
54 Theme: theme::Base,
55 Renderer: program::Renderer,
56 Boot: application::BootFn<State, Message>,
57 Update: application::UpdateFn<State, Message>,
58 View: for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>,
59 {
60 type State = State;
61 type Message = Message;
62 type Theme = Theme;
63 type Renderer = Renderer;
64 type Executor = iced_futures::backend::default::Executor;
65
66 fn name() -> &'static str {
67 let name = std::any::type_name::<State>();
68
69 name.split("::").next().unwrap_or("a_cool_daemon")
70 }
71
72 fn settings(&self) -> Settings {
73 Settings::default()
74 }
75
76 fn window(&self) -> Option<iced_core::window::Settings> {
77 None
78 }
79
80 fn boot(&self) -> (Self::State, Task<Self::Message>) {
81 self.boot.boot()
82 }
83
84 fn update(
85 &self,
86 state: &mut Self::State,
87 message: Self::Message,
88 ) -> Task<Self::Message> {
89 self.update.update(state, message)
90 }
91
92 fn view<'a>(
93 &self,
94 state: &'a Self::State,
95 window: window::Id,
96 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
97 self.view.view(state, window)
98 }
99 }
100
101 Daemon {
102 raw: Instance {
103 boot,
104 update,
105 view,
106 _state: PhantomData,
107 _message: PhantomData,
108 _theme: PhantomData,
109 _renderer: PhantomData,
110 },
111 settings: Settings::default(),
112 presets: Vec::new(),
113 }
114}
115
116#[derive(Debug)]
124pub struct Daemon<P: Program> {
125 raw: P,
126 settings: Settings,
127 presets: Vec<Preset<P::State, P::Message>>,
128}
129
130impl<P: Program> Daemon<P> {
131 pub fn run(self) -> Result
133 where
134 Self: 'static,
135 P::Message: message::MaybeDebug + message::MaybeClone,
136 {
137 #[cfg(all(feature = "debug", not(target_arch = "wasm32")))]
138 let program = {
139 iced_debug::init(iced_debug::Metadata {
140 name: P::name(),
141 theme: None,
142 can_time_travel: cfg!(feature = "time-travel"),
143 });
144
145 iced_devtools::attach(self)
146 };
147
148 #[cfg(any(not(feature = "debug"), target_arch = "wasm32"))]
149 let program = self;
150
151 Ok(shell::run(program)?)
152 }
153
154 pub fn settings(self, settings: Settings) -> Self {
156 Self { settings, ..self }
157 }
158
159 pub fn antialiasing(self, antialiasing: bool) -> Self {
161 Self {
162 settings: Settings {
163 antialiasing,
164 ..self.settings
165 },
166 ..self
167 }
168 }
169
170 pub fn default_font(self, default_font: Font) -> Self {
172 Self {
173 settings: Settings {
174 default_font,
175 ..self.settings
176 },
177 ..self
178 }
179 }
180
181 pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self {
183 self.settings.fonts.push(font.into());
184 self
185 }
186
187 pub fn title(
189 self,
190 title: impl TitleFn<P::State>,
191 ) -> Daemon<
192 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
193 > {
194 Daemon {
195 raw: program::with_title(self.raw, move |state, window| {
196 title.title(state, window)
197 }),
198 settings: self.settings,
199 presets: self.presets,
200 }
201 }
202
203 pub fn subscription(
205 self,
206 f: impl Fn(&P::State) -> Subscription<P::Message>,
207 ) -> Daemon<
208 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
209 > {
210 Daemon {
211 raw: program::with_subscription(self.raw, f),
212 settings: self.settings,
213 presets: self.presets,
214 }
215 }
216
217 pub fn theme(
219 self,
220 f: impl ThemeFn<P::State, P::Theme>,
221 ) -> Daemon<
222 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
223 > {
224 Daemon {
225 raw: program::with_theme(self.raw, move |state, window| {
226 f.theme(state, window)
227 }),
228 settings: self.settings,
229 presets: self.presets,
230 }
231 }
232
233 pub fn style(
235 self,
236 f: impl Fn(&P::State, &P::Theme) -> theme::Style,
237 ) -> Daemon<
238 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
239 > {
240 Daemon {
241 raw: program::with_style(self.raw, f),
242 settings: self.settings,
243 presets: self.presets,
244 }
245 }
246
247 pub fn scale_factor(
249 self,
250 f: impl Fn(&P::State, window::Id) -> f32,
251 ) -> Daemon<
252 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
253 > {
254 Daemon {
255 raw: program::with_scale_factor(self.raw, f),
256 settings: self.settings,
257 presets: self.presets,
258 }
259 }
260
261 pub fn executor<E>(
263 self,
264 ) -> Daemon<
265 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
266 >
267 where
268 E: Executor,
269 {
270 Daemon {
271 raw: program::with_executor::<P, E>(self.raw),
272 settings: self.settings,
273 presets: self.presets,
274 }
275 }
276
277 pub fn presets(
283 self,
284 presets: impl IntoIterator<Item = Preset<P::State, P::Message>>,
285 ) -> Self {
286 Self {
287 presets: presets.into_iter().collect(),
288 ..self
289 }
290 }
291}
292
293impl<P: Program> Program for Daemon<P> {
294 type State = P::State;
295 type Message = P::Message;
296 type Theme = P::Theme;
297 type Renderer = P::Renderer;
298 type Executor = P::Executor;
299
300 fn name() -> &'static str {
301 P::name()
302 }
303
304 fn settings(&self) -> Settings {
305 self.settings.clone()
306 }
307
308 fn window(&self) -> Option<window::Settings> {
309 None
310 }
311
312 fn boot(&self) -> (Self::State, Task<Self::Message>) {
313 self.raw.boot()
314 }
315
316 fn update(
317 &self,
318 state: &mut Self::State,
319 message: Self::Message,
320 ) -> Task<Self::Message> {
321 debug::hot(|| self.raw.update(state, message))
322 }
323
324 fn view<'a>(
325 &self,
326 state: &'a Self::State,
327 window: window::Id,
328 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
329 debug::hot(|| self.raw.view(state, window))
330 }
331
332 fn title(&self, state: &Self::State, window: window::Id) -> String {
333 debug::hot(|| self.raw.title(state, window))
334 }
335
336 fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
337 debug::hot(|| self.raw.subscription(state))
338 }
339
340 fn theme(
341 &self,
342 state: &Self::State,
343 window: iced_core::window::Id,
344 ) -> Option<Self::Theme> {
345 debug::hot(|| self.raw.theme(state, window))
346 }
347
348 fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
349 debug::hot(|| self.raw.style(state, theme))
350 }
351
352 fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
353 debug::hot(|| self.raw.scale_factor(state, window))
354 }
355
356 fn presets(&self) -> &[Preset<Self::State, Self::Message>] {
357 &self.presets
358 }
359}
360
361pub trait TitleFn<State> {
368 fn title(&self, state: &State, window: window::Id) -> String;
370}
371
372impl<State> TitleFn<State> for &'static str {
373 fn title(&self, _state: &State, _window: window::Id) -> String {
374 self.to_string()
375 }
376}
377
378impl<T, State> TitleFn<State> for T
379where
380 T: Fn(&State, window::Id) -> String,
381{
382 fn title(&self, state: &State, window: window::Id) -> String {
383 self(state, window)
384 }
385}
386
387pub trait ViewFn<'a, State, Message, Theme, Renderer> {
392 fn view(
394 &self,
395 state: &'a State,
396 window: window::Id,
397 ) -> Element<'a, Message, Theme, Renderer>;
398}
399
400impl<'a, T, State, Message, Theme, Renderer, Widget>
401 ViewFn<'a, State, Message, Theme, Renderer> for T
402where
403 T: Fn(&'a State, window::Id) -> Widget,
404 State: 'static,
405 Widget: Into<Element<'a, Message, Theme, Renderer>>,
406{
407 fn view(
408 &self,
409 state: &'a State,
410 window: window::Id,
411 ) -> Element<'a, Message, Theme, Renderer> {
412 self(state, window).into()
413 }
414}
415
416pub trait ThemeFn<State, Theme> {
425 fn theme(&self, state: &State, window: window::Id) -> Option<Theme>;
430}
431
432impl<State> ThemeFn<State, Theme> for Theme {
433 fn theme(&self, _state: &State, _window: window::Id) -> Option<Theme> {
434 Some(self.clone())
435 }
436}
437
438impl<F, T, State, Theme> ThemeFn<State, Theme> for F
439where
440 F: Fn(&State, window::Id) -> T,
441 T: Into<Option<Theme>>,
442{
443 fn theme(&self, state: &State, window: window::Id) -> Option<Theme> {
444 (self)(state, window).into()
445 }
446}