Skip to main content

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