diff options
Diffstat (limited to 'futures')
| -rw-r--r-- | futures/Cargo.toml | 61 | ||||
| -rw-r--r-- | futures/src/event.rs | 59 | ||||
| -rw-r--r-- | futures/src/keyboard.rs | 61 | ||||
| -rw-r--r-- | futures/src/lib.rs | 7 | ||||
| -rw-r--r-- | futures/src/runtime.rs | 5 | ||||
| -rw-r--r-- | futures/src/subscription.rs | 97 | ||||
| -rw-r--r-- | futures/src/subscription/tracker.rs | 3 | 
7 files changed, 179 insertions, 114 deletions
| diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 044827c2..69a915e4 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,47 +1,40 @@  [package]  name = "iced_futures" -version = "0.7.0" -authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] -edition = "2021" -description = "Commands, subscriptions, and runtimes for Iced" -license = "MIT" -repository = "https://github.com/iced-rs/iced" -documentation = "https://docs.rs/iced_futures" -keywords = ["gui", "ui", "graphics", "interface", "futures"] -categories = ["gui"] +description = "Commands, subscriptions, and future executors for iced" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +categories.workspace = true +keywords.workspace = true + +[package.metadata.docs.rs] +rustdoc-args = ["--cfg", "docsrs"] +all-features = true  [features]  thread-pool = ["futures/thread-pool"]  [dependencies] -log = "0.4" - -[dependencies.iced_core] -version = "0.10" -path = "../core" +iced_core.workspace = true -[dependencies.futures] -version = "0.3" +futures.workspace = true +log.workspace = true -[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio] -package = "tokio" -version = "1.0" -optional = true -features = ["rt", "rt-multi-thread", "time"] +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +async-std.workspace = true +async-std.optional = true +async-std.features = ["unstable"] -[target.'cfg(not(target_arch = "wasm32"))'.dependencies.async-std] -version = "1.0" -optional = true -features = ["unstable"] +smol.workspace = true +smol.optional = true -[target.'cfg(not(target_arch = "wasm32"))'.dependencies.smol] -version = "1.2" -optional = true +tokio.workspace = true +tokio.optional = true +tokio.features = ["rt", "rt-multi-thread", "time"]  [target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen-futures = "0.4" -wasm-timer = "0.2" - -[package.metadata.docs.rs] -rustdoc-args = ["--cfg", "docsrs"] -all-features = true +wasm-bindgen-futures.workspace = true +wasm-timer.workspace = true diff --git a/futures/src/event.rs b/futures/src/event.rs new file mode 100644 index 00000000..214d2d40 --- /dev/null +++ b/futures/src/event.rs @@ -0,0 +1,59 @@ +//! Listen to runtime events. +use crate::core::event::{self, Event}; +use crate::core::window; +use crate::subscription::{self, Subscription}; +use crate::MaybeSend; + +/// Returns a [`Subscription`] to all the ignored runtime events. +/// +/// This subscription will notify your application of any [`Event`] that was +/// not captured by any widget. +pub fn listen() -> Subscription<Event> { +    listen_with(|event, status| match status { +        event::Status::Ignored => Some(event), +        event::Status::Captured => None, +    }) +} + +/// Creates a [`Subscription`] that listens and filters all the runtime events +/// with the provided function, producing messages accordingly. +/// +/// This subscription will call the provided function for every [`Event`] +/// handled by the runtime. If the function: +/// +/// - Returns `None`, the [`Event`] will be discarded. +/// - Returns `Some` message, the `Message` will be produced. +pub fn listen_with<Message>( +    f: fn(Event, event::Status) -> Option<Message>, +) -> Subscription<Message> +where +    Message: 'static + MaybeSend, +{ +    #[derive(Hash)] +    struct EventsWith; + +    subscription::filter_map( +        (EventsWith, f), +        move |event, status| match event { +            Event::Window(window::Event::RedrawRequested(_)) => None, +            _ => f(event, status), +        }, +    ) +} + +/// Creates a [`Subscription`] that produces a message for every runtime event, +/// including the redraw request events. +/// +/// **Warning:** This [`Subscription`], if unfiltered, may produce messages in +/// an infinite loop. +pub fn listen_raw<Message>( +    f: fn(Event, event::Status) -> Option<Message>, +) -> Subscription<Message> +where +    Message: 'static + MaybeSend, +{ +    #[derive(Hash)] +    struct RawEvents; + +    subscription::filter_map((RawEvents, f), f) +} diff --git a/futures/src/keyboard.rs b/futures/src/keyboard.rs new file mode 100644 index 00000000..af68e1f2 --- /dev/null +++ b/futures/src/keyboard.rs @@ -0,0 +1,61 @@ +//! Listen to keyboard events. +use crate::core; +use crate::core::keyboard::{Event, KeyCode, Modifiers}; +use crate::subscription::{self, Subscription}; +use crate::MaybeSend; + +/// Listens to keyboard key presses and calls the given function +/// map them into actual messages. +/// +/// If the function returns `None`, the key press will be simply +/// ignored. +pub fn on_key_press<Message>( +    f: fn(KeyCode, Modifiers) -> Option<Message>, +) -> Subscription<Message> +where +    Message: MaybeSend + 'static, +{ +    #[derive(Hash)] +    struct OnKeyPress; + +    subscription::filter_map((OnKeyPress, f), move |event, status| { +        match (event, status) { +            ( +                core::Event::Keyboard(Event::KeyPressed { +                    key_code, +                    modifiers, +                }), +                core::event::Status::Ignored, +            ) => f(key_code, modifiers), +            _ => None, +        } +    }) +} + +/// Listens to keyboard key releases and calls the given function +/// map them into actual messages. +/// +/// If the function returns `None`, the key release will be simply +/// ignored. +pub fn on_key_release<Message>( +    f: fn(KeyCode, Modifiers) -> Option<Message>, +) -> Subscription<Message> +where +    Message: MaybeSend + 'static, +{ +    #[derive(Hash)] +    struct OnKeyRelease; + +    subscription::filter_map((OnKeyRelease, f), move |event, status| { +        match (event, status) { +            ( +                core::Event::Keyboard(Event::KeyReleased { +                    key_code, +                    modifiers, +                }), +                core::event::Status::Ignored, +            ) => f(key_code, modifiers), +            _ => None, +        } +    }) +} diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 34d81e1e..abc46176 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -4,6 +4,7 @@  #![doc(      html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"  )] +#![forbid(unsafe_code, rust_2018_idioms)]  #![deny(      missing_debug_implementations,      missing_docs, @@ -12,9 +13,9 @@      clippy::from_over_into,      clippy::needless_borrow,      clippy::new_without_default, -    clippy::useless_conversion +    clippy::useless_conversion, +    rustdoc::broken_intra_doc_links  )] -#![forbid(unsafe_code, rust_2018_idioms)]  #![allow(clippy::inherent_to_string, clippy::type_complexity)]  #![cfg_attr(docsrs, feature(doc_auto_cfg))]  pub use futures; @@ -24,7 +25,9 @@ mod maybe_send;  mod runtime;  pub mod backend; +pub mod event;  pub mod executor; +pub mod keyboard;  pub mod subscription;  pub use executor::Executor; diff --git a/futures/src/runtime.rs b/futures/src/runtime.rs index 2241a494..16111b36 100644 --- a/futures/src/runtime.rs +++ b/futures/src/runtime.rs @@ -9,9 +9,9 @@ use std::marker::PhantomData;  /// A batteries-included runtime of commands and subscriptions.  ///  /// If you have an [`Executor`], a [`Runtime`] can be leveraged to run any -/// [`Command`] or [`Subscription`] and get notified of the results! +/// `Command` or [`Subscription`] and get notified of the results!  /// -/// [`Command`]: crate::Command +/// [`Subscription`]: crate::Subscription  #[derive(Debug)]  pub struct Runtime<Executor, Sender, Message> {      executor: Executor, @@ -75,6 +75,7 @@ where      /// [`Tracker::update`] to learn more about this!      ///      /// [`Tracker::update`]: subscription::Tracker::update +    /// [`Subscription`]: crate::Subscription      pub fn track(          &mut self,          recipes: impl IntoIterator< diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs index 8f78ce3a..d40125e3 100644 --- a/futures/src/subscription.rs +++ b/futures/src/subscription.rs @@ -4,7 +4,6 @@ mod tracker;  pub use tracker::Tracker;  use crate::core::event::{self, Event}; -use crate::core::window;  use crate::core::Hasher;  use crate::futures::{Future, Stream};  use crate::{BoxStream, MaybeSend}; @@ -20,16 +19,14 @@ pub type EventStream = BoxStream<(Event, event::Status)>;  /// A request to listen to external events.  /// -/// Besides performing async actions on demand with [`Command`], most +/// Besides performing async actions on demand with `Command`, most  /// applications also need to listen to external events passively.  /// -/// A [`Subscription`] is normally provided to some runtime, like a [`Command`], +/// A [`Subscription`] is normally provided to some runtime, like a `Command`,  /// and it will generate events as long as the user keeps requesting it.  ///  /// For instance, you can use a [`Subscription`] to listen to a WebSocket  /// connection, keyboard presses, mouse events, time ticks, etc. -/// -/// [`Command`]: crate::Command  #[must_use = "`Subscription` must be returned to runtime to take effect"]  pub struct Subscription<Message> {      recipes: Vec<Box<dyn Recipe<Output = Message>>>, @@ -215,77 +212,6 @@ where      }  } -/// Returns a [`Subscription`] to all the ignored runtime events. -/// -/// This subscription will notify your application of any [`Event`] that was -/// not captured by any widget. -pub fn events() -> Subscription<Event> { -    events_with(|event, status| match status { -        event::Status::Ignored => Some(event), -        event::Status::Captured => None, -    }) -} - -/// Returns a [`Subscription`] that filters all the runtime events with the -/// provided function, producing messages accordingly. -/// -/// This subscription will call the provided function for every [`Event`] -/// handled by the runtime. If the function: -/// -/// - Returns `None`, the [`Event`] will be discarded. -/// - Returns `Some` message, the `Message` will be produced. -pub fn events_with<Message>( -    f: fn(Event, event::Status) -> Option<Message>, -) -> Subscription<Message> -where -    Message: 'static + MaybeSend, -{ -    #[derive(Hash)] -    struct EventsWith; - -    Subscription::from_recipe(Runner { -        id: (EventsWith, f), -        spawn: move |events| { -            use futures::future; -            use futures::stream::StreamExt; - -            events.filter_map(move |(event, status)| { -                future::ready(match event { -                    Event::Window(window::Event::RedrawRequested(_)) => None, -                    _ => f(event, status), -                }) -            }) -        }, -    }) -} - -/// Returns a [`Subscription`] that produces a message for every runtime event, -/// including the redraw request events. -/// -/// **Warning:** This [`Subscription`], if unfiltered, may produce messages in -/// an infinite loop. -pub fn raw_events<Message>( -    f: fn(Event, event::Status) -> Option<Message>, -) -> Subscription<Message> -where -    Message: 'static + MaybeSend, -{ -    #[derive(Hash)] -    struct RawEvents; - -    Subscription::from_recipe(Runner { -        id: (RawEvents, f), -        spawn: move |events| { -            use futures::future; -            use futures::stream::StreamExt; - -            events.filter_map(move |(event, status)| { -                future::ready(f(event, status)) -            }) -        }, -    }) -} -  /// Returns a [`Subscription`] that will call the given function to create and  /// asynchronously run the given [`Stream`].  pub fn run<S, Message>(builder: fn() -> S) -> Subscription<Message> @@ -338,6 +264,25 @@ where      )  } +pub(crate) fn filter_map<I, F, Message>(id: I, f: F) -> Subscription<Message> +where +    I: Hash + 'static, +    F: Fn(Event, event::Status) -> Option<Message> + MaybeSend + 'static, +    Message: 'static + MaybeSend, +{ +    Subscription::from_recipe(Runner { +        id, +        spawn: |events| { +            use futures::future; +            use futures::stream::StreamExt; + +            events.filter_map(move |(event, status)| { +                future::ready(f(event, status)) +            }) +        }, +    }) +} +  /// Creates a [`Subscription`] that publishes the events sent from a [`Future`]  /// to an [`mpsc::Sender`] with the given bounds.  /// diff --git a/futures/src/subscription/tracker.rs b/futures/src/subscription/tracker.rs index ae71cd25..3a83da09 100644 --- a/futures/src/subscription/tracker.rs +++ b/futures/src/subscription/tracker.rs @@ -14,6 +14,8 @@ use std::hash::Hasher as _;  /// If you have an application that continuously returns a [`Subscription`],  /// you can use a [`Tracker`] to keep track of the different recipes and keep  /// its executions alive. +/// +/// [`Subscription`]: crate::Subscription  #[derive(Debug, Default)]  pub struct Tracker {      subscriptions: HashMap<u64, Execution>, @@ -51,6 +53,7 @@ impl Tracker {      /// the [`Tracker`] changes.      ///      /// [`Recipe`]: crate::subscription::Recipe +    /// [`Subscription`]: crate::Subscription      pub fn update<Message, Receiver>(          &mut self,          recipes: impl Iterator<Item = Box<dyn Recipe<Output = Message>>>, | 
