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(feature = "debug")]
138 iced_debug::init(iced_debug::Metadata {
139 name: P::name(),
140 theme: None,
141 can_time_travel: cfg!(feature = "time-travel"),
142 });
143
144 #[cfg(feature = "tester")]
145 let program = iced_tester::attach(self);
146
147 #[cfg(all(feature = "debug", not(feature = "tester")))]
148 let program = iced_devtools::attach(self);
149
150 #[cfg(not(any(feature = "tester", feature = "debug")))]
151 let program = self;
152
153 Ok(shell::run(program)?)
154 }
155
156 pub fn settings(self, settings: Settings) -> Self {
158 Self { settings, ..self }
159 }
160
161 pub fn antialiasing(self, antialiasing: bool) -> Self {
163 Self {
164 settings: Settings {
165 antialiasing,
166 ..self.settings
167 },
168 ..self
169 }
170 }
171
172 pub fn default_font(self, default_font: Font) -> Self {
174 Self {
175 settings: Settings {
176 default_font,
177 ..self.settings
178 },
179 ..self
180 }
181 }
182
183 pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self {
185 self.settings.fonts.push(font.into());
186 self
187 }
188
189 pub fn title(
191 self,
192 title: impl TitleFn<P::State>,
193 ) -> Daemon<
194 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
195 > {
196 Daemon {
197 raw: program::with_title(self.raw, move |state, window| {
198 title.title(state, window)
199 }),
200 settings: self.settings,
201 presets: self.presets,
202 }
203 }
204
205 pub fn subscription(
207 self,
208 f: impl Fn(&P::State) -> Subscription<P::Message>,
209 ) -> Daemon<
210 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
211 > {
212 Daemon {
213 raw: program::with_subscription(self.raw, f),
214 settings: self.settings,
215 presets: self.presets,
216 }
217 }
218
219 pub fn theme(
221 self,
222 f: impl ThemeFn<P::State, P::Theme>,
223 ) -> Daemon<
224 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
225 > {
226 Daemon {
227 raw: program::with_theme(self.raw, move |state, window| {
228 f.theme(state, window)
229 }),
230 settings: self.settings,
231 presets: self.presets,
232 }
233 }
234
235 pub fn style(
237 self,
238 f: impl Fn(&P::State, &P::Theme) -> theme::Style,
239 ) -> Daemon<
240 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
241 > {
242 Daemon {
243 raw: program::with_style(self.raw, f),
244 settings: self.settings,
245 presets: self.presets,
246 }
247 }
248
249 pub fn scale_factor(
251 self,
252 f: impl Fn(&P::State, window::Id) -> f32,
253 ) -> Daemon<
254 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
255 > {
256 Daemon {
257 raw: program::with_scale_factor(self.raw, f),
258 settings: self.settings,
259 presets: self.presets,
260 }
261 }
262
263 pub fn executor<E>(
265 self,
266 ) -> Daemon<
267 impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
268 >
269 where
270 E: Executor,
271 {
272 Daemon {
273 raw: program::with_executor::<P, E>(self.raw),
274 settings: self.settings,
275 presets: self.presets,
276 }
277 }
278
279 pub fn presets(
285 self,
286 presets: impl IntoIterator<Item = Preset<P::State, P::Message>>,
287 ) -> Self {
288 Self {
289 presets: presets.into_iter().collect(),
290 ..self
291 }
292 }
293}
294
295impl<P: Program> Program for Daemon<P> {
296 type State = P::State;
297 type Message = P::Message;
298 type Theme = P::Theme;
299 type Renderer = P::Renderer;
300 type Executor = P::Executor;
301
302 fn name() -> &'static str {
303 P::name()
304 }
305
306 fn settings(&self) -> Settings {
307 self.settings.clone()
308 }
309
310 fn window(&self) -> Option<window::Settings> {
311 None
312 }
313
314 fn boot(&self) -> (Self::State, Task<Self::Message>) {
315 self.raw.boot()
316 }
317
318 fn update(
319 &self,
320 state: &mut Self::State,
321 message: Self::Message,
322 ) -> Task<Self::Message> {
323 debug::hot(|| self.raw.update(state, message))
324 }
325
326 fn view<'a>(
327 &self,
328 state: &'a Self::State,
329 window: window::Id,
330 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
331 debug::hot(|| self.raw.view(state, window))
332 }
333
334 fn title(&self, state: &Self::State, window: window::Id) -> String {
335 debug::hot(|| self.raw.title(state, window))
336 }
337
338 fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
339 debug::hot(|| self.raw.subscription(state))
340 }
341
342 fn theme(
343 &self,
344 state: &Self::State,
345 window: iced_core::window::Id,
346 ) -> Option<Self::Theme> {
347 debug::hot(|| self.raw.theme(state, window))
348 }
349
350 fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
351 debug::hot(|| self.raw.style(state, theme))
352 }
353
354 fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
355 debug::hot(|| self.raw.scale_factor(state, window))
356 }
357
358 fn presets(&self) -> &[Preset<Self::State, Self::Message>] {
359 &self.presets
360 }
361}
362
363pub trait TitleFn<State> {
370 fn title(&self, state: &State, window: window::Id) -> String;
372}
373
374impl<State> TitleFn<State> for &'static str {
375 fn title(&self, _state: &State, _window: window::Id) -> String {
376 self.to_string()
377 }
378}
379
380impl<T, State> TitleFn<State> for T
381where
382 T: Fn(&State, window::Id) -> String,
383{
384 fn title(&self, state: &State, window: window::Id) -> String {
385 self(state, window)
386 }
387}
388
389pub trait ViewFn<'a, State, Message, Theme, Renderer> {
394 fn view(
396 &self,
397 state: &'a State,
398 window: window::Id,
399 ) -> Element<'a, Message, Theme, Renderer>;
400}
401
402impl<'a, T, State, Message, Theme, Renderer, Widget>
403 ViewFn<'a, State, Message, Theme, Renderer> for T
404where
405 T: Fn(&'a State, window::Id) -> Widget,
406 State: 'static,
407 Widget: Into<Element<'a, Message, Theme, Renderer>>,
408{
409 fn view(
410 &self,
411 state: &'a State,
412 window: window::Id,
413 ) -> Element<'a, Message, Theme, Renderer> {
414 self(state, window).into()
415 }
416}
417
418pub trait ThemeFn<State, Theme> {
427 fn theme(&self, state: &State, window: window::Id) -> Option<Theme>;
432}
433
434impl<State> ThemeFn<State, Theme> for Theme {
435 fn theme(&self, _state: &State, _window: window::Id) -> Option<Theme> {
436 Some(self.clone())
437 }
438}
439
440impl<F, T, State, Theme> ThemeFn<State, Theme> for F
441where
442 F: Fn(&State, window::Id) -> T,
443 T: Into<Option<Theme>>,
444{
445 fn theme(&self, state: &State, window: window::Id) -> Option<Theme> {
446 (self)(state, window).into()
447 }
448}