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