diff options
Diffstat (limited to 'src/application.rs')
-rw-r--r-- | src/application.rs | 251 |
1 files changed, 147 insertions, 104 deletions
diff --git a/src/application.rs b/src/application.rs index 2ee3337f..3b690a7c 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,4 +1,5 @@ -use crate::{window, Command, Element, Executor, Settings, Subscription}; +use crate::window; +use crate::{Color, Command, Element, Executor, Settings, Subscription}; /// An interactive cross-platform application. /// @@ -9,109 +10,114 @@ use crate::{window, Command, Element, Executor, Settings, Subscription}; /// - On the web, it will take control of the `<title>` and the `<body>` of the /// document. /// -/// An [`Application`](trait.Application.html) can execute asynchronous actions -/// by returning a [`Command`](struct.Command.html) in some of its methods. If -/// you do not intend to perform any background work in your program, the -/// [`Sandbox`](trait.Sandbox.html) trait offers a simplified interface. +/// An [`Application`] can execute asynchronous actions by returning a +/// [`Command`] in some of its methods. If you do not intend to perform any +/// background work in your program, the [`Sandbox`] trait offers a simplified +/// interface. /// -/// # Example -/// Let's say we want to run the [`Counter` example we implemented -/// before](index.html#overview). We just need to fill in the gaps: +/// When using an [`Application`] with the `debug` feature enabled, a debug view +/// can be toggled by pressing `F12`. /// -/// ```no_run -/// use iced::{button, executor, Application, Button, Column, Command, Element, Settings, Text}; +/// # Examples +/// [The repository has a bunch of examples] that use the [`Application`] trait: /// -/// pub fn main() { -/// Counter::run(Settings::default()) -/// } +/// - [`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]. /// -/// #[derive(Default)] -/// struct Counter { -/// value: i32, -/// increment_button: button::State, -/// decrement_button: button::State, -/// } +/// [The repository has a bunch of examples]: https://github.com/hecrj/iced/tree/0.2/examples +/// [`clock`]: https://github.com/hecrj/iced/tree/0.2/examples/clock +/// [`download_progress`]: https://github.com/hecrj/iced/tree/0.2/examples/download_progress +/// [`events`]: https://github.com/hecrj/iced/tree/0.2/examples/events +/// [`game_of_life`]: https://github.com/hecrj/iced/tree/0.2/examples/game_of_life +/// [`pokedex`]: https://github.com/hecrj/iced/tree/0.2/examples/pokedex +/// [`solar_system`]: https://github.com/hecrj/iced/tree/0.2/examples/solar_system +/// [`stopwatch`]: https://github.com/hecrj/iced/tree/0.2/examples/stopwatch +/// [`todos`]: https://github.com/hecrj/iced/tree/0.2/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!": +/// +/// ```no_run +/// use iced::{executor, Application, Command, Element, Settings, Text}; /// -/// #[derive(Debug, Clone, Copy)] -/// enum Message { -/// IncrementPressed, -/// DecrementPressed, +/// pub fn main() -> iced::Result { +/// Hello::run(Settings::default()) /// } /// -/// impl Application for Counter { -/// type Executor = executor::Null; -/// type Message = Message; +/// struct Hello; /// -/// fn new() -> (Self, Command<Message>) { -/// (Self::default(), Command::none()) +/// impl Application for Hello { +/// type Executor = executor::Default; +/// type Message = (); +/// type Flags = (); +/// +/// fn new(_flags: ()) -> (Hello, Command<Self::Message>) { +/// (Hello, Command::none()) /// } /// /// fn title(&self) -> String { -/// String::from("A simple counter") +/// String::from("A cool application") /// } /// -/// fn update(&mut self, message: Message) -> Command<Message> { -/// match message { -/// Message::IncrementPressed => { -/// self.value += 1; -/// } -/// Message::DecrementPressed => { -/// self.value -= 1; -/// } -/// } -/// +/// fn update(&mut self, _message: Self::Message) -> Command<Self::Message> { /// Command::none() /// } /// -/// fn view(&mut self) -> Element<Message> { -/// Column::new() -/// .push( -/// Button::new(&mut self.increment_button, Text::new("Increment")) -/// .on_press(Message::IncrementPressed), -/// ) -/// .push( -/// Text::new(self.value.to_string()).size(50), -/// ) -/// .push( -/// Button::new(&mut self.decrement_button, Text::new("Decrement")) -/// .on_press(Message::DecrementPressed), -/// ) -/// .into() +/// fn view(&mut self) -> Element<Self::Message> { +/// Text::new("Hello, world!").into() /// } /// } /// ``` pub trait Application: Sized { /// The [`Executor`] that will run commands and subscriptions. /// - /// The [`executor::Default`] can be a good starting point! + /// The [default executor] can be a good starting point! /// - /// [`Executor`]: trait.Executor.html - /// [`executor::Default`]: executor/struct.Default.html + /// [`Executor`]: Self::Executor + /// [default executor]: crate::executor::Default type Executor: Executor; /// The type of __messages__ your [`Application`] will produce. - /// - /// [`Application`]: trait.Application.html type Message: std::fmt::Debug + Send; - /// Initializes the [`Application`]. + /// 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`](struct.Command.html) 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. + /// 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. /// - /// [`Application`]: trait.Application.html - fn new() -> (Self, Command<Self::Message>); + /// [`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. - /// - /// [`Application`]: trait.Application.html fn title(&self) -> String; /// Handles a __message__ and updates the state of the [`Application`]. @@ -121,9 +127,6 @@ pub trait Application: Sized { /// this method. /// /// Any [`Command`] returned will be executed immediately in the background. - /// - /// [`Application`]: trait.Application.html - /// [`Command`]: struct.Command.html fn update(&mut self, message: Self::Message) -> Command<Self::Message>; /// Returns the event [`Subscription`] for the current state of the @@ -134,8 +137,6 @@ pub trait Application: Sized { /// [`update`](#tymethod.update). /// /// By default, this method returns an empty [`Subscription`]. - /// - /// [`Subscription`]: struct.Subscription.html fn subscription(&self) -> Subscription<Self::Message> { Subscription::none() } @@ -143,8 +144,6 @@ pub trait Application: Sized { /// Returns the widgets to display in the [`Application`]. /// /// These widgets can produce __messages__ based on user interaction. - /// - /// [`Application`]: trait.Application.html fn view(&mut self) -> Element<'_, Self::Message>; /// Returns the current [`Application`] mode. @@ -155,56 +154,99 @@ pub trait Application: Sized { /// Currently, the mode only has an effect in native platforms. /// /// By default, an application will run in windowed mode. - /// - /// [`Application`]: trait.Application.html 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 + } + /// Runs the [`Application`]. /// - /// This method will take control of the current thread and __will NOT - /// return__. + /// On native platforms, this method will take control of the current thread + /// and __will NOT return__ unless there is an [`Error`] during startup. /// /// It should probably be that last thing you call in your `main` function. /// - /// [`Application`]: trait.Application.html - fn run(_settings: Settings) + /// [`Error`]: crate::Error + fn run(settings: Settings<Self::Flags>) -> crate::Result where Self: 'static, { #[cfg(not(target_arch = "wasm32"))] - <Instance<Self> as iced_winit::Application>::run( - _settings.into(), - iced_wgpu::Settings { - default_font: _settings.default_font, - antialiasing: if _settings.antialiasing { - Some(iced_wgpu::settings::Antialiasing::MSAAx4) + { + let renderer_settings = crate::renderer::Settings { + default_font: settings.default_font, + default_text_size: settings.default_text_size, + antialiasing: if settings.antialiasing { + Some(crate::renderer::settings::Antialiasing::MSAAx4) } else { None }, - ..iced_wgpu::Settings::default() - }, - ); + ..crate::renderer::Settings::default() + }; + + Ok(crate::runtime::application::run::< + Instance<Self>, + Self::Executor, + crate::renderer::window::Compositor, + >(settings.into(), renderer_settings)?) + } #[cfg(target_arch = "wasm32")] - <Instance<Self> as iced_web::Application>::run(); + { + <Instance<Self> as iced_web::Application>::run(settings.flags); + + Ok(()) + } } } struct Instance<A: Application>(A); #[cfg(not(target_arch = "wasm32"))] -impl<A> iced_winit::Application for Instance<A> +impl<A> iced_winit::Program for Instance<A> where A: Application, { - type Backend = iced_wgpu::window::Backend; - type Executor = A::Executor; + type Renderer = crate::renderer::Renderer; type Message = A::Message; - fn new() -> (Self, Command<A::Message>) { - let (app, command) = A::new(); + fn update(&mut self, message: Self::Message) -> Command<Self::Message> { + self.0.update(message) + } + + fn view(&mut self) -> Element<'_, Self::Message> { + self.0.view() + } +} + +#[cfg(not(target_arch = "wasm32"))] +impl<A> crate::runtime::Application for Instance<A> +where + A: Application, +{ + type Flags = A::Flags; + + fn new(flags: Self::Flags) -> (Self, Command<A::Message>) { + let (app, command) = A::new(flags); (Instance(app), command) } @@ -220,16 +262,16 @@ where } } - fn update(&mut self, message: Self::Message) -> Command<Self::Message> { - self.0.update(message) - } - fn subscription(&self) -> Subscription<Self::Message> { self.0.subscription() } - fn view(&mut self) -> Element<'_, Self::Message> { - self.0.view() + fn background_color(&self) -> Color { + self.0.background_color() + } + + fn scale_factor(&self) -> f64 { + self.0.scale_factor() } } @@ -238,11 +280,12 @@ impl<A> iced_web::Application for Instance<A> where A: Application, { - type Message = A::Message; type Executor = A::Executor; + type Message = A::Message; + type Flags = A::Flags; - fn new() -> (Self, Command<A::Message>) { - let (app, command) = A::new(); + fn new(flags: Self::Flags) -> (Self, Command<A::Message>) { + let (app, command) = A::new(flags); (Instance(app), command) } |