diff options
Diffstat (limited to '')
| -rw-r--r-- | src/advanced.rs | 1 | ||||
| -rw-r--r-- | src/application.rs | 590 | ||||
| -rw-r--r-- | src/daemon.rs | 298 | ||||
| -rw-r--r-- | src/lib.rs | 33 | ||||
| -rw-r--r-- | src/multi_window.rs | 254 | ||||
| -rw-r--r-- | src/program.rs | 754 | ||||
| -rw-r--r-- | src/settings.rs | 49 | 
7 files changed, 949 insertions, 1030 deletions
| diff --git a/src/advanced.rs b/src/advanced.rs index 5826ba0f..8d06e805 100644 --- a/src/advanced.rs +++ b/src/advanced.rs @@ -1,5 +1,4 @@  //! Leverage advanced concepts like custom widgets. -pub use crate::application::Application;  pub use crate::core::clipboard::{self, Clipboard};  pub use crate::core::image;  pub use crate::core::layout::{self, Layout}; diff --git a/src/application.rs b/src/application.rs index 4cd4a87d..edca6e79 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,278 +1,426 @@ -//! Build interactive cross-platform applications. -use crate::core::text; -use crate::graphics::compositor; -use crate::shell::application; -use crate::{Element, Executor, Settings, Subscription, Task}; +//! Create and run iced applications step by step. +//! +//! # Example +//! ```no_run +//! use iced::widget::{button, column, text, Column}; +//! use iced::Theme; +//! +//! pub fn main() -> iced::Result { +//!     iced::application("A counter", 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<Message> { +//!     column![ +//!         text(value), +//!         button("+").on_press(Message::Increment), +//!     ] +//! } +//! ``` +use crate::program::{self, Program}; +use crate::window; +use crate::{Element, Font, Result, Settings, Size, Subscription, Task}; -pub use application::{Appearance, DefaultStyle}; +use std::borrow::Cow; -/// An interactive cross-platform application. -/// -/// This trait is the main entrypoint of Iced. Once implemented, you can run -/// your GUI application by simply calling [`run`](#method.run). -/// -/// - On native platforms, it will run in its own window. -/// - On the web, it will take control of the `<title>` and the `<body>` of the -///   document. -/// -/// An [`Application`] can execute asynchronous actions by returning a -/// [`Task`] in some of its methods. -/// -/// When using an [`Application`] with the `debug` feature enabled, a debug view -/// can be toggled by pressing `F12`. -/// -/// # Examples -/// [The repository has a bunch of examples] that use the [`Application`] trait: -/// -/// - [`clock`], an application that uses the [`Canvas`] widget to draw a clock -/// and its hands to display the current time. -/// - [`download_progress`], a basic application that asynchronously downloads -/// a dummy file of 100 MB and tracks the download progress. -/// - [`events`], a log of native events displayed using a conditional -/// [`Subscription`]. -/// - [`game_of_life`], an interactive version of the [Game of Life], invented -/// by [John Horton Conway]. -/// - [`pokedex`], an application that displays a random Pokédex entry (sprite -/// included!) by using the [PokéAPI]. -/// - [`solar_system`], an animated solar system drawn using the [`Canvas`] widget -/// and showcasing how to compose different transforms. -/// - [`stopwatch`], a watch with start/stop and reset buttons showcasing how -/// to listen to time. -/// - [`todos`], a todos tracker inspired by [TodoMVC]. -/// -/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.12/examples -/// [`clock`]: https://github.com/iced-rs/iced/tree/0.12/examples/clock -/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.12/examples/download_progress -/// [`events`]: https://github.com/iced-rs/iced/tree/0.12/examples/events -/// [`game_of_life`]: https://github.com/iced-rs/iced/tree/0.12/examples/game_of_life -/// [`pokedex`]: https://github.com/iced-rs/iced/tree/0.12/examples/pokedex -/// [`solar_system`]: https://github.com/iced-rs/iced/tree/0.12/examples/solar_system -/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.12/examples/stopwatch -/// [`todos`]: https://github.com/iced-rs/iced/tree/0.12/examples/todos -/// [`Sandbox`]: crate::Sandbox -/// [`Canvas`]: crate::widget::Canvas -/// [PokéAPI]: https://pokeapi.co/ -/// [TodoMVC]: http://todomvc.com/ -/// -/// ## A simple "Hello, world!" -/// -/// If you just want to get started, here is a simple [`Application`] that -/// says "Hello, world!": +pub use crate::shell::program::{Appearance, DefaultStyle}; + +/// Creates an iced [`Application`] given its title, update, and view logic.  /// +/// # Example  /// ```no_run -/// use iced::advanced::Application; -/// use iced::executor; -/// use iced::{Task, Element, Settings, Theme, Renderer}; +/// use iced::widget::{button, column, text, Column};  ///  /// pub fn main() -> iced::Result { -///     Hello::run(Settings::default()) +///     iced::application("A counter", update, view).run()  /// }  /// -/// struct Hello; -/// -/// impl Application for Hello { -///     type Executor = executor::Default; -///     type Flags = (); -///     type Message = (); -///     type Theme = Theme; -///     type Renderer = Renderer; -/// -///     fn new(_flags: ()) -> (Hello, Task<Self::Message>) { -///         (Hello, Task::none()) -///     } -/// -///     fn title(&self) -> String { -///         String::from("A cool application") -///     } +/// #[derive(Debug, Clone)] +/// enum Message { +///     Increment, +/// }  /// -///     fn update(&mut self, _message: Self::Message) -> Task<Self::Message> { -///         Task::none() +/// fn update(value: &mut u64, message: Message) { +///     match message { +///         Message::Increment => *value += 1,  ///     } +/// }  /// -///     fn view(&self) -> Element<Self::Message> { -///         "Hello, world!".into() -///     } +/// fn view(value: &u64) -> Column<Message> { +///     column![ +///         text(value), +///         button("+").on_press(Message::Increment), +///     ]  /// }  /// ``` -pub trait Application: Sized +pub fn application<State, Message, Theme, Renderer>( +    title: impl Title<State>, +    update: impl Update<State, Message>, +    view: impl for<'a> self::View<'a, State, Message, Theme, Renderer>, +) -> Application<impl Program<State = State, Message = Message, Theme = Theme>>  where -    Self::Theme: DefaultStyle, +    State: 'static, +    Message: Send + std::fmt::Debug + 'static, +    Theme: Default + DefaultStyle, +    Renderer: program::Renderer,  { -    /// The [`Executor`] that will run commands and subscriptions. -    /// -    /// The [default executor] can be a good starting point! -    /// -    /// [`Executor`]: Self::Executor -    /// [default executor]: crate::executor::Default -    type Executor: Executor; +    use std::marker::PhantomData; -    /// The type of __messages__ your [`Application`] will produce. -    type Message: std::fmt::Debug + Send; +    struct Instance<State, Message, Theme, Renderer, Update, View> { +        update: Update, +        view: View, +        _state: PhantomData<State>, +        _message: PhantomData<Message>, +        _theme: PhantomData<Theme>, +        _renderer: PhantomData<Renderer>, +    } -    /// The theme of your [`Application`]. -    type Theme: Default; +    impl<State, Message, Theme, Renderer, Update, View> Program +        for Instance<State, Message, Theme, Renderer, Update, View> +    where +        Message: Send + std::fmt::Debug + 'static, +        Theme: Default + DefaultStyle, +        Renderer: program::Renderer, +        Update: self::Update<State, Message>, +        View: for<'a> self::View<'a, State, Message, Theme, Renderer>, +    { +        type State = State; +        type Message = Message; +        type Theme = Theme; +        type Renderer = Renderer; +        type Executor = iced_futures::backend::default::Executor; -    /// The renderer of your [`Application`]. -    type Renderer: text::Renderer + compositor::Default; +        fn load(&self) -> Task<Self::Message> { +            Task::none() +        } -    /// The data needed to initialize your [`Application`]. -    type Flags; +        fn update( +            &self, +            state: &mut Self::State, +            message: Self::Message, +        ) -> Task<Self::Message> { +            self.update.update(state, message).into() +        } -    /// Initializes the [`Application`] with the flags provided to -    /// [`run`] as part of the [`Settings`]. -    /// -    /// Here is where you should return the initial state of your app. -    /// -    /// Additionally, you can return a [`Task`] if you need to perform some -    /// async action in the background on startup. This is useful if you want to -    /// load state from a file, perform an initial HTTP request, etc. -    /// -    /// [`run`]: Self::run -    fn new(flags: Self::Flags) -> (Self, Task<Self::Message>); +        fn view<'a>( +            &self, +            state: &'a Self::State, +            _window: window::Id, +        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { +            self.view.view(state).into() +        } +    } -    /// Returns the current title of the [`Application`]. -    /// -    /// This title can be dynamic! The runtime will automatically update the -    /// title of your application when necessary. -    fn title(&self) -> String; +    Application { +        raw: Instance { +            update, +            view, +            _state: PhantomData, +            _message: PhantomData, +            _theme: PhantomData, +            _renderer: PhantomData, +        }, +        settings: Settings::default(), +        window: window::Settings::default(), +    } +    .title(title) +} -    /// Handles a __message__ and updates the state of the [`Application`]. +/// 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<P: Program> { +    raw: P, +    settings: Settings, +    window: window::Settings, +} + +impl<P: Program> Application<P> { +    /// Runs the [`Application`].      /// -    /// This is where you define your __update logic__. All the __messages__, -    /// produced by either user interactions or commands, will be handled by -    /// this method. +    /// The state of the [`Application`] must implement [`Default`]. +    /// If your state does not implement [`Default`], use [`run_with`] +    /// instead.      /// -    /// Any [`Task`] returned will be executed immediately in the background. -    fn update(&mut self, message: Self::Message) -> Task<Self::Message>; +    /// [`run_with`]: Self::run_with +    pub fn run(self) -> Result +    where +        Self: 'static, +        P::State: Default, +    { +        self.run_with(P::State::default) +    } -    /// Returns the widgets to display in the [`Application`]. -    /// -    /// These widgets can produce __messages__ based on user interaction. -    fn view(&self) -> Element<'_, Self::Message, Self::Theme, Self::Renderer>; +    /// Runs the [`Application`] with a closure that creates the initial state. +    pub fn run_with<I>(self, initialize: I) -> Result +    where +        Self: 'static, +        I: Fn() -> P::State + Clone + 'static, +    { +        self.raw +            .run_with(self.settings, Some(self.window), initialize) +    } -    /// Returns the current [`Theme`] of the [`Application`]. -    /// -    /// [`Theme`]: Self::Theme -    fn theme(&self) -> Self::Theme { -        Self::Theme::default() +    /// Sets the [`Settings`] that will be used to run the [`Application`]. +    pub fn settings(self, settings: Settings) -> Self { +        Self { settings, ..self }      } -    /// Returns the current [`Appearance`] of the [`Application`]. -    fn style(&self, theme: &Self::Theme) -> Appearance { -        theme.default_style() +    /// Sets the [`Settings::antialiasing`] of the [`Application`]. +    pub fn antialiasing(self, antialiasing: bool) -> Self { +        Self { +            settings: Settings { +                antialiasing, +                ..self.settings +            }, +            ..self +        }      } -    /// Returns the event [`Subscription`] for the current state of the -    /// application. -    /// -    /// A [`Subscription`] will be kept alive as long as you keep returning it, -    /// and the __messages__ produced will be handled by -    /// [`update`](#tymethod.update). -    /// -    /// By default, this method returns an empty [`Subscription`]. -    fn subscription(&self) -> Subscription<Self::Message> { -        Subscription::none() +    /// Sets the default [`Font`] of the [`Application`]. +    pub fn default_font(self, default_font: Font) -> Self { +        Self { +            settings: Settings { +                default_font, +                ..self.settings +            }, +            ..self +        }      } -    /// Returns the scale factor of the [`Application`]. -    /// -    /// It can be used to dynamically control the size of the UI at runtime -    /// (i.e. zooming). -    /// -    /// For instance, a scale factor of `2.0` will make widgets twice as big, -    /// while a scale factor of `0.5` will shrink them to half their size. -    /// -    /// By default, it returns `1.0`. -    fn scale_factor(&self) -> f64 { -        1.0 +    /// 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<Cow<'static, [u8]>>) -> Self { +        self.settings.fonts.push(font.into()); +        self      } -    /// Runs the [`Application`]. -    /// -    /// On native platforms, this method will take control of the current thread -    /// until the [`Application`] exits. -    /// -    /// On the web platform, this method __will NOT return__ unless there is an -    /// [`Error`] during startup. -    /// -    /// [`Error`]: crate::Error -    fn run(settings: Settings<Self::Flags>) -> crate::Result -    where -        Self: 'static, -    { -        #[allow(clippy::needless_update)] -        let renderer_settings = crate::graphics::Settings { -            default_font: settings.default_font, -            default_text_size: settings.default_text_size, -            antialiasing: if settings.antialiasing { -                Some(crate::graphics::Antialiasing::MSAAx4) -            } else { -                None +    /// 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              }, -            ..crate::graphics::Settings::default() -        }; - -        Ok(crate::shell::application::run::< -            Instance<Self>, -            Self::Executor, -            <Self::Renderer as compositor::Default>::Compositor, -        >(settings.into(), renderer_settings)?) +            ..self +        }      } -} -struct Instance<A>(A) -where -    A: Application, -    A::Theme: DefaultStyle; +    /// 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 +        } +    } -impl<A> crate::runtime::Program for Instance<A> -where -    A: Application, -    A::Theme: DefaultStyle, -{ -    type Message = A::Message; -    type Theme = A::Theme; -    type Renderer = A::Renderer; +    /// Sets the [`window::Settings::size`] of the [`Application`]. +    pub fn window_size(self, size: impl Into<Size>) -> Self { +        Self { +            window: window::Settings { +                size: size.into(), +                ..self.window +            }, +            ..self +        } +    } -    fn update(&mut self, message: Self::Message) -> Task<Self::Message> { -        self.0.update(message) +    /// Sets the [`window::Settings::transparent`] of the [`Application`]. +    pub fn transparent(self, transparent: bool) -> Self { +        Self { +            window: window::Settings { +                transparent, +                ..self.window +            }, +            ..self +        }      } -    fn view(&self) -> Element<'_, Self::Message, Self::Theme, Self::Renderer> { -        self.0.view() +    /// Sets the [`Title`] of the [`Application`]. +    pub(crate) fn title( +        self, +        title: impl Title<P::State>, +    ) -> Application< +        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, +    > { +        Application { +            raw: program::with_title(self.raw, move |state, _window| { +                title.title(state) +            }), +            settings: self.settings, +            window: self.window, +        }      } -} -impl<A> application::Application for Instance<A> -where -    A: Application, -    A::Theme: DefaultStyle, -{ -    type Flags = A::Flags; +    /// Runs the [`Task`] produced by the closure at startup. +    pub fn load( +        self, +        f: impl Fn() -> Task<P::Message>, +    ) -> Application< +        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, +    > { +        Application { +            raw: program::with_load(self.raw, f), +            settings: self.settings, +            window: self.window, +        } +    } -    fn new(flags: Self::Flags) -> (Self, Task<A::Message>) { -        let (app, command) = A::new(flags); +    /// Sets the subscription logic of the [`Application`]. +    pub fn subscription( +        self, +        f: impl Fn(&P::State) -> Subscription<P::Message>, +    ) -> Application< +        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, +    > { +        Application { +            raw: program::with_subscription(self.raw, f), +            settings: self.settings, +            window: self.window, +        } +    } -        (Instance(app), command) +    /// Sets the theme logic of the [`Application`]. +    pub fn theme( +        self, +        f: impl Fn(&P::State) -> P::Theme, +    ) -> Application< +        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, +    > { +        Application { +            raw: program::with_theme(self.raw, move |state, _window| f(state)), +            settings: self.settings, +            window: self.window, +        }      } -    fn title(&self) -> String { -        self.0.title() +    /// Sets the style logic of the [`Application`]. +    pub fn style( +        self, +        f: impl Fn(&P::State, &P::Theme) -> Appearance, +    ) -> Application< +        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, +    > { +        Application { +            raw: program::with_style(self.raw, f), +            settings: self.settings, +            window: self.window, +        }      } -    fn theme(&self) -> A::Theme { -        self.0.theme() +    /// Sets the scale factor of the [`Application`]. +    pub fn scale_factor( +        self, +        f: impl Fn(&P::State) -> f64, +    ) -> Application< +        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, +    > { +        Application { +            raw: program::with_scale_factor(self.raw, move |state, _window| { +                f(state) +            }), +            settings: self.settings, +            window: self.window, +        }      } +} + +/// 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 Title<State> { +    /// Produces the title of the [`Application`]. +    fn title(&self, state: &State) -> String; +} -    fn style(&self, theme: &A::Theme) -> Appearance { -        self.0.style(theme) +impl<State> Title<State> for &'static str { +    fn title(&self, _state: &State) -> String { +        self.to_string()      } +} -    fn subscription(&self) -> Subscription<Self::Message> { -        self.0.subscription() +impl<T, State> Title<State> for T +where +    T: Fn(&State) -> String, +{ +    fn title(&self, state: &State) -> String { +        self(state)      } +} -    fn scale_factor(&self) -> f64 { -        self.0.scale_factor() +/// The update logic of some [`Application`]. +/// +/// This trait allows the [`application`] builder to take any closure that +/// returns any `Into<Task<Message>>`. +pub trait Update<State, Message> { +    /// Processes the message and updates the state of the [`Application`]. +    fn update( +        &self, +        state: &mut State, +        message: Message, +    ) -> impl Into<Task<Message>>; +} + +impl<T, State, Message, C> Update<State, Message> for T +where +    T: Fn(&mut State, Message) -> C, +    C: Into<Task<Message>>, +{ +    fn update( +        &self, +        state: &mut State, +        message: Message, +    ) -> impl Into<Task<Message>> { +        self(state, message) +    } +} + +/// The view logic of some [`Application`]. +/// +/// This trait allows the [`application`] builder to take any closure that +/// returns any `Into<Element<'_, Message>>`. +pub trait View<'a, State, Message, Theme, Renderer> { +    /// Produces the widget of the [`Application`]. +    fn view( +        &self, +        state: &'a State, +    ) -> impl Into<Element<'a, Message, Theme, Renderer>>; +} + +impl<'a, T, State, Message, Theme, Renderer, Widget> +    View<'a, State, Message, Theme, Renderer> for T +where +    T: Fn(&'a State) -> Widget, +    State: 'static, +    Widget: Into<Element<'a, Message, Theme, Renderer>>, +{ +    fn view( +        &self, +        state: &'a State, +    ) -> impl Into<Element<'a, Message, Theme, Renderer>> { +        self(state)      }  } diff --git a/src/daemon.rs b/src/daemon.rs new file mode 100644 index 00000000..58293949 --- /dev/null +++ b/src/daemon.rs @@ -0,0 +1,298 @@ +//! Create and run daemons that run in the background. +use crate::application; +use crate::program::{self, Program}; +use crate::window; +use crate::{Element, Font, Result, Settings, Subscription, Task}; + +use std::borrow::Cow; + +pub use crate::shell::program::{Appearance, DefaultStyle}; + +/// Creates an iced [`Daemon`] given its title, update, and view logic. +/// +/// A [`Daemon`] will not open a window by default, but will run silently +/// instead until a [`Task`] from [`window::open`] is returned by its update logic. +/// +/// Furthermore, a [`Daemon`] will not stop running when all its windows are closed. +/// In order to completely terminate a [`Daemon`], its process must be interrupted or +/// its update logic must produce a [`Task`] from [`exit`]. +/// +/// [`exit`]: crate::exit +pub fn daemon<State, Message, Theme, Renderer>( +    title: impl Title<State>, +    update: impl application::Update<State, Message>, +    view: impl for<'a> self::View<'a, State, Message, Theme, Renderer>, +) -> Daemon<impl Program<State = State, Message = Message, Theme = Theme>> +where +    State: 'static, +    Message: Send + std::fmt::Debug + 'static, +    Theme: Default + DefaultStyle, +    Renderer: program::Renderer, +{ +    use std::marker::PhantomData; + +    struct Instance<State, Message, Theme, Renderer, Update, View> { +        update: Update, +        view: View, +        _state: PhantomData<State>, +        _message: PhantomData<Message>, +        _theme: PhantomData<Theme>, +        _renderer: PhantomData<Renderer>, +    } + +    impl<State, Message, Theme, Renderer, Update, View> Program +        for Instance<State, Message, Theme, Renderer, Update, View> +    where +        Message: Send + std::fmt::Debug + 'static, +        Theme: Default + DefaultStyle, +        Renderer: program::Renderer, +        Update: application::Update<State, Message>, +        View: for<'a> self::View<'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 load(&self) -> Task<Self::Message> { +            Task::none() +        } + +        fn update( +            &self, +            state: &mut Self::State, +            message: Self::Message, +        ) -> Task<Self::Message> { +            self.update.update(state, message).into() +        } + +        fn view<'a>( +            &self, +            state: &'a Self::State, +            window: window::Id, +        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { +            self.view.view(state, window).into() +        } +    } + +    Daemon { +        raw: Instance { +            update, +            view, +            _state: PhantomData, +            _message: PhantomData, +            _theme: PhantomData, +            _renderer: PhantomData, +        }, +        settings: Settings::default(), +    } +    .title(title) +} + +/// The underlying definition and configuration of an iced daemon. +/// +/// 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 a [`Daemon`] with the [`daemon`] helper. +#[derive(Debug)] +pub struct Daemon<P: Program> { +    raw: P, +    settings: Settings, +} + +impl<P: Program> Daemon<P> { +    /// Runs the [`Daemon`]. +    /// +    /// The state of the [`Daemon`] must implement [`Default`]. +    /// If your state does not implement [`Default`], use [`run_with`] +    /// instead. +    /// +    /// [`run_with`]: Self::run_with +    pub fn run(self) -> Result +    where +        Self: 'static, +        P::State: Default, +    { +        self.run_with(P::State::default) +    } + +    /// Runs the [`Daemon`] with a closure that creates the initial state. +    pub fn run_with<I>(self, initialize: I) -> Result +    where +        Self: 'static, +        I: Fn() -> P::State + Clone + 'static, +    { +        self.raw.run_with(self.settings, None, initialize) +    } + +    /// Sets the [`Settings`] that will be used to run the [`Daemon`]. +    pub fn settings(self, settings: Settings) -> Self { +        Self { settings, ..self } +    } + +    /// Sets the [`Settings::antialiasing`] of the [`Daemon`]. +    pub fn antialiasing(self, antialiasing: bool) -> Self { +        Self { +            settings: Settings { +                antialiasing, +                ..self.settings +            }, +            ..self +        } +    } + +    /// Sets the default [`Font`] of the [`Daemon`]. +    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 [`Daemon`]. +    pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self { +        self.settings.fonts.push(font.into()); +        self +    } + +    /// Sets the [`Title`] of the [`Daemon`]. +    pub(crate) fn title( +        self, +        title: impl Title<P::State>, +    ) -> Daemon< +        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, +    > { +        Daemon { +            raw: program::with_title(self.raw, move |state, window| { +                title.title(state, window) +            }), +            settings: self.settings, +        } +    } + +    /// Runs the [`Task`] produced by the closure at startup. +    pub fn load( +        self, +        f: impl Fn() -> Task<P::Message>, +    ) -> Daemon< +        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, +    > { +        Daemon { +            raw: program::with_load(self.raw, f), +            settings: self.settings, +        } +    } + +    /// Sets the subscription logic of the [`Daemon`]. +    pub fn subscription( +        self, +        f: impl Fn(&P::State) -> Subscription<P::Message>, +    ) -> Daemon< +        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, +    > { +        Daemon { +            raw: program::with_subscription(self.raw, f), +            settings: self.settings, +        } +    } + +    /// Sets the theme logic of the [`Daemon`]. +    pub fn theme( +        self, +        f: impl Fn(&P::State, window::Id) -> P::Theme, +    ) -> Daemon< +        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, +    > { +        Daemon { +            raw: program::with_theme(self.raw, f), +            settings: self.settings, +        } +    } + +    /// Sets the style logic of the [`Daemon`]. +    pub fn style( +        self, +        f: impl Fn(&P::State, &P::Theme) -> Appearance, +    ) -> Daemon< +        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, +    > { +        Daemon { +            raw: program::with_style(self.raw, f), +            settings: self.settings, +        } +    } + +    /// Sets the scale factor of the [`Daemon`]. +    pub fn scale_factor( +        self, +        f: impl Fn(&P::State, window::Id) -> f64, +    ) -> Daemon< +        impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, +    > { +        Daemon { +            raw: program::with_scale_factor(self.raw, f), +            settings: self.settings, +        } +    } +} + +/// The title logic of some [`Daemon`]. +/// +/// This trait is implemented both for `&static str` and +/// any closure `Fn(&State, window::Id) -> String`. +/// +/// This trait allows the [`daemon`] builder to take any of them. +pub trait Title<State> { +    /// Produces the title of the [`Daemon`]. +    fn title(&self, state: &State, window: window::Id) -> String; +} + +impl<State> Title<State> for &'static str { +    fn title(&self, _state: &State, _window: window::Id) -> String { +        self.to_string() +    } +} + +impl<T, State> Title<State> for T +where +    T: Fn(&State, window::Id) -> String, +{ +    fn title(&self, state: &State, window: window::Id) -> String { +        self(state, window) +    } +} + +/// The view logic of some [`Daemon`]. +/// +/// This trait allows the [`daemon`] builder to take any closure that +/// returns any `Into<Element<'_, Message>>`. +pub trait View<'a, State, Message, Theme, Renderer> { +    /// Produces the widget of the [`Daemon`]. +    fn view( +        &self, +        state: &'a State, +        window: window::Id, +    ) -> impl Into<Element<'a, Message, Theme, Renderer>>; +} + +impl<'a, T, State, Message, Theme, Renderer, Widget> +    View<'a, State, Message, Theme, Renderer> for T +where +    T: Fn(&'a State, window::Id) -> Widget, +    State: 'static, +    Widget: Into<Element<'a, Message, Theme, Renderer>>, +{ +    fn view( +        &self, +        state: &'a State, +        window: window::Id, +    ) -> impl Into<Element<'a, Message, Theme, Renderer>> { +        self(state, window) +    } +} @@ -158,11 +158,11 @@  //!   1. Draw the resulting user interface.  //!  //! # Usage -//! Use [`run`] or the [`program`] builder. +//! Use [`run`] or the [`application`] builder.  //!  //! [Elm]: https://elm-lang.org/  //! [The Elm Architecture]: https://guide.elm-lang.org/architecture/ -//! [`program`]: program() +//! [`application`]: application()  #![doc(      html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"  )] @@ -179,10 +179,11 @@ pub use iced_futures::futures;  #[cfg(feature = "highlighter")]  pub use iced_highlighter as highlighter; -mod application;  mod error; +mod program; -pub mod program; +pub mod application; +pub mod daemon;  pub mod settings;  pub mod time;  pub mod window; @@ -190,9 +191,6 @@ pub mod window;  #[cfg(feature = "advanced")]  pub mod advanced; -#[cfg(feature = "multi-window")] -pub mod multi_window; -  pub use crate::core::alignment;  pub use crate::core::border;  pub use crate::core::color; @@ -203,7 +201,7 @@ pub use crate::core::{      Length, Padding, Pixels, Point, Radians, Rectangle, Rotation, Shadow, Size,      Theme, Transformation, Vector,  }; -pub use crate::runtime::Task; +pub use crate::runtime::{exit, Task};  pub mod clipboard {      //! Access the clipboard. @@ -308,11 +306,12 @@ pub mod widget {      mod runtime {}  } +pub use application::{application, Application}; +pub use daemon::{daemon, Daemon};  pub use error::Error;  pub use event::Event;  pub use executor::Executor;  pub use font::Font; -pub use program::Program;  pub use renderer::Renderer;  pub use settings::Settings;  pub use subscription::Subscription; @@ -327,13 +326,13 @@ pub type Element<      Renderer = crate::Renderer,  > = crate::core::Element<'a, Message, Theme, Renderer>; -/// The result of running a [`Program`]. +/// The result of running an iced program.  pub type Result = std::result::Result<(), Error>;  /// Runs a basic iced application with default [`Settings`] given its title,  /// update, and view logic.  /// -/// This is equivalent to chaining [`program`] with [`Program::run`]. +/// This is equivalent to chaining [`application()`] with [`Application::run`].  ///  /// [`program`]: program()  /// @@ -364,9 +363,10 @@ pub type Result = std::result::Result<(), Error>;  /// }  /// ```  pub fn run<State, Message, Theme, Renderer>( -    title: impl program::Title<State> + 'static, -    update: impl program::Update<State, Message> + 'static, -    view: impl for<'a> program::View<'a, State, Message, Theme, Renderer> + 'static, +    title: impl application::Title<State> + 'static, +    update: impl application::Update<State, Message> + 'static, +    view: impl for<'a> application::View<'a, State, Message, Theme, Renderer> +        + 'static,  ) -> Result  where      State: Default + 'static, @@ -374,8 +374,5 @@ where      Theme: Default + program::DefaultStyle + 'static,      Renderer: program::Renderer + 'static,  { -    program(title, update, view).run() +    application(title, update, view).run()  } - -#[doc(inline)] -pub use program::program; diff --git a/src/multi_window.rs b/src/multi_window.rs deleted file mode 100644 index 4900bb85..00000000 --- a/src/multi_window.rs +++ /dev/null @@ -1,254 +0,0 @@ -//! Leverage multi-window support in your application. -use crate::window; -use crate::{Element, Executor, Settings, Subscription, Task}; - -pub use crate::application::{Appearance, DefaultStyle}; - -/// An interactive cross-platform multi-window application. -/// -/// This trait is the main entrypoint of Iced. Once implemented, you can run -/// your GUI application by simply calling [`run`](#method.run). -/// -/// - On native platforms, it will run in its own windows. -/// - On the web, it will take control of the `<title>` and the `<body>` of the -///   document and display only the contents of the `window::Id::MAIN` window. -/// -/// An [`Application`] can execute asynchronous actions by returning a -/// [`Task`] in some of its methods. -/// -/// When using an [`Application`] with the `debug` feature enabled, a debug view -/// can be toggled by pressing `F12`. -/// -/// # Examples -/// See the `examples/multi-window` example to see this multi-window `Application` trait in action. -/// -/// ## A simple "Hello, world!" -/// -/// If you just want to get started, here is a simple [`Application`] that -/// says "Hello, world!": -/// -/// ```no_run -/// use iced::{executor, window}; -/// use iced::{Task, Element, Settings, Theme}; -/// use iced::multi_window::{self, Application}; -/// -/// pub fn main() -> iced::Result { -///     Hello::run(Settings::default()) -/// } -/// -/// struct Hello; -/// -/// impl multi_window::Application for Hello { -///     type Executor = executor::Default; -///     type Flags = (); -///     type Message = (); -///     type Theme = Theme; -/// -///     fn new(_flags: ()) -> (Hello, Task<Self::Message>) { -///         (Hello, Task::none()) -///     } -/// -///     fn title(&self, _window: window::Id) -> String { -///         String::from("A cool application") -///     } -/// -///     fn update(&mut self, _message: Self::Message) -> Task<Self::Message> { -///         Task::none() -///     } -/// -///     fn view(&self, _window: window::Id) -> Element<Self::Message> { -///         "Hello, world!".into() -///     } -/// } -/// ``` -/// -/// [`Sandbox`]: crate::Sandbox -pub trait Application: Sized -where -    Self::Theme: DefaultStyle, -{ -    /// The [`Executor`] that will run commands and subscriptions. -    /// -    /// The [default executor] can be a good starting point! -    /// -    /// [`Executor`]: Self::Executor -    /// [default executor]: crate::executor::Default -    type Executor: Executor; - -    /// The type of __messages__ your [`Application`] will produce. -    type Message: std::fmt::Debug + Send; - -    /// The theme of your [`Application`]. -    type Theme: Default; - -    /// The data needed to initialize your [`Application`]. -    type Flags; - -    /// Initializes the [`Application`] with the flags provided to -    /// [`run`] as part of the [`Settings`]. -    /// -    /// Here is where you should return the initial state of your app. -    /// -    /// Additionally, you can return a [`Task`] if you need to perform some -    /// async action in the background on startup. This is useful if you want to -    /// load state from a file, perform an initial HTTP request, etc. -    /// -    /// [`run`]: Self::run -    fn new(flags: Self::Flags) -> (Self, Task<Self::Message>); - -    /// Returns the current title of the `window` of the [`Application`]. -    /// -    /// This title can be dynamic! The runtime will automatically update the -    /// title of your window when necessary. -    fn title(&self, window: window::Id) -> String; - -    /// Handles a __message__ and updates the state of the [`Application`]. -    /// -    /// This is where you define your __update logic__. All the __messages__, -    /// produced by either user interactions or commands, will be handled by -    /// this method. -    /// -    /// Any [`Task`] returned will be executed immediately in the background. -    fn update(&mut self, message: Self::Message) -> Task<Self::Message>; - -    /// Returns the widgets to display in the `window` of the [`Application`]. -    /// -    /// These widgets can produce __messages__ based on user interaction. -    fn view( -        &self, -        window: window::Id, -    ) -> Element<'_, Self::Message, Self::Theme, crate::Renderer>; - -    /// Returns the current [`Theme`] of the `window` of the [`Application`]. -    /// -    /// [`Theme`]: Self::Theme -    #[allow(unused_variables)] -    fn theme(&self, window: window::Id) -> Self::Theme { -        Self::Theme::default() -    } - -    /// Returns the current `Style` of the [`Theme`]. -    /// -    /// [`Theme`]: Self::Theme -    fn style(&self, theme: &Self::Theme) -> Appearance { -        Self::Theme::default_style(theme) -    } - -    /// Returns the event [`Subscription`] for the current state of the -    /// application. -    /// -    /// A [`Subscription`] will be kept alive as long as you keep returning it, -    /// and the __messages__ produced will be handled by -    /// [`update`](#tymethod.update). -    /// -    /// By default, this method returns an empty [`Subscription`]. -    fn subscription(&self) -> Subscription<Self::Message> { -        Subscription::none() -    } - -    /// Returns the scale factor of the `window` of the [`Application`]. -    /// -    /// It can be used to dynamically control the size of the UI at runtime -    /// (i.e. zooming). -    /// -    /// For instance, a scale factor of `2.0` will make widgets twice as big, -    /// while a scale factor of `0.5` will shrink them to half their size. -    /// -    /// By default, it returns `1.0`. -    #[allow(unused_variables)] -    fn scale_factor(&self, window: window::Id) -> f64 { -        1.0 -    } - -    /// Runs the multi-window [`Application`]. -    /// -    /// On native platforms, this method will take control of the current thread -    /// until the [`Application`] exits. -    /// -    /// On the web platform, this method __will NOT return__ unless there is an -    /// [`Error`] during startup. -    /// -    /// [`Error`]: crate::Error -    fn run(settings: Settings<Self::Flags>) -> crate::Result -    where -        Self: 'static, -    { -        #[allow(clippy::needless_update)] -        let renderer_settings = crate::graphics::Settings { -            default_font: settings.default_font, -            default_text_size: settings.default_text_size, -            antialiasing: if settings.antialiasing { -                Some(crate::graphics::Antialiasing::MSAAx4) -            } else { -                None -            }, -            ..crate::graphics::Settings::default() -        }; - -        Ok(crate::shell::multi_window::run::< -            Instance<Self>, -            Self::Executor, -            crate::renderer::Compositor, -        >(settings.into(), renderer_settings)?) -    } -} - -struct Instance<A>(A) -where -    A: Application, -    A::Theme: DefaultStyle; - -impl<A> crate::runtime::multi_window::Program for Instance<A> -where -    A: Application, -    A::Theme: DefaultStyle, -{ -    type Message = A::Message; -    type Theme = A::Theme; -    type Renderer = crate::Renderer; - -    fn update(&mut self, message: Self::Message) -> Task<Self::Message> { -        self.0.update(message) -    } - -    fn view( -        &self, -        window: window::Id, -    ) -> Element<'_, Self::Message, Self::Theme, Self::Renderer> { -        self.0.view(window) -    } -} - -impl<A> crate::shell::multi_window::Application for Instance<A> -where -    A: Application, -    A::Theme: DefaultStyle, -{ -    type Flags = A::Flags; - -    fn new(flags: Self::Flags) -> (Self, Task<A::Message>) { -        let (app, command) = A::new(flags); - -        (Instance(app), command) -    } - -    fn title(&self, window: window::Id) -> String { -        self.0.title(window) -    } - -    fn theme(&self, window: window::Id) -> A::Theme { -        self.0.theme(window) -    } - -    fn style(&self, theme: &Self::Theme) -> Appearance { -        self.0.style(theme) -    } - -    fn subscription(&self) -> Subscription<Self::Message> { -        self.0.subscription() -    } - -    fn scale_factor(&self, window: window::Id) -> f64 { -        self.0.scale_factor(window) -    } -} diff --git a/src/program.rs b/src/program.rs index ea6b0e8e..3f9d2d0c 100644 --- a/src/program.rs +++ b/src/program.rs @@ -1,194 +1,108 @@ -//! Create and run iced applications step by step. -//! -//! # Example -//! ```no_run -//! use iced::widget::{button, column, text, Column}; -//! use iced::Theme; -//! -//! pub fn main() -> iced::Result { -//!     iced::program("A counter", 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<Message> { -//!     column![ -//!         text(value), -//!         button("+").on_press(Message::Increment), -//!     ] -//! } -//! ``` -use crate::application::Application;  use crate::core::text; -use crate::executor::{self, Executor};  use crate::graphics::compositor; +use crate::shell;  use crate::window; -use crate::{Element, Font, Result, Settings, Size, Subscription, Task}; +use crate::{Element, Executor, Result, Settings, Subscription, Task}; -pub use crate::application::{Appearance, DefaultStyle}; +pub use crate::shell::program::{Appearance, DefaultStyle}; -use std::borrow::Cow; - -/// Creates an iced [`Program`] given its title, update, and view logic. -/// -/// # Example -/// ```no_run -/// use iced::widget::{button, column, text, Column}; -/// -/// pub fn main() -> iced::Result { -///     iced::program("A counter", update, view).run() -/// } -/// -/// #[derive(Debug, Clone)] -/// enum Message { -///     Increment, -/// } -/// -/// fn update(value: &mut u64, message: Message) { -///     match message { -///         Message::Increment => *value += 1, -///     } -/// } +/// The internal definition of a [`Program`].  /// -/// fn view(value: &u64) -> Column<Message> { -///     column![ -///         text(value), -///         button("+").on_press(Message::Increment), -///     ] -/// } -/// ``` -pub fn program<State, Message, Theme, Renderer>( -    title: impl Title<State>, -    update: impl Update<State, Message>, -    view: impl for<'a> self::View<'a, State, Message, Theme, Renderer>, -) -> Program<impl Definition<State = State, Message = Message, Theme = Theme>> -where -    State: 'static, -    Message: Send + std::fmt::Debug + 'static, -    Theme: Default + DefaultStyle, -    Renderer: self::Renderer, -{ -    use std::marker::PhantomData; - -    struct Application<State, Message, Theme, Renderer, Update, View> { -        update: Update, -        view: View, -        _state: PhantomData<State>, -        _message: PhantomData<Message>, -        _theme: PhantomData<Theme>, -        _renderer: PhantomData<Renderer>, -    } +/// You should not need to implement this trait directly. Instead, use the +/// methods available in the [`Program`] struct. +#[allow(missing_docs)] +pub trait Program: Sized { +    /// The state of the program. +    type State; -    impl<State, Message, Theme, Renderer, Update, View> Definition -        for Application<State, Message, Theme, Renderer, Update, View> -    where -        Message: Send + std::fmt::Debug + 'static, -        Theme: Default + DefaultStyle, -        Renderer: self::Renderer, -        Update: self::Update<State, Message>, -        View: for<'a> self::View<'a, State, Message, Theme, Renderer>, -    { -        type State = State; -        type Message = Message; -        type Theme = Theme; -        type Renderer = Renderer; -        type Executor = executor::Default; +    /// The message of the program. +    type Message: Send + std::fmt::Debug + 'static; -        fn load(&self) -> Task<Self::Message> { -            Task::none() -        } +    /// The theme of the program. +    type Theme: Default + DefaultStyle; -        fn update( -            &self, -            state: &mut Self::State, -            message: Self::Message, -        ) -> Task<Self::Message> { -            self.update.update(state, message).into() -        } +    /// The renderer of the program. +    type Renderer: Renderer; -        fn view<'a>( -            &self, -            state: &'a Self::State, -        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { -            self.view.view(state).into() -        } +    /// The executor of the program. +    type Executor: Executor; + +    fn load(&self) -> Task<Self::Message>; + +    fn update( +        &self, +        state: &mut Self::State, +        message: Self::Message, +    ) -> Task<Self::Message>; + +    fn view<'a>( +        &self, +        state: &'a Self::State, +        window: window::Id, +    ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer>; + +    fn title(&self, _state: &Self::State, _window: window::Id) -> String { +        String::from("A cool iced application!")      } -    Program { -        raw: Application { -            update, -            view, -            _state: PhantomData, -            _message: PhantomData, -            _theme: PhantomData, -            _renderer: PhantomData, -        }, -        settings: Settings::default(), +    fn subscription( +        &self, +        _state: &Self::State, +    ) -> Subscription<Self::Message> { +        Subscription::none()      } -    .title(title) -} -/// 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 a [`Program`] with the [`program`] helper. -/// -/// [`run`]: Program::run -#[derive(Debug)] -pub struct Program<P: Definition> { -    raw: P, -    settings: Settings, -} +    fn theme(&self, _state: &Self::State, _window: window::Id) -> Self::Theme { +        Self::Theme::default() +    } + +    fn style(&self, _state: &Self::State, theme: &Self::Theme) -> Appearance { +        DefaultStyle::default_style(theme) +    } -impl<P: Definition> Program<P> { -    /// Runs the underlying [`Application`] of the [`Program`]. +    fn scale_factor(&self, _state: &Self::State, _window: window::Id) -> f64 { +        1.0 +    } + +    /// Runs the [`Program`].      ///      /// The state of the [`Program`] must implement [`Default`].      /// If your state does not implement [`Default`], use [`run_with`]      /// instead.      ///      /// [`run_with`]: Self::run_with -    pub fn run(self) -> Result +    fn run( +        self, +        settings: Settings, +        window_settings: Option<window::Settings>, +    ) -> Result      where          Self: 'static, -        P::State: Default, +        Self::State: Default,      { -        self.run_with(P::State::default) +        self.run_with(settings, window_settings, Self::State::default)      } -    /// Runs the underlying [`Application`] of the [`Program`] with a -    /// closure that creates the initial state. -    pub fn run_with( +    /// Runs the [`Program`] with the given [`Settings`] and a closure that creates the initial state. +    fn run_with<I>(          self, -        initialize: impl Fn() -> P::State + Clone + 'static, +        settings: Settings, +        window_settings: Option<window::Settings>, +        initialize: I,      ) -> Result      where          Self: 'static, +        I: Fn() -> Self::State + Clone + 'static,      {          use std::marker::PhantomData; -        struct Instance<P: Definition, I> { +        struct Instance<P: Program, I> {              program: P,              state: P::State,              _initialize: PhantomData<I>,          } -        impl<P: Definition, I: Fn() -> P::State> Application for Instance<P, I> { +        impl<P: Program, I: Fn() -> P::State> shell::Program for Instance<P, I> {              type Message = P::Message;              type Theme = P::Theme;              type Renderer = P::Renderer; @@ -211,8 +125,8 @@ impl<P: Definition> Program<P> {                  )              } -            fn title(&self) -> String { -                self.program.title(&self.state) +            fn title(&self, window: window::Id) -> String { +                self.program.title(&self.state, window)              }              fn update( @@ -224,259 +138,73 @@ impl<P: Definition> Program<P> {              fn view(                  &self, +                window: window::Id,              ) -> crate::Element<'_, Self::Message, Self::Theme, Self::Renderer>              { -                self.program.view(&self.state) +                self.program.view(&self.state, window)              }              fn subscription(&self) -> Subscription<Self::Message> {                  self.program.subscription(&self.state)              } -            fn theme(&self) -> Self::Theme { -                self.program.theme(&self.state) +            fn theme(&self, window: window::Id) -> Self::Theme { +                self.program.theme(&self.state, window)              }              fn style(&self, theme: &Self::Theme) -> Appearance {                  self.program.style(&self.state, theme)              } -        } -        let Self { raw, settings } = self; +            fn scale_factor(&self, window: window::Id) -> f64 { +                self.program.scale_factor(&self.state, window) +            } +        } -        Instance::run(Settings { -            flags: (raw, initialize), -            id: settings.id, -            window: settings.window, -            fonts: settings.fonts, +        #[allow(clippy::needless_update)] +        let renderer_settings = crate::graphics::Settings {              default_font: settings.default_font,              default_text_size: settings.default_text_size, -            antialiasing: settings.antialiasing, -        }) -    } - -    /// Sets the [`Settings`] that will be used to run the [`Program`]. -    pub fn settings(self, settings: Settings) -> Self { -        Self { settings, ..self } -    } - -    /// Sets the [`Settings::antialiasing`] of the [`Program`]. -    pub fn antialiasing(self, antialiasing: bool) -> Self { -        Self { -            settings: Settings { -                antialiasing, -                ..self.settings -            }, -            ..self -        } -    } - -    /// Sets the default [`Font`] of the [`Program`]. -    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 [`Program`]. -    pub fn font(mut self, font: impl Into<Cow<'static, [u8]>>) -> Self { -        self.settings.fonts.push(font.into()); -        self -    } - -    /// Sets the [`window::Settings::position`] to [`window::Position::Centered`] in the [`Program`]. -    pub fn centered(self) -> Self { -        Self { -            settings: Settings { -                window: window::Settings { -                    position: window::Position::Centered, -                    ..self.settings.window -                }, -                ..self.settings -            }, -            ..self -        } -    } - -    /// Sets the [`window::Settings::exit_on_close_request`] of the [`Program`]. -    pub fn exit_on_close_request(self, exit_on_close_request: bool) -> Self { -        Self { -            settings: Settings { -                window: window::Settings { -                    exit_on_close_request, -                    ..self.settings.window -                }, -                ..self.settings -            }, -            ..self -        } -    } - -    /// Sets the [`window::Settings::size`] of the [`Program`]. -    pub fn window_size(self, size: impl Into<Size>) -> Self { -        Self { -            settings: Settings { -                window: window::Settings { -                    size: size.into(), -                    ..self.settings.window -                }, -                ..self.settings -            }, -            ..self -        } -    } - -    /// Sets the [`window::Settings::transparent`] of the [`Program`]. -    pub fn transparent(self, transparent: bool) -> Self { -        Self { -            settings: Settings { -                window: window::Settings { -                    transparent, -                    ..self.settings.window -                }, -                ..self.settings +            antialiasing: if settings.antialiasing { +                Some(crate::graphics::Antialiasing::MSAAx4) +            } else { +                None              }, -            ..self -        } -    } - -    /// Sets the [`Title`] of the [`Program`]. -    pub(crate) fn title( -        self, -        title: impl Title<P::State>, -    ) -> Program< -        impl Definition<State = P::State, Message = P::Message, Theme = P::Theme>, -    > { -        Program { -            raw: with_title(self.raw, title), -            settings: self.settings, -        } -    } - -    /// Runs the [`Task`] produced by the closure at startup. -    pub fn load( -        self, -        f: impl Fn() -> Task<P::Message>, -    ) -> Program< -        impl Definition<State = P::State, Message = P::Message, Theme = P::Theme>, -    > { -        Program { -            raw: with_load(self.raw, f), -            settings: self.settings, -        } -    } - -    /// Sets the subscription logic of the [`Program`]. -    pub fn subscription( -        self, -        f: impl Fn(&P::State) -> Subscription<P::Message>, -    ) -> Program< -        impl Definition<State = P::State, Message = P::Message, Theme = P::Theme>, -    > { -        Program { -            raw: with_subscription(self.raw, f), -            settings: self.settings, -        } -    } - -    /// Sets the theme logic of the [`Program`]. -    pub fn theme( -        self, -        f: impl Fn(&P::State) -> P::Theme, -    ) -> Program< -        impl Definition<State = P::State, Message = P::Message, Theme = P::Theme>, -    > { -        Program { -            raw: with_theme(self.raw, f), -            settings: self.settings, -        } -    } - -    /// Sets the style logic of the [`Program`]. -    pub fn style( -        self, -        f: impl Fn(&P::State, &P::Theme) -> Appearance, -    ) -> Program< -        impl Definition<State = P::State, Message = P::Message, Theme = P::Theme>, -    > { -        Program { -            raw: with_style(self.raw, f), -            settings: self.settings, -        } -    } -} - -/// The internal definition of a [`Program`]. -/// -/// You should not need to implement this trait directly. Instead, use the -/// methods available in the [`Program`] struct. -#[allow(missing_docs)] -pub trait Definition: Sized { -    /// The state of the program. -    type State; - -    /// The message of the program. -    type Message: Send + std::fmt::Debug + 'static; - -    /// The theme of the program. -    type Theme: Default + DefaultStyle; - -    /// The renderer of the program. -    type Renderer: Renderer; - -    /// The executor of the program. -    type Executor: Executor; - -    fn load(&self) -> Task<Self::Message>; - -    fn update( -        &self, -        state: &mut Self::State, -        message: Self::Message, -    ) -> Task<Self::Message>; - -    fn view<'a>( -        &self, -        state: &'a Self::State, -    ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer>; - -    fn title(&self, _state: &Self::State) -> String { -        String::from("A cool iced application!") -    } - -    fn subscription( -        &self, -        _state: &Self::State, -    ) -> Subscription<Self::Message> { -        Subscription::none() -    } - -    fn theme(&self, _state: &Self::State) -> Self::Theme { -        Self::Theme::default() -    } - -    fn style(&self, _state: &Self::State, theme: &Self::Theme) -> Appearance { -        DefaultStyle::default_style(theme) +            ..crate::graphics::Settings::default() +        }; + +        Ok(shell::program::run::< +            Instance<Self, I>, +            <Self::Renderer as compositor::Default>::Compositor, +        >( +            Settings { +                id: settings.id, +                fonts: settings.fonts, +                default_font: settings.default_font, +                default_text_size: settings.default_text_size, +                antialiasing: settings.antialiasing, +            } +            .into(), +            renderer_settings, +            window_settings, +            (self, initialize), +        )?)      }  } -fn with_title<P: Definition>( +pub fn with_title<P: Program>(      program: P, -    title: impl Title<P::State>, -) -> impl Definition<State = P::State, Message = P::Message, Theme = P::Theme> { +    title: impl Fn(&P::State, window::Id) -> String, +) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {      struct WithTitle<P, Title> {          program: P,          title: Title,      } -    impl<P, Title> Definition for WithTitle<P, Title> +    impl<P, Title> Program for WithTitle<P, Title>      where -        P: Definition, -        Title: self::Title<P::State>, +        P: Program, +        Title: Fn(&P::State, window::Id) -> String,      {          type State = P::State;          type Message = P::Message; @@ -488,8 +216,8 @@ fn with_title<P: Definition>(              self.program.load()          } -        fn title(&self, state: &Self::State) -> String { -            self.title.title(state) +        fn title(&self, state: &Self::State, window: window::Id) -> String { +            (self.title)(state, window)          }          fn update( @@ -503,12 +231,17 @@ fn with_title<P: Definition>(          fn view<'a>(              &self,              state: &'a Self::State, +            window: window::Id,          ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { -            self.program.view(state) +            self.program.view(state, window)          } -        fn theme(&self, state: &Self::State) -> Self::Theme { -            self.program.theme(state) +        fn theme( +            &self, +            state: &Self::State, +            window: window::Id, +        ) -> Self::Theme { +            self.program.theme(state, window)          }          fn subscription( @@ -525,21 +258,25 @@ fn with_title<P: Definition>(          ) -> Appearance {              self.program.style(state, theme)          } + +        fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { +            self.program.scale_factor(state, window) +        }      }      WithTitle { program, title }  } -fn with_load<P: Definition>( +pub fn with_load<P: Program>(      program: P,      f: impl Fn() -> Task<P::Message>, -) -> impl Definition<State = P::State, Message = P::Message, Theme = P::Theme> { +) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {      struct WithLoad<P, F> {          program: P,          load: F,      } -    impl<P: Definition, F> Definition for WithLoad<P, F> +    impl<P: Program, F> Program for WithLoad<P, F>      where          F: Fn() -> Task<P::Message>,      { @@ -547,7 +284,7 @@ fn with_load<P: Definition>(          type Message = P::Message;          type Theme = P::Theme;          type Renderer = P::Renderer; -        type Executor = executor::Default; +        type Executor = P::Executor;          fn load(&self) -> Task<Self::Message> {              Task::batch([self.program.load(), (self.load)()]) @@ -564,12 +301,13 @@ fn with_load<P: Definition>(          fn view<'a>(              &self,              state: &'a Self::State, +            window: window::Id,          ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { -            self.program.view(state) +            self.program.view(state, window)          } -        fn title(&self, state: &Self::State) -> String { -            self.program.title(state) +        fn title(&self, state: &Self::State, window: window::Id) -> String { +            self.program.title(state, window)          }          fn subscription( @@ -579,8 +317,12 @@ fn with_load<P: Definition>(              self.program.subscription(state)          } -        fn theme(&self, state: &Self::State) -> Self::Theme { -            self.program.theme(state) +        fn theme( +            &self, +            state: &Self::State, +            window: window::Id, +        ) -> Self::Theme { +            self.program.theme(state, window)          }          fn style( @@ -590,21 +332,25 @@ fn with_load<P: Definition>(          ) -> Appearance {              self.program.style(state, theme)          } + +        fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { +            self.program.scale_factor(state, window) +        }      }      WithLoad { program, load: f }  } -fn with_subscription<P: Definition>( +pub fn with_subscription<P: Program>(      program: P,      f: impl Fn(&P::State) -> Subscription<P::Message>, -) -> impl Definition<State = P::State, Message = P::Message, Theme = P::Theme> { +) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {      struct WithSubscription<P, F> {          program: P,          subscription: F,      } -    impl<P: Definition, F> Definition for WithSubscription<P, F> +    impl<P: Program, F> Program for WithSubscription<P, F>      where          F: Fn(&P::State) -> Subscription<P::Message>,      { @@ -612,7 +358,7 @@ fn with_subscription<P: Definition>(          type Message = P::Message;          type Theme = P::Theme;          type Renderer = P::Renderer; -        type Executor = executor::Default; +        type Executor = P::Executor;          fn subscription(              &self, @@ -636,16 +382,21 @@ fn with_subscription<P: Definition>(          fn view<'a>(              &self,              state: &'a Self::State, +            window: window::Id,          ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { -            self.program.view(state) +            self.program.view(state, window)          } -        fn title(&self, state: &Self::State) -> String { -            self.program.title(state) +        fn title(&self, state: &Self::State, window: window::Id) -> String { +            self.program.title(state, window)          } -        fn theme(&self, state: &Self::State) -> Self::Theme { -            self.program.theme(state) +        fn theme( +            &self, +            state: &Self::State, +            window: window::Id, +        ) -> Self::Theme { +            self.program.theme(state, window)          }          fn style( @@ -655,6 +406,10 @@ fn with_subscription<P: Definition>(          ) -> Appearance {              self.program.style(state, theme)          } + +        fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { +            self.program.scale_factor(state, window) +        }      }      WithSubscription { @@ -663,18 +418,18 @@ fn with_subscription<P: Definition>(      }  } -fn with_theme<P: Definition>( +pub fn with_theme<P: Program>(      program: P, -    f: impl Fn(&P::State) -> P::Theme, -) -> impl Definition<State = P::State, Message = P::Message, Theme = P::Theme> { +    f: impl Fn(&P::State, window::Id) -> P::Theme, +) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {      struct WithTheme<P, F> {          program: P,          theme: F,      } -    impl<P: Definition, F> Definition for WithTheme<P, F> +    impl<P: Program, F> Program for WithTheme<P, F>      where -        F: Fn(&P::State) -> P::Theme, +        F: Fn(&P::State, window::Id) -> P::Theme,      {          type State = P::State;          type Message = P::Message; @@ -682,16 +437,20 @@ fn with_theme<P: Definition>(          type Renderer = P::Renderer;          type Executor = P::Executor; -        fn theme(&self, state: &Self::State) -> Self::Theme { -            (self.theme)(state) +        fn theme( +            &self, +            state: &Self::State, +            window: window::Id, +        ) -> Self::Theme { +            (self.theme)(state, window)          }          fn load(&self) -> Task<Self::Message> {              self.program.load()          } -        fn title(&self, state: &Self::State) -> String { -            self.program.title(state) +        fn title(&self, state: &Self::State, window: window::Id) -> String { +            self.program.title(state, window)          }          fn update( @@ -705,8 +464,9 @@ fn with_theme<P: Definition>(          fn view<'a>(              &self,              state: &'a Self::State, +            window: window::Id,          ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { -            self.program.view(state) +            self.program.view(state, window)          }          fn subscription( @@ -723,21 +483,25 @@ fn with_theme<P: Definition>(          ) -> Appearance {              self.program.style(state, theme)          } + +        fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { +            self.program.scale_factor(state, window) +        }      }      WithTheme { program, theme: f }  } -fn with_style<P: Definition>( +pub fn with_style<P: Program>(      program: P,      f: impl Fn(&P::State, &P::Theme) -> Appearance, -) -> impl Definition<State = P::State, Message = P::Message, Theme = P::Theme> { +) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> {      struct WithStyle<P, F> {          program: P,          style: F,      } -    impl<P: Definition, F> Definition for WithStyle<P, F> +    impl<P: Program, F> Program for WithStyle<P, F>      where          F: Fn(&P::State, &P::Theme) -> Appearance,      { @@ -759,8 +523,8 @@ fn with_style<P: Definition>(              self.program.load()          } -        fn title(&self, state: &Self::State) -> String { -            self.program.title(state) +        fn title(&self, state: &Self::State, window: window::Id) -> String { +            self.program.title(state, window)          }          fn update( @@ -774,8 +538,9 @@ fn with_style<P: Definition>(          fn view<'a>(              &self,              state: &'a Self::State, +            window: window::Id,          ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { -            self.program.view(state) +            self.program.view(state, window)          }          fn subscription( @@ -785,91 +550,96 @@ fn with_style<P: Definition>(              self.program.subscription(state)          } -        fn theme(&self, state: &Self::State) -> Self::Theme { -            self.program.theme(state) +        fn theme( +            &self, +            state: &Self::State, +            window: window::Id, +        ) -> Self::Theme { +            self.program.theme(state, window) +        } + +        fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { +            self.program.scale_factor(state, window)          }      }      WithStyle { program, style: f }  } -/// The title logic of some [`Program`]. -/// -/// This trait is implemented both for `&static str` and -/// any closure `Fn(&State) -> String`. -/// -/// This trait allows the [`program`] builder to take any of them. -pub trait Title<State> { -    /// Produces the title of the [`Program`]. -    fn title(&self, state: &State) -> String; -} - -impl<State> Title<State> for &'static str { -    fn title(&self, _state: &State) -> String { -        self.to_string() +pub fn with_scale_factor<P: Program>( +    program: P, +    f: impl Fn(&P::State, window::Id) -> f64, +) -> impl Program<State = P::State, Message = P::Message, Theme = P::Theme> { +    struct WithScaleFactor<P, F> { +        program: P, +        scale_factor: F,      } -} -impl<T, State> Title<State> for T -where -    T: Fn(&State) -> String, -{ -    fn title(&self, state: &State) -> String { -        self(state) -    } -} +    impl<P: Program, F> Program for WithScaleFactor<P, F> +    where +        F: Fn(&P::State, window::Id) -> f64, +    { +        type State = P::State; +        type Message = P::Message; +        type Theme = P::Theme; +        type Renderer = P::Renderer; +        type Executor = P::Executor; -/// The update logic of some [`Program`]. -/// -/// This trait allows the [`program`] builder to take any closure that -/// returns any `Into<Task<Message>>`. -pub trait Update<State, Message> { -    /// Processes the message and updates the state of the [`Program`]. -    fn update( -        &self, -        state: &mut State, -        message: Message, -    ) -> impl Into<Task<Message>>; -} +        fn load(&self) -> Task<Self::Message> { +            self.program.load() +        } -impl<T, State, Message, C> Update<State, Message> for T -where -    T: Fn(&mut State, Message) -> C, -    C: Into<Task<Message>>, -{ -    fn update( -        &self, -        state: &mut State, -        message: Message, -    ) -> impl Into<Task<Message>> { -        self(state, message) -    } -} +        fn title(&self, state: &Self::State, window: window::Id) -> String { +            self.program.title(state, window) +        } -/// The view logic of some [`Program`]. -/// -/// This trait allows the [`program`] builder to take any closure that -/// returns any `Into<Element<'_, Message>>`. -pub trait View<'a, State, Message, Theme, Renderer> { -    /// Produces the widget of the [`Program`]. -    fn view( -        &self, -        state: &'a State, -    ) -> impl Into<Element<'a, Message, Theme, Renderer>>; -} +        fn update( +            &self, +            state: &mut Self::State, +            message: Self::Message, +        ) -> Task<Self::Message> { +            self.program.update(state, message) +        } -impl<'a, T, State, Message, Theme, Renderer, Widget> -    View<'a, State, Message, Theme, Renderer> for T -where -    T: Fn(&'a State) -> Widget, -    State: 'static, -    Widget: Into<Element<'a, Message, Theme, Renderer>>, -{ -    fn view( -        &self, -        state: &'a State, -    ) -> impl Into<Element<'a, Message, Theme, Renderer>> { -        self(state) +        fn view<'a>( +            &self, +            state: &'a Self::State, +            window: window::Id, +        ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { +            self.program.view(state, window) +        } + +        fn subscription( +            &self, +            state: &Self::State, +        ) -> Subscription<Self::Message> { +            self.program.subscription(state) +        } + +        fn theme( +            &self, +            state: &Self::State, +            window: window::Id, +        ) -> Self::Theme { +            self.program.theme(state, window) +        } + +        fn style( +            &self, +            state: &Self::State, +            theme: &Self::Theme, +        ) -> Appearance { +            self.program.style(state, theme) +        } + +        fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { +            (self.scale_factor)(state, window) +        } +    } + +    WithScaleFactor { +        program, +        scale_factor: f,      }  } diff --git a/src/settings.rs b/src/settings.rs index f7947841..ebac7a86 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,30 +1,17 @@  //! Configure your application. -use crate::window;  use crate::{Font, Pixels};  use std::borrow::Cow; -/// The settings of an iced [`Program`]. -/// -/// [`Program`]: crate::Program +/// The settings of an iced program.  #[derive(Debug, Clone)] -pub struct Settings<Flags = ()> { +pub struct Settings {      /// The identifier of the application.      ///      /// If provided, this identifier may be used to identify the application or      /// communicate with it through the windowing system.      pub id: Option<String>, -    /// The window settings. -    /// -    /// They will be ignored on the Web. -    pub window: window::Settings, - -    /// The data needed to initialize the [`Program`]. -    /// -    /// [`Program`]: crate::Program -    pub flags: Flags, -      /// The fonts to load on boot.      pub fonts: Vec<Cow<'static, [u8]>>, @@ -50,34 +37,10 @@ pub struct Settings<Flags = ()> {      pub antialiasing: bool,  } -impl<Flags> Settings<Flags> { -    /// Initialize [`Program`] settings using the given data. -    /// -    /// [`Program`]: crate::Program -    pub fn with_flags(flags: Flags) -> Self { -        let default_settings = Settings::<()>::default(); - -        Self { -            flags, -            id: default_settings.id, -            window: default_settings.window, -            fonts: default_settings.fonts, -            default_font: default_settings.default_font, -            default_text_size: default_settings.default_text_size, -            antialiasing: default_settings.antialiasing, -        } -    } -} - -impl<Flags> Default for Settings<Flags> -where -    Flags: Default, -{ +impl Default for Settings {      fn default() -> Self {          Self {              id: None, -            window: window::Settings::default(), -            flags: Default::default(),              fonts: Vec::new(),              default_font: Font::default(),              default_text_size: Pixels(16.0), @@ -86,12 +49,10 @@ where      }  } -impl<Flags> From<Settings<Flags>> for iced_winit::Settings<Flags> { -    fn from(settings: Settings<Flags>) -> iced_winit::Settings<Flags> { +impl From<Settings> for iced_winit::Settings { +    fn from(settings: Settings) -> iced_winit::Settings {          iced_winit::Settings {              id: settings.id, -            window: settings.window, -            flags: settings.flags,              fonts: settings.fonts,          }      } | 
