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