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