diff --git a/Cargo.toml b/Cargo.toml index fbe3b9f2..87f3000e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ iced_web = { version = "0.1.0", path = "web" } [dev-dependencies] iced_native = { version = "0.1", path = "./native" } iced_wgpu = { version = "0.1", path = "./wgpu" } +iced_futures = { version = "0.1.0-alpha", path = "./futures", features = ["async-std"] } env_logger = "0.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/examples/events.rs b/examples/events.rs index 74542171..0c9dca05 100644 --- a/examples/events.rs +++ b/examples/events.rs @@ -1,6 +1,6 @@ use iced::{ - Align, Application, Checkbox, Column, Command, Container, Element, Length, - Settings, Subscription, Text, + executor, Align, Application, Checkbox, Column, Command, Container, + Element, Length, Settings, Subscription, Text, }; pub fn main() { @@ -20,6 +20,7 @@ enum Message { } impl Application for Events { + type Executor = executor::Default; type Message = Message; fn new() -> (Events, Command) { diff --git a/examples/pokedex.rs b/examples/pokedex.rs index 7326f94f..505dbf19 100644 --- a/examples/pokedex.rs +++ b/examples/pokedex.rs @@ -27,6 +27,7 @@ enum Message { } impl Application for Pokedex { + type Executor = iced_futures::executor::AsyncStd; type Message = Message; fn new() -> (Pokedex, Command) { diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index 2bc85c4d..6e357039 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -28,6 +28,7 @@ enum Message { } impl Application for Stopwatch { + type Executor = iced_futures::executor::AsyncStd; type Message = Message; fn new() -> (Stopwatch, Command) { diff --git a/examples/todos.rs b/examples/todos.rs index 4166f75a..06595a1e 100644 --- a/examples/todos.rs +++ b/examples/todos.rs @@ -38,6 +38,7 @@ enum Message { } impl Application for Todos { + type Executor = iced_futures::executor::AsyncStd; type Message = Message; fn new() -> (Todos, Command) { diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 5b303e01..13c2d6b7 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -23,3 +23,7 @@ version = "0.3" version = "0.2" optional = true features = ["rt-core"] + +[dependencies.async-std] +version = "1.0" +optional = true diff --git a/futures/src/executor.rs b/futures/src/executor.rs new file mode 100644 index 00000000..144a41f8 --- /dev/null +++ b/futures/src/executor.rs @@ -0,0 +1,36 @@ +//! Choose your preferred executor to power a runtime. +mod null; + +#[cfg(feature = "thread-pool")] +mod thread_pool; + +#[cfg(feature = "thread-pool")] +pub use thread_pool::ThreadPool; + +#[cfg(feature = "tokio")] +mod tokio; + +#[cfg(feature = "async-std")] +mod async_std; + +pub use null::Null; + +#[cfg(feature = "tokio")] +pub use self::tokio::Tokio; + +#[cfg(feature = "async-std")] +pub use self::async_std::AsyncStd; + +use futures::Future; + +pub trait Executor: Sized { + fn new() -> Result + where + Self: Sized; + + fn spawn(&self, future: impl Future + Send + 'static); + + fn enter(&self, f: impl FnOnce() -> R) -> R { + f() + } +} diff --git a/futures/src/executor/async_std.rs b/futures/src/executor/async_std.rs new file mode 100644 index 00000000..b056b23d --- /dev/null +++ b/futures/src/executor/async_std.rs @@ -0,0 +1,15 @@ +use crate::Executor; + +use futures::Future; + +pub struct AsyncStd; + +impl Executor for AsyncStd { + fn new() -> Result { + Ok(Self) + } + + fn spawn(&self, future: impl Future + Send + 'static) { + let _ = async_std::task::spawn(future); + } +} diff --git a/futures/src/executor/null.rs b/futures/src/executor/null.rs new file mode 100644 index 00000000..722073bb --- /dev/null +++ b/futures/src/executor/null.rs @@ -0,0 +1,13 @@ +use crate::Executor; + +use futures::Future; + +pub struct Null; + +impl Executor for Null { + fn new() -> Result { + Ok(Self) + } + + fn spawn(&self, _future: impl Future + Send + 'static) {} +} diff --git a/futures/src/executor/thread_pool.rs b/futures/src/executor/thread_pool.rs new file mode 100644 index 00000000..6393d0d5 --- /dev/null +++ b/futures/src/executor/thread_pool.rs @@ -0,0 +1,15 @@ +use crate::Executor; + +use futures::Future; + +pub type ThreadPool = futures::executor::ThreadPool; + +impl Executor for futures::executor::ThreadPool { + fn new() -> Result { + futures::executor::ThreadPool::new() + } + + fn spawn(&self, future: impl Future + Send + 'static) { + self.spawn_ok(future); + } +} diff --git a/futures/src/executor/tokio.rs b/futures/src/executor/tokio.rs new file mode 100644 index 00000000..aafa7e7b --- /dev/null +++ b/futures/src/executor/tokio.rs @@ -0,0 +1,19 @@ +use crate::Executor; + +use futures::Future; + +pub type Tokio = tokio::runtime::Runtime; + +impl Executor for Tokio { + fn new() -> Result { + tokio::runtime::Runtime::new() + } + + fn spawn(&self, future: impl Future + Send + 'static) { + let _ = tokio::runtime::Runtime::spawn(self, future); + } + + fn enter(&self, f: impl FnOnce() -> R) -> R { + tokio::runtime::Runtime::enter(self, f) + } +} diff --git a/futures/src/lib.rs b/futures/src/lib.rs index f6bcf85a..832a50f6 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -1,8 +1,12 @@ -mod command; +pub use futures; -pub mod runtime; +mod command; +mod runtime; + +pub mod executor; pub mod subscription; pub use command::Command; +pub use executor::Executor; pub use runtime::Runtime; pub use subscription::Subscription; diff --git a/futures/src/runtime.rs b/futures/src/runtime.rs index 37905c61..a508c46e 100644 --- a/futures/src/runtime.rs +++ b/futures/src/runtime.rs @@ -1,9 +1,5 @@ -//! Run commands and subscriptions. -mod executor; - -pub use executor::Executor; - -use crate::{subscription, Command, Subscription}; +//! Run commands and keep track of subscriptions. +use crate::{subscription, Command, Executor, Subscription}; use futures::Sink; use std::marker::PhantomData; diff --git a/futures/src/runtime/executor.rs b/futures/src/runtime/executor.rs deleted file mode 100644 index eec5e231..00000000 --- a/futures/src/runtime/executor.rs +++ /dev/null @@ -1,27 +0,0 @@ -use futures::Future; - -pub trait Executor { - fn spawn(&self, future: impl Future + Send + 'static); - - fn enter(&self, f: impl FnOnce() -> R) -> R { - f() - } -} - -#[cfg(feature = "thread-pool")] -impl Executor for futures::executor::ThreadPool { - fn spawn(&self, future: impl Future + Send + 'static) { - self.spawn_ok(future); - } -} - -#[cfg(feature = "tokio")] -impl Executor for tokio::runtime::Runtime { - fn spawn(&self, future: impl Future + Send + 'static) { - let _ = tokio::runtime::Runtime::spawn(self, future); - } - - fn enter(&self, f: impl FnOnce() -> R) -> R { - tokio::runtime::Runtime::enter(self, f) - } -} diff --git a/native/Cargo.toml b/native/Cargo.toml index 57a869e2..6276535e 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -11,7 +11,6 @@ repository = "https://github.com/hecrj/iced" twox-hash = "1.5" raw-window-handle = "0.3" unicode-segmentation = "1.6" -futures = "0.3" [dependencies.iced_core] version = "0.1.0" @@ -20,3 +19,4 @@ path = "../core" [dependencies.iced_futures] version = "0.1.0-alpha" path = "../futures" +features = ["thread-pool"] diff --git a/native/src/lib.rs b/native/src/lib.rs index 7730c6a3..b5856c00 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -42,7 +42,6 @@ pub mod input; pub mod layout; pub mod renderer; -pub mod runtime; pub mod subscription; pub mod widget; pub mod window; @@ -52,6 +51,7 @@ mod element; mod event; mod hasher; mod mouse_cursor; +mod runtime; mod size; mod user_interface; @@ -59,7 +59,10 @@ pub use iced_core::{ Align, Background, Color, Font, HorizontalAlignment, Length, Point, Rectangle, Vector, VerticalAlignment, }; -pub use iced_futures::Command; +pub use iced_futures::{executor, futures, Command}; + +#[doc(no_inline)] +pub use executor::Executor; pub use clipboard::Clipboard; pub use element::Element; diff --git a/native/src/runtime.rs b/native/src/runtime.rs index 2b3abbf1..9fa031f4 100644 --- a/native/src/runtime.rs +++ b/native/src/runtime.rs @@ -10,5 +10,3 @@ use crate::{Event, Hasher}; /// [`Subscription`]: ../struct.Subscription.html pub type Runtime = iced_futures::Runtime; - -pub use iced_futures::runtime::Executor; diff --git a/native/src/subscription.rs b/native/src/subscription.rs index 43f1758a..0d002c6c 100644 --- a/native/src/subscription.rs +++ b/native/src/subscription.rs @@ -1,6 +1,6 @@ //! Listen to external events in your application. use crate::{Event, Hasher}; -use futures::stream::BoxStream; +use iced_futures::futures::stream::BoxStream; /// A request to listen to external events. /// diff --git a/native/src/subscription/events.rs b/native/src/subscription/events.rs index 6ff2c0fb..7d33166e 100644 --- a/native/src/subscription/events.rs +++ b/native/src/subscription/events.rs @@ -2,6 +2,7 @@ use crate::{ subscription::{EventStream, Recipe}, Event, Hasher, }; +use iced_futures::futures::stream::BoxStream; pub struct Events; @@ -17,7 +18,7 @@ impl Recipe for Events { fn stream( self: Box, event_stream: EventStream, - ) -> futures::stream::BoxStream<'static, Self::Output> { + ) -> BoxStream<'static, Self::Output> { event_stream } } diff --git a/src/application.rs b/src/application.rs index b940cc17..3a526f1b 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,4 +1,4 @@ -use crate::{window, Command, Element, Settings, Subscription}; +use crate::{window, Command, Element, Executor, Settings, Subscription}; /// An interactive cross-platform application. /// @@ -19,7 +19,7 @@ use crate::{window, Command, Element, Settings, Subscription}; /// before](index.html#overview). We just need to fill in the gaps: /// /// ```no_run -/// use iced::{button, Application, Button, Column, Command, Element, Settings, Text}; +/// use iced::{button, executor, Application, Button, Column, Command, Element, Settings, Text}; /// /// pub fn main() { /// Counter::run(Settings::default()) @@ -39,6 +39,7 @@ use crate::{window, Command, Element, Settings, Subscription}; /// } /// /// impl Application for Counter { +/// type Executor = executor::Null; /// type Message = Message; /// /// fn new() -> (Self, Command) { @@ -80,6 +81,14 @@ use crate::{window, Command, Element, Settings, Subscription}; /// } /// ``` pub trait Application: Sized { + /// The [`Executor`] that will run commands and subscriptions. + /// + /// The [`executor::Default`] can be a good starting point! + /// + /// [`Executor`]: trait.Executor.html + /// [`executor::Default`]: executor/struct.Default.html + type Executor: Executor; + /// The type of __messages__ your [`Application`] will produce. /// /// [`Application`]: trait.Application.html @@ -185,6 +194,7 @@ where A: Application, { type Renderer = iced_wgpu::Renderer; + type Executor = A::Executor; type Message = A::Message; fn new() -> (Self, Command) { diff --git a/src/lib.rs b/src/lib.rs index 759dea2f..18dfc098 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -180,18 +180,26 @@ #![deny(unsafe_code)] #![deny(rust_2018_idioms)] mod application; -#[cfg(target_arch = "wasm32")] -#[path = "web.rs"] -mod platform; -#[cfg(not(target_arch = "wasm32"))] -#[path = "native.rs"] -mod platform; mod sandbox; +#[cfg(not(target_arch = "wasm32"))] +mod native; + +#[cfg(not(target_arch = "wasm32"))] +pub use native::*; + +#[cfg(target_arch = "wasm32")] +mod web; + +#[cfg(target_arch = "wasm32")] +pub use web::*; + pub mod settings; pub mod window; +#[doc(no_inline)] +pub use executor::Executor; + pub use application::Application; -pub use platform::*; pub use sandbox::Sandbox; pub use settings::Settings; diff --git a/src/native.rs b/src/native.rs index 35441a3e..86ccffab 100644 --- a/src/native.rs +++ b/src/native.rs @@ -3,6 +3,8 @@ pub use iced_winit::{ Space, Subscription, Vector, VerticalAlignment, }; +pub mod executor; + pub mod widget { //! Display information and interactive controls in your application. //! diff --git a/src/native/executor.rs b/src/native/executor.rs new file mode 100644 index 00000000..68a1d280 --- /dev/null +++ b/src/native/executor.rs @@ -0,0 +1,23 @@ +//! Choose your preferred executor to power your application. +pub use iced_winit::{executor::Null, Executor}; +use iced_winit::{executor::ThreadPool, futures}; + +/// The default cross-platform executor. +/// +/// - On native platforms, it will use a `ThreadPool`. +/// - On the Web, it will use `wasm-bindgen-futures::spawn_local`. +#[derive(Debug)] +pub struct Default(ThreadPool); + +impl Executor for Default { + fn new() -> Result { + Ok(Default(ThreadPool::new()?)) + } + + fn spawn( + &self, + future: impl futures::Future + Send + 'static, + ) { + self.0.spawn(future); + } +} diff --git a/src/sandbox.rs b/src/sandbox.rs index dda4c3f5..2c0332ff 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -1,4 +1,4 @@ -use crate::{Application, Command, Element, Settings, Subscription}; +use crate::{executor, Application, Command, Element, Settings, Subscription}; /// A sandboxed [`Application`]. /// @@ -133,6 +133,7 @@ impl Application for T where T: Sandbox, { + type Executor = executor::Null; type Message = T::Message; fn new() -> (Self, Command) { diff --git a/web/Cargo.toml b/web/Cargo.toml index ea092575..46953863 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -18,7 +18,6 @@ maintenance = { status = "actively-developed" } dodrio = "0.1.0" wasm-bindgen = "0.2.51" wasm-bindgen-futures = "0.4" -futures = "0.3" [dependencies.iced_core] version = "0.1.0" diff --git a/web/src/lib.rs b/web/src/lib.rs index b183c390..c44b99b5 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -75,7 +75,7 @@ pub use iced_core::{ Align, Background, Color, Font, HorizontalAlignment, Length, VerticalAlignment, }; -pub use iced_futures::Command; +pub use iced_futures::{futures, Command}; pub use style::Style; pub use subscription::Subscription; pub use widget::*; diff --git a/winit/Cargo.toml b/winit/Cargo.toml index ba6d5229..cef41e9c 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -16,17 +16,11 @@ debug = [] [dependencies] winit = { version = "0.20.0-alpha3", git = "https://github.com/hecrj/winit", rev = "709808eb4e69044705fcb214bcc30556db761405"} log = "0.4" -futures = "0.3" [dependencies.iced_native] version = "0.1.0-alpha" path = "../native" -[dependencies.iced_futures] -version = "0.1.0-alpha" -path = "../futures" -features = ["thread-pool"] - [dependencies.window_clipboard] git = "https://github.com/hecrj/window_clipboard" rev = "22c6dd6c04cd05d528029b50a30c56417cd4bebf" diff --git a/winit/src/application.rs b/winit/src/application.rs index 076ac092..4b21a930 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -1,10 +1,9 @@ use crate::{ conversion, input::{keyboard, mouse}, - window, Cache, Clipboard, Command, Debug, Element, Event, Mode, - MouseCursor, Proxy, Settings, Size, Subscription, UserInterface, + window, Cache, Clipboard, Command, Debug, Element, Event, Executor, Mode, + MouseCursor, Proxy, Runtime, Settings, Size, Subscription, UserInterface, }; -use iced_native::Runtime; /// An interactive, native cross-platform application. /// @@ -20,6 +19,11 @@ pub trait Application: Sized { /// [`Application`]: trait.Application.html type Renderer: window::Renderer; + /// The [`Executor`] that will run commands and subscriptions. + /// + /// [`Executor`]: trait.Executor.html + type Executor: Executor; + /// The type of __messages__ your [`Application`] will produce. /// /// [`Application`]: trait.Application.html @@ -110,14 +114,14 @@ pub trait Application: Sized { debug.startup_started(); let event_loop = EventLoop::with_user_event(); - let mut runtime = { - let thread_pool = futures::executor::ThreadPool::new() - .expect("Create thread pool"); - - Runtime::new(thread_pool, Proxy::new(event_loop.create_proxy())) - }; let mut external_messages = Vec::new(); + let mut runtime = { + let executor = Self::Executor::new().expect("Create executor"); + + Runtime::new(executor, Proxy::new(event_loop.create_proxy())) + }; + let (mut application, init_command) = Self::new(); runtime.spawn(init_command); diff --git a/winit/src/proxy.rs b/winit/src/proxy.rs index 7e8dee98..cff9df33 100644 --- a/winit/src/proxy.rs +++ b/winit/src/proxy.rs @@ -1,4 +1,4 @@ -use futures::{ +use iced_native::futures::{ task::{Context, Poll}, Sink, };