diff options
Diffstat (limited to 'src/pure')
| -rw-r--r-- | src/pure/application.rs | 186 | ||||
| -rw-r--r-- | src/pure/sandbox.rs | 119 | 
2 files changed, 305 insertions, 0 deletions
| diff --git a/src/pure/application.rs b/src/pure/application.rs new file mode 100644 index 00000000..5f400bea --- /dev/null +++ b/src/pure/application.rs @@ -0,0 +1,186 @@ +use crate::pure::{self, Pure}; +use crate::window; +use crate::{Color, Command, Executor, Settings, Subscription}; + +/// A pure version of [`Application`]. +/// +/// Unlike the impure version, the `view` method of this trait takes an +/// immutable reference to `self` and returns a pure [`Element`]. +/// +/// [`Application`]: crate::Application +/// [`Element`]: pure::Element +pub trait Application: Sized { +    /// 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 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 [`Command`] 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, Command<Self::Message>); + +    /// 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; + +    /// 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 [`Command`] returned will be executed immediately in the background. +    fn update(&mut self, message: Self::Message) -> Command<Self::Message>; + +    /// 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 widgets to display in the [`Application`]. +    /// +    /// These widgets can produce __messages__ based on user interaction. +    fn view(&self) -> pure::Element<'_, Self::Message>; + +    /// Returns the current [`Application`] mode. +    /// +    /// The runtime will automatically transition your application if a new mode +    /// is returned. +    /// +    /// Currently, the mode only has an effect in native platforms. +    /// +    /// By default, an application will run in windowed mode. +    fn mode(&self) -> window::Mode { +        window::Mode::Windowed +    } + +    /// Returns the background color of the [`Application`]. +    /// +    /// By default, it returns [`Color::WHITE`]. +    fn background_color(&self) -> Color { +        Color::WHITE +    } + +    /// 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 +    } + +    /// Returns whether the [`Application`] should be terminated. +    /// +    /// By default, it returns `false`. +    fn should_exit(&self) -> bool { +        false +    } + +    /// 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, +    { +        <Instance<Self> as crate::Application>::run(settings) +    } +} + +struct Instance<A: Application> { +    application: A, +    state: pure::State, +} + +impl<A> crate::Application for Instance<A> +where +    A: Application, +    A::Message: 'static, +{ +    type Executor = A::Executor; +    type Message = A::Message; +    type Flags = A::Flags; + +    fn new(flags: Self::Flags) -> (Self, Command<Self::Message>) { +        let (application, command) = A::new(flags); + +        ( +            Instance { +                application, +                state: pure::State::new(), +            }, +            command, +        ) +    } + +    fn title(&self) -> String { +        A::title(&self.application) +    } + +    fn update(&mut self, message: Self::Message) -> Command<Self::Message> { +        A::update(&mut self.application, message) +    } + +    fn subscription(&self) -> Subscription<Self::Message> { +        A::subscription(&self.application) +    } + +    fn view(&mut self) -> crate::Element<'_, Self::Message> { +        let content = A::view(&self.application); + +        Pure::new(&mut self.state, content).into() +    } + +    fn mode(&self) -> window::Mode { +        A::mode(&self.application) +    } + +    fn background_color(&self) -> Color { +        A::background_color(&self.application) +    } + +    fn scale_factor(&self) -> f64 { +        A::scale_factor(&self.application) +    } + +    fn should_exit(&self) -> bool { +        A::should_exit(&self.application) +    } +} diff --git a/src/pure/sandbox.rs b/src/pure/sandbox.rs new file mode 100644 index 00000000..fbd1d7a8 --- /dev/null +++ b/src/pure/sandbox.rs @@ -0,0 +1,119 @@ +use crate::pure; +use crate::{Color, Command, Error, Settings, Subscription}; + +/// A pure version of [`Sandbox`]. +/// +/// Unlike the impure version, the `view` method of this trait takes an +/// immutable reference to `self` and returns a pure [`Element`]. +/// +/// [`Sandbox`]: crate::Sandbox +/// [`Element`]: pure::Element +pub trait Sandbox { +    /// The type of __messages__ your [`Sandbox`] will produce. +    type Message: std::fmt::Debug + Send; + +    /// Initializes the [`Sandbox`]. +    /// +    /// Here is where you should return the initial state of your app. +    fn new() -> Self; + +    /// Returns the current title of the [`Sandbox`]. +    /// +    /// This title can be dynamic! The runtime will automatically update the +    /// title of your application when necessary. +    fn title(&self) -> String; + +    /// Handles a __message__ and updates the state of the [`Sandbox`]. +    /// +    /// This is where you define your __update logic__. All the __messages__, +    /// produced by user interactions, will be handled by this method. +    fn update(&mut self, message: Self::Message); + +    /// Returns the widgets to display in the [`Sandbox`]. +    /// +    /// These widgets can produce __messages__ based on user interaction. +    fn view(&self) -> pure::Element<'_, Self::Message>; + +    /// Returns the background color of the [`Sandbox`]. +    /// +    /// By default, it returns [`Color::WHITE`]. +    fn background_color(&self) -> Color { +        Color::WHITE +    } + +    /// Returns the scale factor of the [`Sandbox`]. +    /// +    /// 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 +    } + +    /// Returns whether the [`Sandbox`] should be terminated. +    /// +    /// By default, it returns `false`. +    fn should_exit(&self) -> bool { +        false +    } + +    /// Runs the [`Sandbox`]. +    /// +    /// On native platforms, this method will take control of the current thread +    /// and __will NOT return__. +    /// +    /// It should probably be that last thing you call in your `main` function. +    fn run(settings: Settings<()>) -> Result<(), Error> +    where +        Self: 'static + Sized, +    { +        <Self as pure::Application>::run(settings) +    } +} + +impl<T> pure::Application for T +where +    T: Sandbox, +{ +    type Executor = iced_futures::backend::null::Executor; +    type Flags = (); +    type Message = T::Message; + +    fn new(_flags: ()) -> (Self, Command<T::Message>) { +        (T::new(), Command::none()) +    } + +    fn title(&self) -> String { +        T::title(self) +    } + +    fn update(&mut self, message: T::Message) -> Command<T::Message> { +        T::update(self, message); + +        Command::none() +    } + +    fn subscription(&self) -> Subscription<T::Message> { +        Subscription::none() +    } + +    fn view(&self) -> pure::Element<'_, T::Message> { +        T::view(self) +    } + +    fn background_color(&self) -> Color { +        T::background_color(self) +    } + +    fn scale_factor(&self) -> f64 { +        T::scale_factor(self) +    } + +    fn should_exit(&self) -> bool { +        T::should_exit(self) +    } +} | 
