Skip to main content

iced_sessionlock/
build_pattern.rs

1use std::borrow::Cow;
2
3use iced_core::Font;
4
5/// The renderer of iced program.
6pub 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    /// The update logic of some [`Application`].
25    ///
26    /// This trait allows the [`application`] builder to take any closure that
27    /// returns any `Into<Task<Message>>`.
28    pub trait UpdateFn<State, Message> {
29        /// Processes the message and updates the state of the [`Application`].
30        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    /// The view logic of some [`Application`].
48    ///
49    /// This trait allows the [`application`] builder to take any closure that
50    /// returns any `Into<Element<'_, Message>>`.
51    pub trait ViewFn<'a, State, Message, Theme, Renderer> {
52        /// Produces the widget of the [`Application`].
53        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        /// Initializes the [`Application`] state.
78        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    /// The initial state of some [`Application`].
92    pub trait IntoBoot<State, Message> {
93        /// Turns some type into the initial state of some [`Application`].
94        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    /// The theme logic of some [`Application`].
110    ///
111    /// Any implementors of this trait can be provided as an argument to
112    /// [`Application::theme`].
113    ///
114    /// `iced` provides two implementors:
115    /// - the built-in [`iced::Theme`] itself
116    /// - and any `Fn(&State, iced::window::Id) -> impl Into<Option<iced::Theme>>`.
117    pub trait ThemeFn<State, Theme> {
118        /// Returns the theme of the [`Daemon`] for the current state and window.
119        ///
120        /// If `None` is returned, `iced` will try to use a theme that
121        /// matches the system color scheme.
122        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        /// Sets the [`Settings::antialiasing`] of the [`Application`].
651        pub fn antialiasing(self, antialiasing: bool) -> Self {
652            Self {
653                settings: Settings {
654                    antialiasing,
655                    ..self.settings
656                },
657                ..self
658            }
659        }
660
661        /// Sets the default [`Font`] of the [`Application`].
662        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        /// Adds a font to the list of fonts that will be loaded at the start of the [`Application`].
673        pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self {
674            self.settings.fonts.push(font.into());
675            self
676        }
677
678        /// set the default_text_size
679        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        /// Sets the style logic of the [`Application`].
693        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        /// Sets the subscription logic of the [`Application`].
704        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        /// Sets the theme logic of the [`Application`].
716        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        /// Sets the scale factor of the [`Application`].
730        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        /// Sets the executor of the [`Application`].
743        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}