iced/
application.rs

1//! Create and run iced applications step by step.
2//!
3//! # Example
4//! ```no_run,standalone_crate
5//! use iced::widget::{button, column, text, Column};
6//! use iced::Theme;
7//!
8//! pub fn main() -> iced::Result {
9//!     iced::application(u64::default, update, view)
10//!         .theme(Theme::Dark)
11//!         .centered()
12//!         .run()
13//! }
14//!
15//! #[derive(Debug, Clone)]
16//! enum Message {
17//!     Increment,
18//! }
19//!
20//! fn update(value: &mut u64, message: Message) {
21//!     match message {
22//!         Message::Increment => *value += 1,
23//!     }
24//! }
25//!
26//! fn view(value: &u64) -> Column<Message> {
27//!     column![
28//!         text(value),
29//!         button("+").on_press(Message::Increment),
30//!     ]
31//! }
32//! ```
33use crate::message;
34use crate::program::{self, Program};
35use crate::shell;
36use crate::theme;
37use crate::window;
38use crate::{
39    Element, Executor, Font, Preset, Result, Settings, Size, Subscription,
40    Task, Theme,
41};
42
43use iced_debug as debug;
44
45use std::borrow::Cow;
46
47pub mod timed;
48
49pub use timed::timed;
50
51/// Creates an iced [`Application`] given its boot, update, and view logic.
52///
53/// # Example
54/// ```no_run,standalone_crate
55/// use iced::widget::{button, column, text, Column};
56///
57/// pub fn main() -> iced::Result {
58///     iced::application(u64::default, update, view).run()
59/// }
60///
61/// #[derive(Debug, Clone)]
62/// enum Message {
63///     Increment,
64/// }
65///
66/// fn update(value: &mut u64, message: Message) {
67///     match message {
68///         Message::Increment => *value += 1,
69///     }
70/// }
71///
72/// fn view(value: &u64) -> Column<Message> {
73///     column![
74///         text(value),
75///         button("+").on_press(Message::Increment),
76///     ]
77/// }
78/// ```
79pub fn application<State, Message, Theme, Renderer>(
80    boot: impl BootFn<State, Message>,
81    update: impl UpdateFn<State, Message>,
82    view: impl for<'a> ViewFn<'a, State, Message, Theme, Renderer>,
83) -> Application<impl Program<State = State, Message = Message, Theme = Theme>>
84where
85    State: 'static,
86    Message: Send + 'static,
87    Theme: theme::Base,
88    Renderer: program::Renderer,
89{
90    use std::marker::PhantomData;
91
92    struct Instance<State, Message, Theme, Renderer, Boot, Update, View> {
93        boot: Boot,
94        update: Update,
95        view: View,
96        _state: PhantomData<State>,
97        _message: PhantomData<Message>,
98        _theme: PhantomData<Theme>,
99        _renderer: PhantomData<Renderer>,
100    }
101
102    impl<State, Message, Theme, Renderer, Boot, Update, View> Program
103        for Instance<State, Message, Theme, Renderer, Boot, Update, View>
104    where
105        Message: Send + 'static,
106        Theme: theme::Base,
107        Renderer: program::Renderer,
108        Boot: self::BootFn<State, Message>,
109        Update: self::UpdateFn<State, Message>,
110        View: for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>,
111    {
112        type State = State;
113        type Message = Message;
114        type Theme = Theme;
115        type Renderer = Renderer;
116        type Executor = iced_futures::backend::default::Executor;
117
118        fn name() -> &'static str {
119            let name = std::any::type_name::<State>();
120
121            name.split("::").next().unwrap_or("a_cool_application")
122        }
123
124        fn boot(&self) -> (State, Task<Message>) {
125            self.boot.boot()
126        }
127
128        fn update(
129            &self,
130            state: &mut Self::State,
131            message: Self::Message,
132        ) -> Task<Self::Message> {
133            self.update.update(state, message)
134        }
135
136        fn view<'a>(
137            &self,
138            state: &'a Self::State,
139            _window: window::Id,
140        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
141            self.view.view(state)
142        }
143
144        fn settings(&self) -> Settings {
145            Settings::default()
146        }
147
148        fn window(&self) -> Option<iced_core::window::Settings> {
149            Some(window::Settings::default())
150        }
151    }
152
153    Application {
154        raw: Instance {
155            boot,
156            update,
157            view,
158            _state: PhantomData,
159            _message: PhantomData,
160            _theme: PhantomData,
161            _renderer: PhantomData,
162        },
163        settings: Settings::default(),
164        window: window::Settings::default(),
165        presets: Vec::new(),
166    }
167}
168
169/// The underlying definition and configuration of an iced application.
170///
171/// You can use this API to create and run iced applications
172/// step by step—without coupling your logic to a trait
173/// or a specific type.
174///
175/// You can create an [`Application`] with the [`application`] helper.
176#[derive(Debug)]
177pub struct Application<P: Program> {
178    raw: P,
179    settings: Settings,
180    window: window::Settings,
181    presets: Vec<Preset<P::State, P::Message>>,
182}
183
184impl<P: Program> Application<P> {
185    /// Runs the [`Application`].
186    pub fn run(self) -> Result
187    where
188        Self: 'static,
189        P::Message: message::MaybeDebug + message::MaybeClone,
190    {
191        #[cfg(feature = "debug")]
192        iced_debug::init(iced_debug::Metadata {
193            name: P::name(),
194            theme: None,
195            can_time_travel: cfg!(feature = "time-travel"),
196        });
197
198        #[cfg(feature = "tester")]
199        let program = iced_tester::attach(self);
200
201        #[cfg(all(feature = "debug", not(feature = "tester")))]
202        let program = iced_devtools::attach(self);
203
204        #[cfg(not(any(feature = "tester", feature = "debug")))]
205        let program = self;
206
207        Ok(shell::run(program)?)
208    }
209
210    /// Sets the [`Settings`] that will be used to run the [`Application`].
211    pub fn settings(self, settings: Settings) -> Self {
212        Self { settings, ..self }
213    }
214
215    /// Sets the [`Settings::antialiasing`] of the [`Application`].
216    pub fn antialiasing(self, antialiasing: bool) -> Self {
217        Self {
218            settings: Settings {
219                antialiasing,
220                ..self.settings
221            },
222            ..self
223        }
224    }
225
226    /// Sets the default [`Font`] of the [`Application`].
227    pub fn default_font(self, default_font: Font) -> Self {
228        Self {
229            settings: Settings {
230                default_font,
231                ..self.settings
232            },
233            ..self
234        }
235    }
236
237    /// Adds a font to the list of fonts that will be loaded at the start of the [`Application`].
238    pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self {
239        self.settings.fonts.push(font.into());
240        self
241    }
242
243    /// Sets the [`window::Settings`] of the [`Application`].
244    ///
245    /// Overwrites any previous [`window::Settings`].
246    pub fn window(self, window: window::Settings) -> Self {
247        Self { window, ..self }
248    }
249
250    /// Sets the [`window::Settings::position`] to [`window::Position::Centered`] in the [`Application`].
251    pub fn centered(self) -> Self {
252        Self {
253            window: window::Settings {
254                position: window::Position::Centered,
255                ..self.window
256            },
257            ..self
258        }
259    }
260
261    /// Sets the [`window::Settings::exit_on_close_request`] of the [`Application`].
262    pub fn exit_on_close_request(self, exit_on_close_request: bool) -> Self {
263        Self {
264            window: window::Settings {
265                exit_on_close_request,
266                ..self.window
267            },
268            ..self
269        }
270    }
271
272    /// Sets the [`window::Settings::size`] of the [`Application`].
273    pub fn window_size(self, size: impl Into<Size>) -> Self {
274        Self {
275            window: window::Settings {
276                size: size.into(),
277                ..self.window
278            },
279            ..self
280        }
281    }
282
283    /// Sets the [`window::Settings::transparent`] of the [`Application`].
284    pub fn transparent(self, transparent: bool) -> Self {
285        Self {
286            window: window::Settings {
287                transparent,
288                ..self.window
289            },
290            ..self
291        }
292    }
293
294    /// Sets the [`window::Settings::resizable`] of the [`Application`].
295    pub fn resizable(self, resizable: bool) -> Self {
296        Self {
297            window: window::Settings {
298                resizable,
299                ..self.window
300            },
301            ..self
302        }
303    }
304
305    /// Sets the [`window::Settings::decorations`] of the [`Application`].
306    pub fn decorations(self, decorations: bool) -> Self {
307        Self {
308            window: window::Settings {
309                decorations,
310                ..self.window
311            },
312            ..self
313        }
314    }
315
316    /// Sets the [`window::Settings::position`] of the [`Application`].
317    pub fn position(self, position: window::Position) -> Self {
318        Self {
319            window: window::Settings {
320                position,
321                ..self.window
322            },
323            ..self
324        }
325    }
326
327    /// Sets the [`window::Settings::level`] of the [`Application`].
328    pub fn level(self, level: window::Level) -> Self {
329        Self {
330            window: window::Settings {
331                level,
332                ..self.window
333            },
334            ..self
335        }
336    }
337
338    /// Sets the title of the [`Application`].
339    pub fn title(
340        self,
341        title: impl TitleFn<P::State>,
342    ) -> Application<
343        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
344    > {
345        Application {
346            raw: program::with_title(self.raw, move |state, _window| {
347                title.title(state)
348            }),
349            settings: self.settings,
350            window: self.window,
351            presets: self.presets,
352        }
353    }
354
355    /// Sets the subscription logic of the [`Application`].
356    pub fn subscription(
357        self,
358        f: impl Fn(&P::State) -> Subscription<P::Message>,
359    ) -> Application<
360        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
361    > {
362        Application {
363            raw: program::with_subscription(self.raw, f),
364            settings: self.settings,
365            window: self.window,
366            presets: self.presets,
367        }
368    }
369
370    /// Sets the theme logic of the [`Application`].
371    pub fn theme(
372        self,
373        f: impl ThemeFn<P::State, P::Theme>,
374    ) -> Application<
375        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
376    > {
377        Application {
378            raw: program::with_theme(self.raw, move |state, _window| {
379                f.theme(state)
380            }),
381            settings: self.settings,
382            window: self.window,
383            presets: self.presets,
384        }
385    }
386
387    /// Sets the style logic of the [`Application`].
388    pub fn style(
389        self,
390        f: impl Fn(&P::State, &P::Theme) -> theme::Style,
391    ) -> Application<
392        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
393    > {
394        Application {
395            raw: program::with_style(self.raw, f),
396            settings: self.settings,
397            window: self.window,
398            presets: self.presets,
399        }
400    }
401
402    /// Sets the scale factor of the [`Application`].
403    pub fn scale_factor(
404        self,
405        f: impl Fn(&P::State) -> f32,
406    ) -> Application<
407        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
408    > {
409        Application {
410            raw: program::with_scale_factor(self.raw, move |state, _window| {
411                f(state)
412            }),
413            settings: self.settings,
414            window: self.window,
415            presets: self.presets,
416        }
417    }
418
419    /// Sets the executor of the [`Application`].
420    pub fn executor<E>(
421        self,
422    ) -> Application<
423        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
424    >
425    where
426        E: Executor,
427    {
428        Application {
429            raw: program::with_executor::<P, E>(self.raw),
430            settings: self.settings,
431            window: self.window,
432            presets: self.presets,
433        }
434    }
435
436    /// Sets the boot presets of the [`Application`].
437    ///
438    /// Presets can be used to override the default booting strategy
439    /// of your application during testing to create reproducible
440    /// environments.
441    pub fn presets(
442        self,
443        presets: impl IntoIterator<Item = Preset<P::State, P::Message>>,
444    ) -> Self {
445        Self {
446            presets: presets.into_iter().collect(),
447            ..self
448        }
449    }
450}
451
452impl<P: Program> Program for Application<P> {
453    type State = P::State;
454    type Message = P::Message;
455    type Theme = P::Theme;
456    type Renderer = P::Renderer;
457    type Executor = P::Executor;
458
459    fn name() -> &'static str {
460        P::name()
461    }
462
463    fn settings(&self) -> Settings {
464        self.settings.clone()
465    }
466
467    fn window(&self) -> Option<window::Settings> {
468        Some(self.window.clone())
469    }
470
471    fn boot(&self) -> (Self::State, Task<Self::Message>) {
472        self.raw.boot()
473    }
474
475    fn update(
476        &self,
477        state: &mut Self::State,
478        message: Self::Message,
479    ) -> Task<Self::Message> {
480        debug::hot(|| self.raw.update(state, message))
481    }
482
483    fn view<'a>(
484        &self,
485        state: &'a Self::State,
486        window: window::Id,
487    ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
488        debug::hot(|| self.raw.view(state, window))
489    }
490
491    fn title(&self, state: &Self::State, window: window::Id) -> String {
492        debug::hot(|| self.raw.title(state, window))
493    }
494
495    fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
496        debug::hot(|| self.raw.subscription(state))
497    }
498
499    fn theme(
500        &self,
501        state: &Self::State,
502        window: iced_core::window::Id,
503    ) -> Option<Self::Theme> {
504        debug::hot(|| self.raw.theme(state, window))
505    }
506
507    fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
508        debug::hot(|| self.raw.style(state, theme))
509    }
510
511    fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
512        debug::hot(|| self.raw.scale_factor(state, window))
513    }
514
515    fn presets(&self) -> &[Preset<Self::State, Self::Message>] {
516        &self.presets
517    }
518}
519
520/// The logic to initialize the `State` of some [`Application`].
521///
522/// This trait is implemented for both `Fn() -> State` and
523/// `Fn() -> (State, Task<Message>)`.
524///
525/// In practice, this means that [`application`] can both take
526/// simple functions like `State::default` and more advanced ones
527/// that return a [`Task`].
528pub trait BootFn<State, Message> {
529    /// Initializes the [`Application`] state.
530    fn boot(&self) -> (State, Task<Message>);
531}
532
533impl<T, C, State, Message> BootFn<State, Message> for T
534where
535    T: Fn() -> C,
536    C: IntoBoot<State, Message>,
537{
538    fn boot(&self) -> (State, Task<Message>) {
539        self().into_boot()
540    }
541}
542
543/// The initial state of some [`Application`].
544pub trait IntoBoot<State, Message> {
545    /// Turns some type into the initial state of some [`Application`].
546    fn into_boot(self) -> (State, Task<Message>);
547}
548
549impl<State, Message> IntoBoot<State, Message> for State {
550    fn into_boot(self) -> (State, Task<Message>) {
551        (self, Task::none())
552    }
553}
554
555impl<State, Message> IntoBoot<State, Message> for (State, Task<Message>) {
556    fn into_boot(self) -> (State, Task<Message>) {
557        self
558    }
559}
560
561/// The title logic of some [`Application`].
562///
563/// This trait is implemented both for `&static str` and
564/// any closure `Fn(&State) -> String`.
565///
566/// This trait allows the [`application`] builder to take any of them.
567pub trait TitleFn<State> {
568    /// Produces the title of the [`Application`].
569    fn title(&self, state: &State) -> String;
570}
571
572impl<State> TitleFn<State> for &'static str {
573    fn title(&self, _state: &State) -> String {
574        self.to_string()
575    }
576}
577
578impl<T, State> TitleFn<State> for T
579where
580    T: Fn(&State) -> String,
581{
582    fn title(&self, state: &State) -> String {
583        self(state)
584    }
585}
586
587/// The update logic of some [`Application`].
588///
589/// This trait allows the [`application`] builder to take any closure that
590/// returns any `Into<Task<Message>>`.
591pub trait UpdateFn<State, Message> {
592    /// Processes the message and updates the state of the [`Application`].
593    fn update(&self, state: &mut State, message: Message) -> Task<Message>;
594}
595
596impl<State, Message> UpdateFn<State, Message> for () {
597    fn update(&self, _state: &mut State, _message: Message) -> Task<Message> {
598        Task::none()
599    }
600}
601
602impl<T, State, Message, C> UpdateFn<State, Message> for T
603where
604    T: Fn(&mut State, Message) -> C,
605    C: Into<Task<Message>>,
606{
607    fn update(&self, state: &mut State, message: Message) -> Task<Message> {
608        self(state, message).into()
609    }
610}
611
612/// The view logic of some [`Application`].
613///
614/// This trait allows the [`application`] builder to take any closure that
615/// returns any `Into<Element<'_, Message>>`.
616pub trait ViewFn<'a, State, Message, Theme, Renderer> {
617    /// Produces the widget of the [`Application`].
618    fn view(&self, state: &'a State) -> Element<'a, Message, Theme, Renderer>;
619}
620
621impl<'a, T, State, Message, Theme, Renderer, Widget>
622    ViewFn<'a, State, Message, Theme, Renderer> for T
623where
624    T: Fn(&'a State) -> Widget,
625    State: 'static,
626    Widget: Into<Element<'a, Message, Theme, Renderer>>,
627{
628    fn view(&self, state: &'a State) -> Element<'a, Message, Theme, Renderer> {
629        self(state).into()
630    }
631}
632
633/// The theme logic of some [`Application`].
634///
635/// Any implementors of this trait can be provided as an argument to
636/// [`Application::theme`].
637///
638/// `iced` provides two implementors:
639/// - the built-in [`Theme`] itself
640/// - and any `Fn(&State) -> impl Into<Option<Theme>>`.
641pub trait ThemeFn<State, Theme> {
642    /// Returns the theme of the [`Application`] for the current state.
643    ///
644    /// If `None` is returned, `iced` will try to use a theme that
645    /// matches the system color scheme.
646    fn theme(&self, state: &State) -> Option<Theme>;
647}
648
649impl<State> ThemeFn<State, Theme> for Theme {
650    fn theme(&self, _state: &State) -> Option<Theme> {
651        Some(self.clone())
652    }
653}
654
655impl<F, T, State, Theme> ThemeFn<State, Theme> for F
656where
657    F: Fn(&State) -> T,
658    T: Into<Option<Theme>>,
659{
660    fn theme(&self, state: &State) -> Option<Theme> {
661        (self)(state).into()
662    }
663}