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(
202            feature = "debug",
203            not(feature = "tester"),
204            not(target_arch = "wasm32")
205        ))]
206        let program = iced_devtools::attach(self);
207
208        #[cfg(not(any(
209            feature = "tester",
210            all(feature = "debug", not(target_arch = "wasm32"))
211        )))]
212        let program = self;
213
214        Ok(shell::run(program)?)
215    }
216
217    /// Sets the [`Settings`] that will be used to run the [`Application`].
218    pub fn settings(self, settings: Settings) -> Self {
219        Self { settings, ..self }
220    }
221
222    /// Sets the [`Settings::antialiasing`] of the [`Application`].
223    pub fn antialiasing(self, antialiasing: bool) -> Self {
224        Self {
225            settings: Settings {
226                antialiasing,
227                ..self.settings
228            },
229            ..self
230        }
231    }
232
233    /// Sets the default [`Font`] of the [`Application`].
234    pub fn default_font(self, default_font: Font) -> Self {
235        Self {
236            settings: Settings {
237                default_font,
238                ..self.settings
239            },
240            ..self
241        }
242    }
243
244    /// Adds a font to the list of fonts that will be loaded at the start of the [`Application`].
245    pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self {
246        self.settings.fonts.push(font.into());
247        self
248    }
249
250    /// Sets the [`window::Settings`] of the [`Application`].
251    ///
252    /// Overwrites any previous [`window::Settings`].
253    pub fn window(self, window: window::Settings) -> Self {
254        Self { window, ..self }
255    }
256
257    /// Sets the [`window::Settings::position`] to [`window::Position::Centered`] in the [`Application`].
258    pub fn centered(self) -> Self {
259        Self {
260            window: window::Settings {
261                position: window::Position::Centered,
262                ..self.window
263            },
264            ..self
265        }
266    }
267
268    /// Sets the [`window::Settings::exit_on_close_request`] of the [`Application`].
269    pub fn exit_on_close_request(self, exit_on_close_request: bool) -> Self {
270        Self {
271            window: window::Settings {
272                exit_on_close_request,
273                ..self.window
274            },
275            ..self
276        }
277    }
278
279    /// Sets the [`window::Settings::size`] of the [`Application`].
280    pub fn window_size(self, size: impl Into<Size>) -> Self {
281        Self {
282            window: window::Settings {
283                size: size.into(),
284                ..self.window
285            },
286            ..self
287        }
288    }
289
290    /// Sets the [`window::Settings::transparent`] of the [`Application`].
291    pub fn transparent(self, transparent: bool) -> Self {
292        Self {
293            window: window::Settings {
294                transparent,
295                ..self.window
296            },
297            ..self
298        }
299    }
300
301    /// Sets the [`window::Settings::resizable`] of the [`Application`].
302    pub fn resizable(self, resizable: bool) -> Self {
303        Self {
304            window: window::Settings {
305                resizable,
306                ..self.window
307            },
308            ..self
309        }
310    }
311
312    /// Sets the [`window::Settings::decorations`] of the [`Application`].
313    pub fn decorations(self, decorations: bool) -> Self {
314        Self {
315            window: window::Settings {
316                decorations,
317                ..self.window
318            },
319            ..self
320        }
321    }
322
323    /// Sets the [`window::Settings::position`] of the [`Application`].
324    pub fn position(self, position: window::Position) -> Self {
325        Self {
326            window: window::Settings {
327                position,
328                ..self.window
329            },
330            ..self
331        }
332    }
333
334    /// Sets the [`window::Settings::level`] of the [`Application`].
335    pub fn level(self, level: window::Level) -> Self {
336        Self {
337            window: window::Settings {
338                level,
339                ..self.window
340            },
341            ..self
342        }
343    }
344
345    /// Sets the title of the [`Application`].
346    pub fn title(
347        self,
348        title: impl TitleFn<P::State>,
349    ) -> Application<
350        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
351    > {
352        Application {
353            raw: program::with_title(self.raw, move |state, _window| {
354                title.title(state)
355            }),
356            settings: self.settings,
357            window: self.window,
358            presets: self.presets,
359        }
360    }
361
362    /// Sets the subscription logic of the [`Application`].
363    pub fn subscription(
364        self,
365        f: impl Fn(&P::State) -> Subscription<P::Message>,
366    ) -> Application<
367        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
368    > {
369        Application {
370            raw: program::with_subscription(self.raw, f),
371            settings: self.settings,
372            window: self.window,
373            presets: self.presets,
374        }
375    }
376
377    /// Sets the theme logic of the [`Application`].
378    pub fn theme(
379        self,
380        f: impl ThemeFn<P::State, P::Theme>,
381    ) -> Application<
382        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
383    > {
384        Application {
385            raw: program::with_theme(self.raw, move |state, _window| {
386                f.theme(state)
387            }),
388            settings: self.settings,
389            window: self.window,
390            presets: self.presets,
391        }
392    }
393
394    /// Sets the style logic of the [`Application`].
395    pub fn style(
396        self,
397        f: impl Fn(&P::State, &P::Theme) -> theme::Style,
398    ) -> Application<
399        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
400    > {
401        Application {
402            raw: program::with_style(self.raw, f),
403            settings: self.settings,
404            window: self.window,
405            presets: self.presets,
406        }
407    }
408
409    /// Sets the scale factor of the [`Application`].
410    pub fn scale_factor(
411        self,
412        f: impl Fn(&P::State) -> f32,
413    ) -> Application<
414        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
415    > {
416        Application {
417            raw: program::with_scale_factor(self.raw, move |state, _window| {
418                f(state)
419            }),
420            settings: self.settings,
421            window: self.window,
422            presets: self.presets,
423        }
424    }
425
426    /// Sets the executor of the [`Application`].
427    pub fn executor<E>(
428        self,
429    ) -> Application<
430        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>,
431    >
432    where
433        E: Executor,
434    {
435        Application {
436            raw: program::with_executor::<P, E>(self.raw),
437            settings: self.settings,
438            window: self.window,
439            presets: self.presets,
440        }
441    }
442
443    /// Sets the boot presets of the [`Application`].
444    ///
445    /// Presets can be used to override the default booting strategy
446    /// of your application during testing to create reproducible
447    /// environments.
448    pub fn presets(
449        self,
450        presets: impl IntoIterator<Item = Preset<P::State, P::Message>>,
451    ) -> Self {
452        Self {
453            presets: presets.into_iter().collect(),
454            ..self
455        }
456    }
457}
458
459impl<P: Program> Program for Application<P> {
460    type State = P::State;
461    type Message = P::Message;
462    type Theme = P::Theme;
463    type Renderer = P::Renderer;
464    type Executor = P::Executor;
465
466    fn name() -> &'static str {
467        P::name()
468    }
469
470    fn settings(&self) -> Settings {
471        self.settings.clone()
472    }
473
474    fn window(&self) -> Option<window::Settings> {
475        Some(self.window.clone())
476    }
477
478    fn boot(&self) -> (Self::State, Task<Self::Message>) {
479        self.raw.boot()
480    }
481
482    fn update(
483        &self,
484        state: &mut Self::State,
485        message: Self::Message,
486    ) -> Task<Self::Message> {
487        debug::hot(|| self.raw.update(state, message))
488    }
489
490    fn view<'a>(
491        &self,
492        state: &'a Self::State,
493        window: window::Id,
494    ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
495        debug::hot(|| self.raw.view(state, window))
496    }
497
498    fn title(&self, state: &Self::State, window: window::Id) -> String {
499        debug::hot(|| self.raw.title(state, window))
500    }
501
502    fn subscription(&self, state: &Self::State) -> Subscription<Self::Message> {
503        debug::hot(|| self.raw.subscription(state))
504    }
505
506    fn theme(
507        &self,
508        state: &Self::State,
509        window: iced_core::window::Id,
510    ) -> Option<Self::Theme> {
511        debug::hot(|| self.raw.theme(state, window))
512    }
513
514    fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style {
515        debug::hot(|| self.raw.style(state, theme))
516    }
517
518    fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 {
519        debug::hot(|| self.raw.scale_factor(state, window))
520    }
521
522    fn presets(&self) -> &[Preset<Self::State, Self::Message>] {
523        &self.presets
524    }
525}
526
527/// The logic to initialize the `State` of some [`Application`].
528///
529/// This trait is implemented for both `Fn() -> State` and
530/// `Fn() -> (State, Task<Message>)`.
531///
532/// In practice, this means that [`application`] can both take
533/// simple functions like `State::default` and more advanced ones
534/// that return a [`Task`].
535pub trait BootFn<State, Message> {
536    /// Initializes the [`Application`] state.
537    fn boot(&self) -> (State, Task<Message>);
538}
539
540impl<T, C, State, Message> BootFn<State, Message> for T
541where
542    T: Fn() -> C,
543    C: IntoBoot<State, Message>,
544{
545    fn boot(&self) -> (State, Task<Message>) {
546        self().into_boot()
547    }
548}
549
550/// The initial state of some [`Application`].
551pub trait IntoBoot<State, Message> {
552    /// Turns some type into the initial state of some [`Application`].
553    fn into_boot(self) -> (State, Task<Message>);
554}
555
556impl<State, Message> IntoBoot<State, Message> for State {
557    fn into_boot(self) -> (State, Task<Message>) {
558        (self, Task::none())
559    }
560}
561
562impl<State, Message> IntoBoot<State, Message> for (State, Task<Message>) {
563    fn into_boot(self) -> (State, Task<Message>) {
564        self
565    }
566}
567
568/// The title logic of some [`Application`].
569///
570/// This trait is implemented both for `&static str` and
571/// any closure `Fn(&State) -> String`.
572///
573/// This trait allows the [`application`] builder to take any of them.
574pub trait TitleFn<State> {
575    /// Produces the title of the [`Application`].
576    fn title(&self, state: &State) -> String;
577}
578
579impl<State> TitleFn<State> for &'static str {
580    fn title(&self, _state: &State) -> String {
581        self.to_string()
582    }
583}
584
585impl<T, State> TitleFn<State> for T
586where
587    T: Fn(&State) -> String,
588{
589    fn title(&self, state: &State) -> String {
590        self(state)
591    }
592}
593
594/// The update logic of some [`Application`].
595///
596/// This trait allows the [`application`] builder to take any closure that
597/// returns any `Into<Task<Message>>`.
598pub trait UpdateFn<State, Message> {
599    /// Processes the message and updates the state of the [`Application`].
600    fn update(&self, state: &mut State, message: Message) -> Task<Message>;
601}
602
603impl<State, Message> UpdateFn<State, Message> for () {
604    fn update(&self, _state: &mut State, _message: Message) -> Task<Message> {
605        Task::none()
606    }
607}
608
609impl<T, State, Message, C> UpdateFn<State, Message> for T
610where
611    T: Fn(&mut State, Message) -> C,
612    C: Into<Task<Message>>,
613{
614    fn update(&self, state: &mut State, message: Message) -> Task<Message> {
615        self(state, message).into()
616    }
617}
618
619/// The view logic of some [`Application`].
620///
621/// This trait allows the [`application`] builder to take any closure that
622/// returns any `Into<Element<'_, Message>>`.
623pub trait ViewFn<'a, State, Message, Theme, Renderer> {
624    /// Produces the widget of the [`Application`].
625    fn view(&self, state: &'a State) -> Element<'a, Message, Theme, Renderer>;
626}
627
628impl<'a, T, State, Message, Theme, Renderer, Widget>
629    ViewFn<'a, State, Message, Theme, Renderer> for T
630where
631    T: Fn(&'a State) -> Widget,
632    State: 'static,
633    Widget: Into<Element<'a, Message, Theme, Renderer>>,
634{
635    fn view(&self, state: &'a State) -> Element<'a, Message, Theme, Renderer> {
636        self(state).into()
637    }
638}
639
640/// The theme logic of some [`Application`].
641///
642/// Any implementors of this trait can be provided as an argument to
643/// [`Application::theme`].
644///
645/// `iced` provides two implementors:
646/// - the built-in [`Theme`] itself
647/// - and any `Fn(&State) -> impl Into<Option<Theme>>`.
648pub trait ThemeFn<State, Theme> {
649    /// Returns the theme of the [`Application`] for the current state.
650    ///
651    /// If `None` is returned, `iced` will try to use a theme that
652    /// matches the system color scheme.
653    fn theme(&self, state: &State) -> Option<Theme>;
654}
655
656impl<State> ThemeFn<State, Theme> for Theme {
657    fn theme(&self, _state: &State) -> Option<Theme> {
658        Some(self.clone())
659    }
660}
661
662impl<F, T, State, Theme> ThemeFn<State, Theme> for F
663where
664    F: Fn(&State) -> T,
665    T: Into<Option<Theme>>,
666{
667    fn theme(&self, state: &State) -> Option<Theme> {
668        (self)(state).into()
669    }
670}