From e400f972c1fe6fa4f70f8cfe559ded680e6cf740 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Tue, 4 Jun 2024 23:20:33 +0200
Subject: Introduce `window::Id` to `Event` subscriptions

And remove `window::Id` from `Event` altogether.
---
 futures/src/event.rs                | 19 +++++++++----------
 futures/src/keyboard.rs             | 11 ++++++-----
 futures/src/runtime.rs              | 10 ++++++++--
 futures/src/subscription.rs         | 11 +++++++----
 futures/src/subscription/tracker.rs | 16 +++++++++++++---
 5 files changed, 43 insertions(+), 24 deletions(-)

(limited to 'futures/src')

diff --git a/futures/src/event.rs b/futures/src/event.rs
index 97224506..4f3342ca 100644
--- a/futures/src/event.rs
+++ b/futures/src/event.rs
@@ -9,7 +9,7 @@ use crate::MaybeSend;
 /// 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 {
+    listen_with(|event, status, _window| match status {
         event::Status::Ignored => Some(event),
         event::Status::Captured => None,
     })
@@ -24,7 +24,7 @@ pub fn listen() -> Subscription<Event> {
 /// - 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>,
+    f: fn(Event, event::Status, window::Id) -> Option<Message>,
 ) -> Subscription<Message>
 where
     Message: 'static + MaybeSend,
@@ -32,13 +32,12 @@ where
     #[derive(Hash)]
     struct EventsWith;
 
-    subscription::filter_map(
-        (EventsWith, f),
-        move |event, status| match event {
-            Event::Window(_, window::Event::RedrawRequested(_)) => None,
-            _ => f(event, status),
-        },
-    )
+    subscription::filter_map((EventsWith, f), move |event, status, window| {
+        match event {
+            Event::Window(window::Event::RedrawRequested(_)) => None,
+            _ => f(event, status, window),
+        }
+    })
 }
 
 /// Creates a [`Subscription`] that produces a message for every runtime event,
@@ -47,7 +46,7 @@ where
 /// **Warning:** This [`Subscription`], if unfiltered, may produce messages in
 /// an infinite loop.
 pub fn listen_raw<Message>(
-    f: fn(Event, event::Status) -> Option<Message>,
+    f: fn(Event, event::Status, window::Id) -> Option<Message>,
 ) -> Subscription<Message>
 where
     Message: 'static + MaybeSend,
diff --git a/futures/src/keyboard.rs b/futures/src/keyboard.rs
index 8e7da38f..43ed7742 100644
--- a/futures/src/keyboard.rs
+++ b/futures/src/keyboard.rs
@@ -18,7 +18,7 @@ where
     #[derive(Hash)]
     struct OnKeyPress;
 
-    subscription::filter_map((OnKeyPress, f), move |event, status| {
+    subscription::filter_map((OnKeyPress, f), move |event, status, _window| {
         match (event, status) {
             (
                 core::Event::Keyboard(Event::KeyPressed {
@@ -45,8 +45,9 @@ where
     #[derive(Hash)]
     struct OnKeyRelease;
 
-    subscription::filter_map((OnKeyRelease, f), move |event, status| {
-        match (event, status) {
+    subscription::filter_map(
+        (OnKeyRelease, f),
+        move |event, status, _window| match (event, status) {
             (
                 core::Event::Keyboard(Event::KeyReleased {
                     key,
@@ -56,6 +57,6 @@ where
                 core::event::Status::Ignored,
             ) => f(key, modifiers),
             _ => None,
-        }
-    })
+        },
+    )
 }
diff --git a/futures/src/runtime.rs b/futures/src/runtime.rs
index cac7b7e1..ae55f814 100644
--- a/futures/src/runtime.rs
+++ b/futures/src/runtime.rs
@@ -1,5 +1,6 @@
 //! Run commands and keep track of subscriptions.
 use crate::core::event::{self, Event};
+use crate::core::window;
 use crate::subscription;
 use crate::{BoxFuture, BoxStream, Executor, MaybeSend};
 
@@ -127,7 +128,12 @@ where
     /// See [`Tracker::broadcast`] to learn more.
     ///
     /// [`Tracker::broadcast`]: subscription::Tracker::broadcast
-    pub fn broadcast(&mut self, event: Event, status: event::Status) {
-        self.subscriptions.broadcast(event, status);
+    pub fn broadcast(
+        &mut self,
+        event: Event,
+        status: event::Status,
+        window: window::Id,
+    ) {
+        self.subscriptions.broadcast(event, status, window);
     }
 }
diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs
index 93e35608..79cea6ed 100644
--- a/futures/src/subscription.rs
+++ b/futures/src/subscription.rs
@@ -4,6 +4,7 @@ mod tracker;
 pub use tracker::Tracker;
 
 use crate::core::event::{self, Event};
+use crate::core::window;
 use crate::futures::{Future, Stream};
 use crate::{BoxStream, MaybeSend};
 
@@ -15,7 +16,7 @@ use std::hash::Hash;
 /// A stream of runtime events.
 ///
 /// It is the input of a [`Subscription`].
-pub type EventStream = BoxStream<(Event, event::Status)>;
+pub type EventStream = BoxStream<(Event, event::Status, window::Id)>;
 
 /// The hasher used for identifying subscriptions.
 pub type Hasher = rustc_hash::FxHasher;
@@ -289,7 +290,9 @@ 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,
+    F: Fn(Event, event::Status, window::Id) -> Option<Message>
+        + MaybeSend
+        + 'static,
     Message: 'static + MaybeSend,
 {
     Subscription::from_recipe(Runner {
@@ -298,8 +301,8 @@ where
             use futures::future;
             use futures::stream::StreamExt;
 
-            events.filter_map(move |(event, status)| {
-                future::ready(f(event, status))
+            events.filter_map(move |(event, status, window)| {
+                future::ready(f(event, status, window))
             })
         },
     })
diff --git a/futures/src/subscription/tracker.rs b/futures/src/subscription/tracker.rs
index 277a446b..086b0f09 100644
--- a/futures/src/subscription/tracker.rs
+++ b/futures/src/subscription/tracker.rs
@@ -1,4 +1,5 @@
 use crate::core::event::{self, Event};
+use crate::core::window;
 use crate::subscription::{Hasher, Recipe};
 use crate::{BoxFuture, MaybeSend};
 
@@ -23,7 +24,9 @@ pub struct Tracker {
 #[derive(Debug)]
 pub struct Execution {
     _cancel: futures::channel::oneshot::Sender<()>,
-    listener: Option<futures::channel::mpsc::Sender<(Event, event::Status)>>,
+    listener: Option<
+        futures::channel::mpsc::Sender<(Event, event::Status, window::Id)>,
+    >,
 }
 
 impl Tracker {
@@ -139,12 +142,19 @@ impl Tracker {
     /// currently open.
     ///
     /// [`Recipe::stream`]: crate::subscription::Recipe::stream
-    pub fn broadcast(&mut self, event: Event, status: event::Status) {
+    pub fn broadcast(
+        &mut self,
+        event: Event,
+        status: event::Status,
+        window: window::Id,
+    ) {
         self.subscriptions
             .values_mut()
             .filter_map(|connection| connection.listener.as_mut())
             .for_each(|listener| {
-                if let Err(error) = listener.try_send((event.clone(), status)) {
+                if let Err(error) =
+                    listener.try_send((event.clone(), status, window))
+                {
                     log::warn!(
                         "Error sending event to subscription: {error:?}"
                     );
-- 
cgit 


From 5d7dcf417c694853a606b8fb0a47a580277fc9c0 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Tue, 11 Jun 2024 19:41:05 +0200
Subject: Introduce `subscription::Event`

... and remove `PlatformSpecific` from `Event`
---
 futures/src/event.rs                | 43 +++++++++++++++++++++++++++----
 futures/src/keyboard.rs             | 42 +++++++++++++------------------
 futures/src/runtime.rs              | 11 ++------
 futures/src/subscription.rs         | 50 +++++++++++++++++++++++++++++++------
 futures/src/subscription/tracker.rs | 19 +++-----------
 5 files changed, 104 insertions(+), 61 deletions(-)

(limited to 'futures/src')

diff --git a/futures/src/event.rs b/futures/src/event.rs
index 4f3342ca..72ea78ad 100644
--- a/futures/src/event.rs
+++ b/futures/src/event.rs
@@ -32,11 +32,17 @@ where
     #[derive(Hash)]
     struct EventsWith;
 
-    subscription::filter_map((EventsWith, f), move |event, status, window| {
-        match event {
-            Event::Window(window::Event::RedrawRequested(_)) => None,
-            _ => f(event, status, window),
+    subscription::filter_map((EventsWith, f), move |event| match event {
+        subscription::Event::Interaction {
+            event: Event::Window(window::Event::RedrawRequested(_)),
+            ..
         }
+        | subscription::Event::PlatformSpecific(_) => None,
+        subscription::Event::Interaction {
+            window,
+            event,
+            status,
+        } => f(event, status, window),
     })
 }
 
@@ -54,5 +60,32 @@ where
     #[derive(Hash)]
     struct RawEvents;
 
-    subscription::filter_map((RawEvents, f), f)
+    subscription::filter_map((RawEvents, f), move |event| match event {
+        subscription::Event::Interaction {
+            window,
+            event,
+            status,
+        } => f(event, status, window),
+        subscription::Event::PlatformSpecific(_) => None,
+    })
+}
+
+/// Creates a [`Subscription`] that notifies of custom application URL
+/// received from the system.
+///
+/// _**Note:** Currently, it only triggers on macOS and the executable needs to be properly [bundled]!_
+///
+/// [bundled]: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW19
+pub fn listen_url() -> Subscription<String> {
+    #[derive(Hash)]
+    struct ListenUrl;
+
+    subscription::filter_map(ListenUrl, move |event| match event {
+        subscription::Event::PlatformSpecific(
+            subscription::PlatformSpecific::MacOS(
+                subscription::MacOS::ReceivedUrl(url),
+            ),
+        ) => Some(url),
+        _ => None,
+    })
 }
diff --git a/futures/src/keyboard.rs b/futures/src/keyboard.rs
index 43ed7742..f0d7d757 100644
--- a/futures/src/keyboard.rs
+++ b/futures/src/keyboard.rs
@@ -1,5 +1,6 @@
 //! Listen to keyboard events.
 use crate::core;
+use crate::core::event;
 use crate::core::keyboard::{Event, Key, Modifiers};
 use crate::subscription::{self, Subscription};
 use crate::MaybeSend;
@@ -18,16 +19,14 @@ where
     #[derive(Hash)]
     struct OnKeyPress;
 
-    subscription::filter_map((OnKeyPress, f), move |event, status, _window| {
-        match (event, status) {
-            (
-                core::Event::Keyboard(Event::KeyPressed {
-                    key, modifiers, ..
-                }),
-                core::event::Status::Ignored,
-            ) => f(key, modifiers),
-            _ => None,
-        }
+    subscription::filter_map((OnKeyPress, f), move |event| match event {
+        subscription::Event::Interaction {
+            event:
+                core::Event::Keyboard(Event::KeyPressed { key, modifiers, .. }),
+            status: event::Status::Ignored,
+            ..
+        } => f(key, modifiers),
+        _ => None,
     })
 }
 
@@ -45,18 +44,13 @@ where
     #[derive(Hash)]
     struct OnKeyRelease;
 
-    subscription::filter_map(
-        (OnKeyRelease, f),
-        move |event, status, _window| match (event, status) {
-            (
-                core::Event::Keyboard(Event::KeyReleased {
-                    key,
-                    modifiers,
-                    ..
-                }),
-                core::event::Status::Ignored,
-            ) => f(key, modifiers),
-            _ => None,
-        },
-    )
+    subscription::filter_map((OnKeyRelease, f), move |event| match event {
+        subscription::Event::Interaction {
+            event:
+                core::Event::Keyboard(Event::KeyReleased { key, modifiers, .. }),
+            status: event::Status::Ignored,
+            ..
+        } => f(key, modifiers),
+        _ => None,
+    })
 }
diff --git a/futures/src/runtime.rs b/futures/src/runtime.rs
index ae55f814..157e2c67 100644
--- a/futures/src/runtime.rs
+++ b/futures/src/runtime.rs
@@ -1,6 +1,4 @@
 //! Run commands and keep track of subscriptions.
-use crate::core::event::{self, Event};
-use crate::core::window;
 use crate::subscription;
 use crate::{BoxFuture, BoxStream, Executor, MaybeSend};
 
@@ -128,12 +126,7 @@ where
     /// See [`Tracker::broadcast`] to learn more.
     ///
     /// [`Tracker::broadcast`]: subscription::Tracker::broadcast
-    pub fn broadcast(
-        &mut self,
-        event: Event,
-        status: event::Status,
-        window: window::Id,
-    ) {
-        self.subscriptions.broadcast(event, status, window);
+    pub fn broadcast(&mut self, event: subscription::Event) {
+        self.subscriptions.broadcast(event);
     }
 }
diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs
index 79cea6ed..316fc44d 100644
--- a/futures/src/subscription.rs
+++ b/futures/src/subscription.rs
@@ -3,7 +3,7 @@ mod tracker;
 
 pub use tracker::Tracker;
 
-use crate::core::event::{self, Event};
+use crate::core::event;
 use crate::core::window;
 use crate::futures::{Future, Stream};
 use crate::{BoxStream, MaybeSend};
@@ -13,10 +13,48 @@ use futures::never::Never;
 use std::any::TypeId;
 use std::hash::Hash;
 
+/// A subscription event.
+#[derive(Debug, Clone, PartialEq)]
+pub enum Event {
+    /// A user interacted with a user interface in a window.
+    Interaction {
+        /// The window holding the interface of the interaction.
+        window: window::Id,
+        /// The [`Event`] describing the interaction.
+        ///
+        /// [`Event`]: event::Event
+        event: event::Event,
+
+        /// The [`event::Status`] of the interaction.
+        status: event::Status,
+    },
+
+    /// A platform specific event.
+    PlatformSpecific(PlatformSpecific),
+}
+
+/// A platform specific event
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum PlatformSpecific {
+    /// A MacOS specific event
+    MacOS(MacOS),
+}
+
+/// Describes an event specific to MacOS
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum MacOS {
+    /// Triggered when the app receives an URL from the system
+    ///
+    /// _**Note:** For this event to be triggered, the executable needs to be properly [bundled]!_
+    ///
+    /// [bundled]: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW19
+    ReceivedUrl(String),
+}
+
 /// A stream of runtime events.
 ///
 /// It is the input of a [`Subscription`].
-pub type EventStream = BoxStream<(Event, event::Status, window::Id)>;
+pub type EventStream = BoxStream<Event>;
 
 /// The hasher used for identifying subscriptions.
 pub type Hasher = rustc_hash::FxHasher;
@@ -290,9 +328,7 @@ where
 pub(crate) fn filter_map<I, F, Message>(id: I, f: F) -> Subscription<Message>
 where
     I: Hash + 'static,
-    F: Fn(Event, event::Status, window::Id) -> Option<Message>
-        + MaybeSend
-        + 'static,
+    F: Fn(Event) -> Option<Message> + MaybeSend + 'static,
     Message: 'static + MaybeSend,
 {
     Subscription::from_recipe(Runner {
@@ -301,9 +337,7 @@ where
             use futures::future;
             use futures::stream::StreamExt;
 
-            events.filter_map(move |(event, status, window)| {
-                future::ready(f(event, status, window))
-            })
+            events.filter_map(move |event| future::ready(f(event)))
         },
     })
 }
diff --git a/futures/src/subscription/tracker.rs b/futures/src/subscription/tracker.rs
index 086b0f09..f17e3ea3 100644
--- a/futures/src/subscription/tracker.rs
+++ b/futures/src/subscription/tracker.rs
@@ -1,6 +1,4 @@
-use crate::core::event::{self, Event};
-use crate::core::window;
-use crate::subscription::{Hasher, Recipe};
+use crate::subscription::{Event, Hasher, Recipe};
 use crate::{BoxFuture, MaybeSend};
 
 use futures::channel::mpsc;
@@ -24,9 +22,7 @@ pub struct Tracker {
 #[derive(Debug)]
 pub struct Execution {
     _cancel: futures::channel::oneshot::Sender<()>,
-    listener: Option<
-        futures::channel::mpsc::Sender<(Event, event::Status, window::Id)>,
-    >,
+    listener: Option<futures::channel::mpsc::Sender<Event>>,
 }
 
 impl Tracker {
@@ -142,19 +138,12 @@ impl Tracker {
     /// currently open.
     ///
     /// [`Recipe::stream`]: crate::subscription::Recipe::stream
-    pub fn broadcast(
-        &mut self,
-        event: Event,
-        status: event::Status,
-        window: window::Id,
-    ) {
+    pub fn broadcast(&mut self, event: Event) {
         self.subscriptions
             .values_mut()
             .filter_map(|connection| connection.listener.as_mut())
             .for_each(|listener| {
-                if let Err(error) =
-                    listener.try_send((event.clone(), status, window))
-                {
+                if let Err(error) = listener.try_send(event.clone()) {
                     log::warn!(
                         "Error sending event to subscription: {error:?}"
                     );
-- 
cgit