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