1use std::borrow::Cow;
2
3use iced_core::Font;
4
5pub use pattern::application;
7
8mod pattern {
9 use super::*;
10 use crate::settings::Settings;
11 use iced_core::Element;
12 use iced_runtime::Task;
13
14 use crate::actions::UnLockAction;
15
16 use crate::DefaultStyle;
17
18 use crate::Result;
19 use iced_debug as debug;
20 use iced_exdevtools::gen_attach;
21 use iced_program::Program;
22 gen_attach! { Action = UnLockAction }
23
24 pub trait UpdateFn<State, Message> {
29 fn update(&self, state: &mut State, message: Message) -> impl Into<Task<Message>>;
31 }
32
33 impl<State, Message> UpdateFn<State, Message> for () {
34 fn update(&self, _state: &mut State, _message: Message) -> impl Into<Task<Message>> {}
35 }
36
37 impl<T, State, Message, C> UpdateFn<State, Message> for T
38 where
39 T: Fn(&mut State, Message) -> C,
40 C: Into<Task<Message>>,
41 {
42 fn update(&self, state: &mut State, message: Message) -> impl Into<Task<Message>> {
43 self(state, message)
44 }
45 }
46
47 pub trait ViewFn<'a, State, Message, Theme, Renderer> {
52 fn view(
54 &self,
55 state: &'a State,
56 window: iced_core::window::Id,
57 ) -> Element<'a, Message, Theme, Renderer>;
58 }
59
60 impl<'a, T, State, Message, Theme, Renderer, Widget> ViewFn<'a, State, Message, Theme, Renderer>
61 for T
62 where
63 T: Fn(&'a State, iced_core::window::Id) -> Widget,
64 State: 'static,
65 Widget: Into<Element<'a, Message, Theme, Renderer>>,
66 {
67 fn view(
68 &self,
69 state: &'a State,
70 window: iced_core::window::Id,
71 ) -> Element<'a, Message, Theme, Renderer> {
72 self(state, window).into()
73 }
74 }
75
76 pub trait BootFn<State, Message> {
77 fn boot(&self) -> (State, Task<Message>);
79 }
80
81 impl<T, C, State, Message> BootFn<State, Message> for T
82 where
83 T: Fn() -> C,
84 C: IntoBoot<State, Message>,
85 {
86 fn boot(&self) -> (State, Task<Message>) {
87 self().into_boot()
88 }
89 }
90
91 pub trait IntoBoot<State, Message> {
93 fn into_boot(self) -> (State, Task<Message>);
95 }
96
97 impl<State, Message> IntoBoot<State, Message> for State {
98 fn into_boot(self) -> (State, Task<Message>) {
99 (self, Task::none())
100 }
101 }
102
103 impl<State, Message> IntoBoot<State, Message> for (State, Task<Message>) {
104 fn into_boot(self) -> (State, Task<Message>) {
105 self
106 }
107 }
108
109 pub trait ThemeFn<State, Theme> {
118 fn theme(&self, state: &State, window: iced_core::window::Id) -> Option<Theme>;
123 }
124
125 impl<State> ThemeFn<State, iced_core::Theme> for iced_core::Theme {
126 fn theme(
127 &self,
128 _state: &State,
129 _window: iced_core::window::Id,
130 ) -> Option<iced_core::Theme> {
131 Some(self.clone())
132 }
133 }
134
135 impl<F, T, State, Theme> ThemeFn<State, Theme> for F
136 where
137 F: Fn(&State) -> T,
138 T: Into<Option<Theme>>,
139 {
140 fn theme(&self, state: &State, _window: iced_core::window::Id) -> Option<Theme> {
141 (self)(state).into()
142 }
143 }
144 #[derive(Debug)]
145 pub struct Application<A: Program> {
146 raw: A,
147 settings: Settings,
148 }
149
150 pub fn application<State, Message, Theme, Renderer>(
151 boot: impl BootFn<State, Message>,
152 update: impl UpdateFn<State, Message>,
153 view: impl for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>,
154 ) -> Application<impl Program<Message = Message, Theme = Theme, State = State>>
155 where
156 State: 'static,
157 Message: 'static + TryInto<UnLockAction, Error = Message> + Send + std::fmt::Debug,
158 Theme: DefaultStyle,
159 Renderer: iced_program::Renderer,
160 {
161 use std::marker::PhantomData;
162 struct Instance<State, Message, Theme, Renderer, UpdateFn, ViewFn, BootFn> {
163 update: UpdateFn,
164 view: ViewFn,
165 boot: BootFn,
166 _state: PhantomData<State>,
167 _message: PhantomData<Message>,
168 _theme: PhantomData<Theme>,
169 _renderer: PhantomData<Renderer>,
170 }
171 impl<State, Message, Theme, Renderer, UpdateFn, ViewFn, BootFn> Program
172 for Instance<State, Message, Theme, Renderer, UpdateFn, ViewFn, BootFn>
173 where
174 Message: 'static + TryInto<UnLockAction, Error = Message> + Send + std::fmt::Debug,
175 Theme: DefaultStyle,
176 Renderer: iced_program::Renderer,
177 UpdateFn: self::UpdateFn<State, Message>,
178 BootFn: self::BootFn<State, Message>,
179 ViewFn: for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>,
180 {
181 type State = State;
182 type Renderer = Renderer;
183 type Message = Message;
184 type Theme = Theme;
185 type Executor = iced_futures::backend::default::Executor;
186
187 fn update(
188 &self,
189 state: &mut Self::State,
190 message: Self::Message,
191 ) -> Task<Self::Message> {
192 debug::hot(|| self.update.update(state, message)).into()
193 }
194 fn boot(&self) -> (Self::State, Task<Self::Message>) {
195 self.boot.boot()
196 }
197 fn view<'a>(
198 &self,
199 state: &'a Self::State,
200 window: iced_core::window::Id,
201 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
202 debug::hot(|| self.view.view(state, window))
203 }
204 fn name() -> &'static str {
205 let name = std::any::type_name::<State>();
206
207 name.split("::").next().unwrap_or("a_cool_application")
208 }
209 fn settings(&self) -> iced_core::Settings {
210 Default::default()
211 }
212
213 fn window(&self) -> Option<iced_core::window::Settings> {
214 None
215 }
216 }
217 Application {
218 raw: Instance {
219 update,
220 view,
221 boot,
222 _state: PhantomData,
223 _message: PhantomData,
224 _theme: PhantomData,
225 _renderer: PhantomData,
226 },
227 settings: Settings::default(),
228 }
229 }
230
231 pub fn with_executor<P: Program, E: iced_futures::Executor>(
232 program: P,
233 ) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
234 use std::marker::PhantomData;
235
236 struct WithExecutor<P, E> {
237 program: P,
238 executor: PhantomData<E>,
239 }
240
241 impl<P: Program, E> Program for WithExecutor<P, E>
242 where
243 E: iced_futures::Executor,
244 {
245 type State = P::State;
246 type Message = P::Message;
247 type Theme = P::Theme;
248 type Renderer = P::Renderer;
249 type Executor = E;
250
251 fn update(
252 &self,
253 state: &mut Self::State,
254 message: Self::Message,
255 ) -> Task<Self::Message> {
256 self.program.update(state, message)
257 }
258 fn boot(&self) -> (Self::State, Task<Self::Message>) {
259 self.program.boot()
260 }
261 fn view<'a>(
262 &self,
263 state: &'a Self::State,
264 window: iced_core::window::Id,
265 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
266 self.program.view(state, window)
267 }
268
269 fn subscription(
270 &self,
271 state: &Self::State,
272 ) -> iced_futures::Subscription<Self::Message> {
273 self.program.subscription(state)
274 }
275
276 fn theme(
277 &self,
278 state: &Self::State,
279 window: iced_core::window::Id,
280 ) -> Option<Self::Theme> {
281 self.program.theme(state, window)
282 }
283
284 fn style(&self, state: &Self::State, theme: &Self::Theme) -> crate::Appearance {
285 self.program.style(state, theme)
286 }
287
288 fn scale_factor(&self, state: &Self::State, window: iced_core::window::Id) -> f32 {
289 self.program.scale_factor(state, window)
290 }
291 fn name() -> &'static str {
292 P::name()
293 }
294 fn settings(&self) -> iced_core::Settings {
295 Default::default()
296 }
297
298 fn window(&self) -> Option<iced_core::window::Settings> {
299 None
300 }
301 }
302
303 WithExecutor {
304 program,
305 executor: PhantomData::<E>,
306 }
307 }
308
309 pub fn with_subscription<P: Program>(
310 program: P,
311 f: impl Fn(&P::State) -> iced_futures::Subscription<P::Message>,
312 ) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
313 struct WithSubscription<P, F> {
314 program: P,
315 subscription: F,
316 }
317
318 impl<P: Program, F> Program for WithSubscription<P, F>
319 where
320 F: Fn(&P::State) -> iced_futures::Subscription<P::Message>,
321 {
322 type State = P::State;
323 type Message = P::Message;
324 type Theme = P::Theme;
325 type Renderer = P::Renderer;
326 type Executor = P::Executor;
327
328 fn subscription(
329 &self,
330 state: &Self::State,
331 ) -> iced_futures::Subscription<Self::Message> {
332 (self.subscription)(state)
333 }
334
335 fn update(
336 &self,
337 state: &mut Self::State,
338 message: Self::Message,
339 ) -> Task<Self::Message> {
340 self.program.update(state, message)
341 }
342 fn boot(&self) -> (Self::State, Task<Self::Message>) {
343 self.program.boot()
344 }
345 fn view<'a>(
346 &self,
347 state: &'a Self::State,
348 window: iced_core::window::Id,
349 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
350 self.program.view(state, window)
351 }
352
353 fn theme(
354 &self,
355 state: &Self::State,
356 window: iced_core::window::Id,
357 ) -> Option<Self::Theme> {
358 self.program.theme(state, window)
359 }
360
361 fn style(&self, state: &Self::State, theme: &Self::Theme) -> crate::Appearance {
362 self.program.style(state, theme)
363 }
364
365 fn scale_factor(&self, state: &Self::State, window: iced_core::window::Id) -> f32 {
366 self.program.scale_factor(state, window)
367 }
368 fn name() -> &'static str {
369 P::name()
370 }
371 fn settings(&self) -> iced_core::Settings {
372 Default::default()
373 }
374
375 fn window(&self) -> Option<iced_core::window::Settings> {
376 None
377 }
378 }
379
380 WithSubscription {
381 program,
382 subscription: f,
383 }
384 }
385
386 pub fn with_theme<P: Program>(
387 program: P,
388 f: impl Fn(&P::State, iced_core::window::Id) -> Option<P::Theme>,
389 ) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
390 struct WithTheme<P, F> {
391 program: P,
392 theme: F,
393 }
394
395 impl<P: Program, F> Program for WithTheme<P, F>
396 where
397 F: Fn(&P::State, iced_core::window::Id) -> Option<P::Theme>,
398 {
399 type State = P::State;
400 type Message = P::Message;
401 type Theme = P::Theme;
402 type Renderer = P::Renderer;
403 type Executor = P::Executor;
404
405 fn theme(
406 &self,
407 state: &Self::State,
408 window: iced_core::window::Id,
409 ) -> Option<Self::Theme> {
410 (self.theme)(state, window)
411 }
412 fn boot(&self) -> (Self::State, Task<Self::Message>) {
413 self.program.boot()
414 }
415
416 fn update(
417 &self,
418 state: &mut Self::State,
419 message: Self::Message,
420 ) -> Task<Self::Message> {
421 self.program.update(state, message)
422 }
423
424 fn view<'a>(
425 &self,
426 state: &'a Self::State,
427 window: iced_core::window::Id,
428 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
429 self.program.view(state, window)
430 }
431
432 fn subscription(
433 &self,
434 state: &Self::State,
435 ) -> iced_futures::Subscription<Self::Message> {
436 self.program.subscription(state)
437 }
438
439 fn style(&self, state: &Self::State, theme: &Self::Theme) -> crate::Appearance {
440 self.program.style(state, theme)
441 }
442
443 fn scale_factor(&self, state: &Self::State, window: iced_core::window::Id) -> f32 {
444 self.program.scale_factor(state, window)
445 }
446 fn name() -> &'static str {
447 P::name()
448 }
449 fn settings(&self) -> iced_core::Settings {
450 Default::default()
451 }
452
453 fn window(&self) -> Option<iced_core::window::Settings> {
454 None
455 }
456 }
457
458 WithTheme { program, theme: f }
459 }
460
461 pub fn with_style<P: Program>(
462 program: P,
463 f: impl Fn(&P::State, &P::Theme) -> crate::Appearance,
464 ) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
465 struct WithStyle<P, F> {
466 program: P,
467 style: F,
468 }
469
470 impl<P: Program, F> Program for WithStyle<P, F>
471 where
472 F: Fn(&P::State, &P::Theme) -> crate::Appearance,
473 {
474 type State = P::State;
475 type Message = P::Message;
476 type Theme = P::Theme;
477 type Renderer = P::Renderer;
478 type Executor = P::Executor;
479
480 fn style(&self, state: &Self::State, theme: &Self::Theme) -> crate::Appearance {
481 (self.style)(state, theme)
482 }
483
484 fn update(
485 &self,
486 state: &mut Self::State,
487 message: Self::Message,
488 ) -> Task<Self::Message> {
489 self.program.update(state, message)
490 }
491 fn boot(&self) -> (Self::State, Task<Self::Message>) {
492 self.program.boot()
493 }
494 fn view<'a>(
495 &self,
496 state: &'a Self::State,
497 window: iced_core::window::Id,
498 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
499 self.program.view(state, window)
500 }
501
502 fn subscription(
503 &self,
504 state: &Self::State,
505 ) -> iced_futures::Subscription<Self::Message> {
506 self.program.subscription(state)
507 }
508
509 fn theme(
510 &self,
511 state: &Self::State,
512 window: iced_core::window::Id,
513 ) -> Option<Self::Theme> {
514 self.program.theme(state, window)
515 }
516
517 fn scale_factor(&self, state: &Self::State, window: iced_core::window::Id) -> f32 {
518 self.program.scale_factor(state, window)
519 }
520 fn name() -> &'static str {
521 P::name()
522 }
523 fn settings(&self) -> iced_core::Settings {
524 Default::default()
525 }
526
527 fn window(&self) -> Option<iced_core::window::Settings> {
528 None
529 }
530 }
531
532 WithStyle { program, style: f }
533 }
534
535 pub fn with_scale_factor<P: Program>(
536 program: P,
537 f: impl Fn(&P::State, iced_core::window::Id) -> f32,
538 ) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
539 struct WithScaleFactor<P, F> {
540 program: P,
541 scale_factor: F,
542 }
543
544 impl<P: Program, F> Program for WithScaleFactor<P, F>
545 where
546 F: Fn(&P::State, iced_core::window::Id) -> f32,
547 {
548 type State = P::State;
549 type Message = P::Message;
550 type Theme = P::Theme;
551 type Renderer = P::Renderer;
552 type Executor = P::Executor;
553
554 fn update(
555 &self,
556 state: &mut Self::State,
557 message: Self::Message,
558 ) -> Task<Self::Message> {
559 self.program.update(state, message)
560 }
561 fn boot(&self) -> (Self::State, Task<Self::Message>) {
562 self.program.boot()
563 }
564 fn view<'a>(
565 &self,
566 state: &'a Self::State,
567 window: iced_core::window::Id,
568 ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
569 self.program.view(state, window)
570 }
571
572 fn subscription(
573 &self,
574 state: &Self::State,
575 ) -> iced_futures::Subscription<Self::Message> {
576 self.program.subscription(state)
577 }
578
579 fn theme(
580 &self,
581 state: &Self::State,
582 window: iced_core::window::Id,
583 ) -> Option<Self::Theme> {
584 self.program.theme(state, window)
585 }
586
587 fn style(&self, state: &Self::State, theme: &Self::Theme) -> crate::Appearance {
588 self.program.style(state, theme)
589 }
590
591 fn scale_factor(&self, state: &Self::State, window: iced_core::window::Id) -> f32 {
592 (self.scale_factor)(state, window)
593 }
594 fn name() -> &'static str {
595 P::name()
596 }
597 fn settings(&self) -> iced_core::Settings {
598 Default::default()
599 }
600
601 fn window(&self) -> Option<iced_core::window::Settings> {
602 None
603 }
604 }
605
606 WithScaleFactor {
607 program,
608 scale_factor: f,
609 }
610 }
611
612 impl<P: Program> Application<P> {
613 pub fn run(self) -> Result
614 where
615 Self: 'static,
616 P::Message:
617 std::fmt::Debug + Send + 'static + TryInto<UnLockAction, Error = P::Message>,
618 {
619 #[cfg(all(feature = "debug", not(target_arch = "wasm32")))]
620 let program = {
621 iced_debug::init(iced_debug::Metadata {
622 name: Program::name(),
623 theme: None,
624 can_time_travel: cfg!(feature = "time-travel"),
625 });
626
627 attach(self.raw)
628 };
629
630 #[cfg(any(not(feature = "debug"), target_arch = "wasm32"))]
631 let program = self.raw;
632 let settings = self.settings;
633 let renderer_settings = iced_graphics::Settings {
634 default_font: settings.default_font,
635 default_text_size: settings.default_text_size,
636 antialiasing: if settings.antialiasing {
637 Some(iced_graphics::Antialiasing::MSAAx4)
638 } else {
639 None
640 },
641 ..Default::default()
642 };
643 crate::multi_window::run(program, settings, renderer_settings)
644 }
645
646 pub fn settings(self, settings: Settings) -> Self {
647 Self { settings, ..self }
648 }
649
650 pub fn antialiasing(self, antialiasing: bool) -> Self {
652 Self {
653 settings: Settings {
654 antialiasing,
655 ..self.settings
656 },
657 ..self
658 }
659 }
660
661 pub fn default_font(self, default_font: Font) -> Self {
663 Self {
664 settings: Settings {
665 default_font,
666 ..self.settings
667 },
668 ..self
669 }
670 }
671
672 pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self {
674 self.settings.fonts.push(font.into());
675 self
676 }
677
678 pub fn default_text_size<Pixels: Into<iced_core::Pixels>>(
680 self,
681 default_text_size: Pixels,
682 ) -> Self {
683 Self {
684 settings: Settings {
685 default_text_size: default_text_size.into(),
686 ..self.settings
687 },
688 ..self
689 }
690 }
691
692 pub fn style(
694 self,
695 f: impl Fn(&P::State, &P::Theme) -> crate::Appearance,
696 ) -> Application<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>>
697 {
698 Application {
699 raw: with_style(self.raw, move |state, theme| debug::hot(|| f(state, theme))),
700 settings: self.settings,
701 }
702 }
703 pub fn subscription(
705 self,
706 f: impl Fn(&P::State) -> iced_futures::Subscription<P::Message>,
707 ) -> Application<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>>
708 {
709 Application {
710 raw: with_subscription(self.raw, move |state| debug::hot(|| f(state))),
711 settings: self.settings,
712 }
713 }
714
715 pub fn theme(
717 self,
718 f: impl ThemeFn<P::State, P::Theme>,
719 ) -> Application<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>>
720 {
721 Application {
722 raw: with_theme(self.raw, move |state, window| {
723 debug::hot(|| f.theme(state, window))
724 }),
725 settings: self.settings,
726 }
727 }
728
729 pub fn scale_factor(
731 self,
732 f: impl Fn(&P::State, iced_core::window::Id) -> f32,
733 ) -> Application<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>>
734 {
735 Application {
736 raw: with_scale_factor(self.raw, move |state, window| {
737 debug::hot(|| f(state, window))
738 }),
739 settings: self.settings,
740 }
741 }
742 pub fn executor<E>(
744 self,
745 ) -> Application<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>>
746 where
747 E: iced_futures::Executor,
748 {
749 Application {
750 raw: with_executor::<P, E>(self.raw),
751 settings: self.settings,
752 }
753 }
754 }
755}