//! Create and run iced applications step by step. //! //! # Example //! ```no_run,standalone_crate //! use iced::widget::{button, column, text, Column}; //! use iced::Theme; //! //! pub fn main() -> iced::Result { //! iced::application(u64::default, update, view) //! .theme(Theme::Dark) //! .centered() //! .run() //! } //! //! #[derive(Debug, Clone)] //! enum Message { //! Increment, //! } //! //! fn update(value: &mut u64, message: Message) { //! match message { //! Message::Increment => *value += 1, //! } //! } //! //! fn view(value: &u64) -> Column { //! column![ //! text(value), //! button("+").on_press(Message::Increment), //! ] //! } //! ``` use crate::message; use crate::program::{self, Program}; use crate::shell; use crate::theme; use crate::window; use crate::{ Element, Executor, Font, Preset, Result, Settings, Size, Subscription, Task, Theme, }; use iced_debug as debug; use std::borrow::Cow; pub mod timed; pub use timed::timed; /// Creates an iced [`Application`] given its boot, update, and view logic. /// /// # Example /// ```no_run,standalone_crate /// use iced::widget::{button, column, text, Column}; /// /// pub fn main() -> iced::Result { /// iced::application(u64::default, update, view).run() /// } /// /// #[derive(Debug, Clone)] /// enum Message { /// Increment, /// } /// /// fn update(value: &mut u64, message: Message) { /// match message { /// Message::Increment => *value += 1, /// } /// } /// /// fn view(value: &u64) -> Column { /// column![ /// text(value), /// button("+").on_press(Message::Increment), /// ] /// } /// ``` pub fn application( boot: impl BootFn, update: impl UpdateFn, view: impl for<'a> ViewFn<'a, State, Message, Theme, Renderer>, ) -> Application> where State: 'static, Message: Send + 'static, Theme: theme::Base, Renderer: program::Renderer, { use std::marker::PhantomData; struct Instance { boot: Boot, update: Update, view: View, _state: PhantomData, _message: PhantomData, _theme: PhantomData, _renderer: PhantomData, } impl Program for Instance where Message: Send + 'static, Theme: theme::Base, Renderer: program::Renderer, Boot: self::BootFn, Update: self::UpdateFn, View: for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>, { type State = State; type Message = Message; type Theme = Theme; type Renderer = Renderer; type Executor = iced_futures::backend::default::Executor; fn name() -> &'static str { let name = std::any::type_name::(); name.split("::").next().unwrap_or("a_cool_application") } fn boot(&self) -> (State, Task) { self.boot.boot() } fn update( &self, state: &mut Self::State, message: Self::Message, ) -> Task { self.update.update(state, message) } fn view<'a>( &self, state: &'a Self::State, _window: window::Id, ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { self.view.view(state) } fn settings(&self) -> Settings { Settings::default() } fn window(&self) -> Option { Some(window::Settings::default()) } } Application { raw: Instance { boot, update, view, _state: PhantomData, _message: PhantomData, _theme: PhantomData, _renderer: PhantomData, }, settings: Settings::default(), window: window::Settings::default(), presets: Vec::new(), } } /// The underlying definition and configuration of an iced application. /// /// You can use this API to create and run iced applications /// step by step—without coupling your logic to a trait /// or a specific type. /// /// You can create an [`Application`] with the [`application`] helper. #[derive(Debug)] pub struct Application { raw: P, settings: Settings, window: window::Settings, presets: Vec>, } impl Application

{ /// Runs the [`Application`]. pub fn run(self) -> Result where Self: 'static, P::Message: message::MaybeDebug + message::MaybeClone, { #[cfg(feature = "debug")] iced_debug::init(iced_debug::Metadata { name: P::name(), theme: None, can_time_travel: cfg!(feature = "time-travel"), }); #[cfg(feature = "tester")] let program = iced_tester::attach(self); #[cfg(all( feature = "debug", not(feature = "tester"), not(target_arch = "wasm32") ))] let program = iced_devtools::attach(self); #[cfg(not(any( feature = "tester", all(feature = "debug", not(target_arch = "wasm32")) )))] let program = self; Ok(shell::run(program)?) } /// Sets the [`Settings`] that will be used to run the [`Application`]. pub fn settings(self, settings: Settings) -> Self { Self { settings, ..self } } /// Sets the [`Settings::antialiasing`] of the [`Application`]. pub fn antialiasing(self, antialiasing: bool) -> Self { Self { settings: Settings { antialiasing, ..self.settings }, ..self } } /// Sets the default [`Font`] of the [`Application`]. pub fn default_font(self, default_font: Font) -> Self { Self { settings: Settings { default_font, ..self.settings }, ..self } } /// Adds a font to the list of fonts that will be loaded at the start of the [`Application`]. pub fn font(mut self, font: impl Into>) -> Self { self.settings.fonts.push(font.into()); self } /// Sets the [`window::Settings`] of the [`Application`]. /// /// Overwrites any previous [`window::Settings`]. pub fn window(self, window: window::Settings) -> Self { Self { window, ..self } } /// Sets the [`window::Settings::position`] to [`window::Position::Centered`] in the [`Application`]. pub fn centered(self) -> Self { Self { window: window::Settings { position: window::Position::Centered, ..self.window }, ..self } } /// Sets the [`window::Settings::exit_on_close_request`] of the [`Application`]. pub fn exit_on_close_request(self, exit_on_close_request: bool) -> Self { Self { window: window::Settings { exit_on_close_request, ..self.window }, ..self } } /// Sets the [`window::Settings::size`] of the [`Application`]. pub fn window_size(self, size: impl Into) -> Self { Self { window: window::Settings { size: size.into(), ..self.window }, ..self } } /// Sets the [`window::Settings::transparent`] of the [`Application`]. pub fn transparent(self, transparent: bool) -> Self { Self { window: window::Settings { transparent, ..self.window }, ..self } } /// Sets the [`window::Settings::resizable`] of the [`Application`]. pub fn resizable(self, resizable: bool) -> Self { Self { window: window::Settings { resizable, ..self.window }, ..self } } /// Sets the [`window::Settings::decorations`] of the [`Application`]. pub fn decorations(self, decorations: bool) -> Self { Self { window: window::Settings { decorations, ..self.window }, ..self } } /// Sets the [`window::Settings::position`] of the [`Application`]. pub fn position(self, position: window::Position) -> Self { Self { window: window::Settings { position, ..self.window }, ..self } } /// Sets the [`window::Settings::level`] of the [`Application`]. pub fn level(self, level: window::Level) -> Self { Self { window: window::Settings { level, ..self.window }, ..self } } /// Sets the title of the [`Application`]. pub fn title( self, title: impl TitleFn, ) -> Application< impl Program, > { Application { raw: program::with_title(self.raw, move |state, _window| { title.title(state) }), settings: self.settings, window: self.window, presets: self.presets, } } /// Sets the subscription logic of the [`Application`]. pub fn subscription( self, f: impl Fn(&P::State) -> Subscription, ) -> Application< impl Program, > { Application { raw: program::with_subscription(self.raw, f), settings: self.settings, window: self.window, presets: self.presets, } } /// Sets the theme logic of the [`Application`]. pub fn theme( self, f: impl ThemeFn, ) -> Application< impl Program, > { Application { raw: program::with_theme(self.raw, move |state, _window| { f.theme(state) }), settings: self.settings, window: self.window, presets: self.presets, } } /// Sets the style logic of the [`Application`]. pub fn style( self, f: impl Fn(&P::State, &P::Theme) -> theme::Style, ) -> Application< impl Program, > { Application { raw: program::with_style(self.raw, f), settings: self.settings, window: self.window, presets: self.presets, } } /// Sets the scale factor of the [`Application`]. pub fn scale_factor( self, f: impl Fn(&P::State) -> f32, ) -> Application< impl Program, > { Application { raw: program::with_scale_factor(self.raw, move |state, _window| { f(state) }), settings: self.settings, window: self.window, presets: self.presets, } } /// Sets the executor of the [`Application`]. pub fn executor( self, ) -> Application< impl Program, > where E: Executor, { Application { raw: program::with_executor::(self.raw), settings: self.settings, window: self.window, presets: self.presets, } } /// Sets the boot presets of the [`Application`]. /// /// Presets can be used to override the default booting strategy /// of your application during testing to create reproducible /// environments. pub fn presets( self, presets: impl IntoIterator>, ) -> Self { Self { presets: presets.into_iter().collect(), ..self } } } impl Program for Application

{ type State = P::State; type Message = P::Message; type Theme = P::Theme; type Renderer = P::Renderer; type Executor = P::Executor; fn name() -> &'static str { P::name() } fn settings(&self) -> Settings { self.settings.clone() } fn window(&self) -> Option { Some(self.window.clone()) } fn boot(&self) -> (Self::State, Task) { self.raw.boot() } fn update( &self, state: &mut Self::State, message: Self::Message, ) -> Task { debug::hot(|| self.raw.update(state, message)) } fn view<'a>( &self, state: &'a Self::State, window: window::Id, ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { debug::hot(|| self.raw.view(state, window)) } fn title(&self, state: &Self::State, window: window::Id) -> String { debug::hot(|| self.raw.title(state, window)) } fn subscription(&self, state: &Self::State) -> Subscription { debug::hot(|| self.raw.subscription(state)) } fn theme( &self, state: &Self::State, window: iced_core::window::Id, ) -> Option { debug::hot(|| self.raw.theme(state, window)) } fn style(&self, state: &Self::State, theme: &Self::Theme) -> theme::Style { debug::hot(|| self.raw.style(state, theme)) } fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 { debug::hot(|| self.raw.scale_factor(state, window)) } fn presets(&self) -> &[Preset] { &self.presets } } /// The logic to initialize the `State` of some [`Application`]. /// /// This trait is implemented for both `Fn() -> State` and /// `Fn() -> (State, Task)`. /// /// In practice, this means that [`application`] can both take /// simple functions like `State::default` and more advanced ones /// that return a [`Task`]. pub trait BootFn { /// Initializes the [`Application`] state. fn boot(&self) -> (State, Task); } impl BootFn for T where T: Fn() -> C, C: IntoBoot, { fn boot(&self) -> (State, Task) { self().into_boot() } } /// The initial state of some [`Application`]. pub trait IntoBoot { /// Turns some type into the initial state of some [`Application`]. fn into_boot(self) -> (State, Task); } impl IntoBoot for State { fn into_boot(self) -> (State, Task) { (self, Task::none()) } } impl IntoBoot for (State, Task) { fn into_boot(self) -> (State, Task) { self } } /// The title logic of some [`Application`]. /// /// This trait is implemented both for `&static str` and /// any closure `Fn(&State) -> String`. /// /// This trait allows the [`application`] builder to take any of them. pub trait TitleFn { /// Produces the title of the [`Application`]. fn title(&self, state: &State) -> String; } impl TitleFn for &'static str { fn title(&self, _state: &State) -> String { self.to_string() } } impl TitleFn for T where T: Fn(&State) -> String, { fn title(&self, state: &State) -> String { self(state) } } /// The update logic of some [`Application`]. /// /// This trait allows the [`application`] builder to take any closure that /// returns any `Into>`. pub trait UpdateFn { /// Processes the message and updates the state of the [`Application`]. fn update(&self, state: &mut State, message: Message) -> Task; } impl UpdateFn for () { fn update(&self, _state: &mut State, _message: Message) -> Task { Task::none() } } impl UpdateFn for T where T: Fn(&mut State, Message) -> C, C: Into>, { fn update(&self, state: &mut State, message: Message) -> Task { self(state, message).into() } } /// The view logic of some [`Application`]. /// /// This trait allows the [`application`] builder to take any closure that /// returns any `Into>`. pub trait ViewFn<'a, State, Message, Theme, Renderer> { /// Produces the widget of the [`Application`]. fn view(&self, state: &'a State) -> Element<'a, Message, Theme, Renderer>; } impl<'a, T, State, Message, Theme, Renderer, Widget> ViewFn<'a, State, Message, Theme, Renderer> for T where T: Fn(&'a State) -> Widget, State: 'static, Widget: Into>, { fn view(&self, state: &'a State) -> Element<'a, Message, Theme, Renderer> { self(state).into() } } /// The theme logic of some [`Application`]. /// /// Any implementors of this trait can be provided as an argument to /// [`Application::theme`]. /// /// `iced` provides two implementors: /// - the built-in [`Theme`] itself /// - and any `Fn(&State) -> impl Into>`. pub trait ThemeFn { /// Returns the theme of the [`Application`] for the current state. /// /// If `None` is returned, `iced` will try to use a theme that /// matches the system color scheme. fn theme(&self, state: &State) -> Option; } impl ThemeFn for Theme { fn theme(&self, _state: &State) -> Option { Some(self.clone()) } } impl ThemeFn for F where F: Fn(&State) -> T, T: Into>, { fn theme(&self, state: &State) -> Option { (self)(state).into() } }