iced_program/
lib.rs

1//! The definition of an iced program.
2pub use iced_graphics as graphics;
3pub use iced_runtime as runtime;
4pub use iced_runtime::core;
5pub use iced_runtime::futures;
6
7pub mod message;
8
9mod preset;
10
11pub use preset::Preset;
12
13use crate::core::renderer;
14use crate::core::text;
15use crate::core::theme;
16use crate::core::window;
17use crate::core::{Element, Font, Settings};
18use crate::futures::{Executor, Subscription};
19use crate::graphics::compositor;
20use crate::runtime::Task;
21
22/// An interactive, native, cross-platform, multi-windowed application.
23///
24/// A [`Program`] can execute asynchronous actions by returning a
25/// [`Task`] in some of its methods.
26#[allow(missing_docs)]
27pub trait Program: Sized {
28    /// The state of the program.
29    type State;
30
31    /// The message of the program.
32    type Message: Send + 'static;
33
34    /// The theme of the program.
35    type Theme: theme::Base;
36
37    /// The renderer of the program.
38    type Renderer: Renderer;
39
40    /// The executor of the program.
41    type Executor: Executor;
42
43    /// Returns the unique name of the [`Program`].
44    fn name() -> &'static str;
45
46    fn settings(&self) -> Settings;
47
48    fn window(&self) -> Option<window::Settings>;
49
50    fn boot(&self) -> (Self::State, Task<Self::Message>);
51
52    fn update(
53        &self,
54        state: &mut Self::State,
55        message: Self::Message,
56    ) -> Task<Self::Message>;
57
58    fn view<'a>(
59        &self,
60        state: &'a Self::State,
61        window: window::Id,
62    ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer>;
63
64    fn title(&self, _state: &Self::State, _window: window::Id) -> String {
65        let mut title = String::new();
66
67        for (i, part) in Self::name().split("_").enumerate() {
68            use std::borrow::Cow;
69
70            let part = match part {
71                "a" | "an" | "of" | "in" | "and" => Cow::Borrowed(part),
72                _ => {
73                    let mut part = part.to_owned();
74
75                    if let Some(first_letter) = part.get_mut(0..1) {
76                        first_letter.make_ascii_uppercase();
77                    }
78
79                    Cow::Owned(part)
80                }
81            };
82
83            if i > 0 {
84                title.push(' ');
85            }
86
87            title.push_str(&part);
88        }
89
90        format!("{title} - Iced")
91    }
92
93    fn subscription(
94        &self,
95        _state: &Self::State,
96    ) -> Subscription<Self::Message> {
97        Subscription::none()
98    }
99
100    fn theme(
101        &self,
102        _state: &Self::State,
103        _window: window::Id,
104    ) -> Option<Self::Theme> {
105        None
106    }
107
108    fn style(&self, _state: &Self::State, theme: &Self::Theme) -> theme::Style {
109        theme::Base::base(theme)
110    }
111
112    fn scale_factor(&self, _state: &Self::State, _window: window::Id) -> f32 {
113        1.0
114    }
115
116    fn presets(&self) -> &[Preset<Self::State, Self::Message>] {
117        &[]
118    }
119}
120
121/// Decorates a [`Program`] with the given title function.
122pub fn with_title<P: Program>(
123    program: P,
124    title: impl Fn(&P::State, window::Id) -> String,
125) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
126    struct WithTitle<P, Title> {
127        program: P,
128        title: Title,
129    }
130
131    impl<P, Title> Program for WithTitle<P, Title>
132    where
133        P: Program,
134        Title: Fn(&P::State, window::Id) -> String,
135    {
136        type State = P::State;
137        type Message = P::Message;
138        type Theme = P::Theme;
139        type Renderer = P::Renderer;
140        type Executor = P::Executor;
141
142        fn title(&self, state: &Self::State, window: window::Id) -> String {
143            (self.title)(state, window)
144        }
145
146        fn name() -> &'static str {
147            P::name()
148        }
149
150        fn settings(&self) -> Settings {
151            self.program.settings()
152        }
153
154        fn window(&self) -> Option<window::Settings> {
155            self.program.window()
156        }
157
158        fn boot(&self) -> (Self::State, Task<Self::Message>) {
159            self.program.boot()
160        }
161
162        fn update(
163            &self,
164            state: &mut Self::State,
165            message: Self::Message,
166        ) -> Task<Self::Message> {
167            self.program.update(state, message)
168        }
169
170        fn view<'a>(
171            &self,
172            state: &'a Self::State,
173            window: window::Id,
174        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
175            self.program.view(state, window)
176        }
177
178        fn theme(
179            &self,
180            state: &Self::State,
181            window: window::Id,
182        ) -> Option<Self::Theme> {
183            self.program.theme(state, window)
184        }
185
186        fn subscription(
187            &self,
188            state: &Self::State,
189        ) -> Subscription<Self::Message> {
190            self.program.subscription(state)
191        }
192
193        fn style(
194            &self,
195            state: &Self::State,
196            theme: &Self::Theme,
197        ) -> theme::Style {
198            self.program.style(state, theme)
199        }
200
201        fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
202            self.program.scale_factor(state, window)
203        }
204    }
205
206    WithTitle { program, title }
207}
208
209/// Decorates a [`Program`] with the given subscription function.
210pub fn with_subscription<P: Program>(
211    program: P,
212    f: impl Fn(&P::State) -> Subscription<P::Message>,
213) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
214    struct WithSubscription<P, F> {
215        program: P,
216        subscription: F,
217    }
218
219    impl<P: Program, F> Program for WithSubscription<P, F>
220    where
221        F: Fn(&P::State) -> Subscription<P::Message>,
222    {
223        type State = P::State;
224        type Message = P::Message;
225        type Theme = P::Theme;
226        type Renderer = P::Renderer;
227        type Executor = P::Executor;
228
229        fn subscription(
230            &self,
231            state: &Self::State,
232        ) -> Subscription<Self::Message> {
233            (self.subscription)(state)
234        }
235
236        fn name() -> &'static str {
237            P::name()
238        }
239
240        fn settings(&self) -> Settings {
241            self.program.settings()
242        }
243
244        fn window(&self) -> Option<window::Settings> {
245            self.program.window()
246        }
247
248        fn boot(&self) -> (Self::State, Task<Self::Message>) {
249            self.program.boot()
250        }
251
252        fn update(
253            &self,
254            state: &mut Self::State,
255            message: Self::Message,
256        ) -> Task<Self::Message> {
257            self.program.update(state, message)
258        }
259
260        fn view<'a>(
261            &self,
262            state: &'a Self::State,
263            window: window::Id,
264        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
265            self.program.view(state, window)
266        }
267
268        fn title(&self, state: &Self::State, window: window::Id) -> String {
269            self.program.title(state, window)
270        }
271
272        fn theme(
273            &self,
274            state: &Self::State,
275            window: window::Id,
276        ) -> Option<Self::Theme> {
277            self.program.theme(state, window)
278        }
279
280        fn style(
281            &self,
282            state: &Self::State,
283            theme: &Self::Theme,
284        ) -> theme::Style {
285            self.program.style(state, theme)
286        }
287
288        fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
289            self.program.scale_factor(state, window)
290        }
291    }
292
293    WithSubscription {
294        program,
295        subscription: f,
296    }
297}
298
299/// Decorates a [`Program`] with the given theme function.
300pub fn with_theme<P: Program>(
301    program: P,
302    f: impl Fn(&P::State, window::Id) -> Option<P::Theme>,
303) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
304    struct WithTheme<P, F> {
305        program: P,
306        theme: F,
307    }
308
309    impl<P: Program, F> Program for WithTheme<P, F>
310    where
311        F: Fn(&P::State, window::Id) -> Option<P::Theme>,
312    {
313        type State = P::State;
314        type Message = P::Message;
315        type Theme = P::Theme;
316        type Renderer = P::Renderer;
317        type Executor = P::Executor;
318
319        fn theme(
320            &self,
321            state: &Self::State,
322            window: window::Id,
323        ) -> Option<Self::Theme> {
324            (self.theme)(state, window)
325        }
326
327        fn name() -> &'static str {
328            P::name()
329        }
330
331        fn settings(&self) -> Settings {
332            self.program.settings()
333        }
334
335        fn window(&self) -> Option<window::Settings> {
336            self.program.window()
337        }
338
339        fn boot(&self) -> (Self::State, Task<Self::Message>) {
340            self.program.boot()
341        }
342
343        fn title(&self, state: &Self::State, window: window::Id) -> String {
344            self.program.title(state, window)
345        }
346
347        fn update(
348            &self,
349            state: &mut Self::State,
350            message: Self::Message,
351        ) -> Task<Self::Message> {
352            self.program.update(state, message)
353        }
354
355        fn view<'a>(
356            &self,
357            state: &'a Self::State,
358            window: window::Id,
359        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
360            self.program.view(state, window)
361        }
362
363        fn subscription(
364            &self,
365            state: &Self::State,
366        ) -> Subscription<Self::Message> {
367            self.program.subscription(state)
368        }
369
370        fn style(
371            &self,
372            state: &Self::State,
373            theme: &Self::Theme,
374        ) -> theme::Style {
375            self.program.style(state, theme)
376        }
377
378        fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
379            self.program.scale_factor(state, window)
380        }
381    }
382
383    WithTheme { program, theme: f }
384}
385
386/// Decorates a [`Program`] with the given style function.
387pub fn with_style<P: Program>(
388    program: P,
389    f: impl Fn(&P::State, &P::Theme) -> theme::Style,
390) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
391    struct WithStyle<P, F> {
392        program: P,
393        style: F,
394    }
395
396    impl<P: Program, F> Program for WithStyle<P, F>
397    where
398        F: Fn(&P::State, &P::Theme) -> theme::Style,
399    {
400        type State = P::State;
401        type Message = P::Message;
402        type Theme = P::Theme;
403        type Renderer = P::Renderer;
404        type Executor = P::Executor;
405
406        fn style(
407            &self,
408            state: &Self::State,
409            theme: &Self::Theme,
410        ) -> theme::Style {
411            (self.style)(state, theme)
412        }
413
414        fn name() -> &'static str {
415            P::name()
416        }
417
418        fn settings(&self) -> Settings {
419            self.program.settings()
420        }
421
422        fn window(&self) -> Option<window::Settings> {
423            self.program.window()
424        }
425
426        fn boot(&self) -> (Self::State, Task<Self::Message>) {
427            self.program.boot()
428        }
429
430        fn title(&self, state: &Self::State, window: window::Id) -> String {
431            self.program.title(state, window)
432        }
433
434        fn update(
435            &self,
436            state: &mut Self::State,
437            message: Self::Message,
438        ) -> Task<Self::Message> {
439            self.program.update(state, message)
440        }
441
442        fn view<'a>(
443            &self,
444            state: &'a Self::State,
445            window: window::Id,
446        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
447            self.program.view(state, window)
448        }
449
450        fn subscription(
451            &self,
452            state: &Self::State,
453        ) -> Subscription<Self::Message> {
454            self.program.subscription(state)
455        }
456
457        fn theme(
458            &self,
459            state: &Self::State,
460            window: window::Id,
461        ) -> Option<Self::Theme> {
462            self.program.theme(state, window)
463        }
464
465        fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
466            self.program.scale_factor(state, window)
467        }
468    }
469
470    WithStyle { program, style: f }
471}
472
473/// Decorates a [`Program`] with the given scale factor function.
474pub fn with_scale_factor<P: Program>(
475    program: P,
476    f: impl Fn(&P::State, window::Id) -> f32,
477) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
478    struct WithScaleFactor<P, F> {
479        program: P,
480        scale_factor: F,
481    }
482
483    impl<P: Program, F> Program for WithScaleFactor<P, F>
484    where
485        F: Fn(&P::State, window::Id) -> f32,
486    {
487        type State = P::State;
488        type Message = P::Message;
489        type Theme = P::Theme;
490        type Renderer = P::Renderer;
491        type Executor = P::Executor;
492
493        fn title(&self, state: &Self::State, window: window::Id) -> String {
494            self.program.title(state, window)
495        }
496
497        fn name() -> &'static str {
498            P::name()
499        }
500
501        fn settings(&self) -> Settings {
502            self.program.settings()
503        }
504
505        fn window(&self) -> Option<window::Settings> {
506            self.program.window()
507        }
508
509        fn boot(&self) -> (Self::State, Task<Self::Message>) {
510            self.program.boot()
511        }
512
513        fn update(
514            &self,
515            state: &mut Self::State,
516            message: Self::Message,
517        ) -> Task<Self::Message> {
518            self.program.update(state, message)
519        }
520
521        fn view<'a>(
522            &self,
523            state: &'a Self::State,
524            window: window::Id,
525        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
526            self.program.view(state, window)
527        }
528
529        fn subscription(
530            &self,
531            state: &Self::State,
532        ) -> Subscription<Self::Message> {
533            self.program.subscription(state)
534        }
535
536        fn theme(
537            &self,
538            state: &Self::State,
539            window: window::Id,
540        ) -> Option<Self::Theme> {
541            self.program.theme(state, window)
542        }
543
544        fn style(
545            &self,
546            state: &Self::State,
547            theme: &Self::Theme,
548        ) -> theme::Style {
549            self.program.style(state, theme)
550        }
551
552        fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
553            (self.scale_factor)(state, window)
554        }
555    }
556
557    WithScaleFactor {
558        program,
559        scale_factor: f,
560    }
561}
562
563/// Decorates a [`Program`] with the given executor function.
564pub fn with_executor<P: Program, E: Executor>(
565    program: P,
566) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {
567    use std::marker::PhantomData;
568
569    struct WithExecutor<P, E> {
570        program: P,
571        executor: PhantomData<E>,
572    }
573
574    impl<P: Program, E> Program for WithExecutor<P, E>
575    where
576        E: Executor,
577    {
578        type State = P::State;
579        type Message = P::Message;
580        type Theme = P::Theme;
581        type Renderer = P::Renderer;
582        type Executor = E;
583
584        fn title(&self, state: &Self::State, window: window::Id) -> String {
585            self.program.title(state, window)
586        }
587
588        fn name() -> &'static str {
589            P::name()
590        }
591
592        fn settings(&self) -> Settings {
593            self.program.settings()
594        }
595
596        fn window(&self) -> Option<window::Settings> {
597            self.program.window()
598        }
599
600        fn boot(&self) -> (Self::State, Task<Self::Message>) {
601            self.program.boot()
602        }
603
604        fn update(
605            &self,
606            state: &mut Self::State,
607            message: Self::Message,
608        ) -> Task<Self::Message> {
609            self.program.update(state, message)
610        }
611
612        fn view<'a>(
613            &self,
614            state: &'a Self::State,
615            window: window::Id,
616        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
617            self.program.view(state, window)
618        }
619
620        fn subscription(
621            &self,
622            state: &Self::State,
623        ) -> Subscription<Self::Message> {
624            self.program.subscription(state)
625        }
626
627        fn theme(
628            &self,
629            state: &Self::State,
630            window: window::Id,
631        ) -> Option<Self::Theme> {
632            self.program.theme(state, window)
633        }
634
635        fn style(
636            &self,
637            state: &Self::State,
638            theme: &Self::Theme,
639        ) -> theme::Style {
640            self.program.style(state, theme)
641        }
642
643        fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
644            self.program.scale_factor(state, window)
645        }
646    }
647
648    WithExecutor {
649        program,
650        executor: PhantomData::<E>,
651    }
652}
653
654/// The renderer of some [`Program`].
655pub trait Renderer:
656    text::Renderer<Font = Font> + compositor::Default + renderer::Headless
657{
658}
659
660impl<T> Renderer for T where
661    T: text::Renderer<Font = Font> + compositor::Default + renderer::Headless
662{
663}
664
665/// A particular instance of a running [`Program`].
666pub struct Instance<P: Program> {
667    program: P,
668    state: P::State,
669}
670
671impl<P: Program> Instance<P> {
672    /// Creates a new [`Instance`] of the given [`Program`].
673    pub fn new(program: P) -> (Self, Task<P::Message>) {
674        let (state, task) = program.boot();
675
676        (Self { program, state }, task)
677    }
678
679    /// Returns the current title of the [`Instance`].
680    pub fn title(&self, window: window::Id) -> String {
681        self.program.title(&self.state, window)
682    }
683
684    /// Processes the given message and updates the [`Instance`].
685    pub fn update(&mut self, message: P::Message) -> Task<P::Message> {
686        self.program.update(&mut self.state, message)
687    }
688
689    /// Produces the current widget tree of the [`Instance`].
690    pub fn view(
691        &self,
692        window: window::Id,
693    ) -> Element<'_, P::Message, P::Theme, P::Renderer> {
694        self.program.view(&self.state, window)
695    }
696
697    /// Returns the current [`Subscription`] of the [`Instance`].
698    pub fn subscription(&self) -> Subscription<P::Message> {
699        self.program.subscription(&self.state)
700    }
701
702    /// Returns the current theme of the [`Instance`].
703    pub fn theme(&self, window: window::Id) -> Option<P::Theme> {
704        self.program.theme(&self.state, window)
705    }
706
707    /// Returns the current [`theme::Style`] of the [`Instance`].
708    pub fn style(&self, theme: &P::Theme) -> theme::Style {
709        self.program.style(&self.state, theme)
710    }
711
712    /// Returns the current scale factor of the [`Instance`].
713    pub fn scale_factor(&self, window: window::Id) -> f32 {
714        self.program.scale_factor(&self.state, window)
715    }
716}