From cdb7acf6c20fe13a09e75ea1c47d53ced6174698 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 10 Dec 2019 03:43:00 +0100 Subject: Implement `Subscription::map` and `from_recipe` --- core/src/subscription.rs | 117 +++++++++++++++++++++++++++++++++++---------- examples/events.rs | 38 ++++++--------- native/src/subscription.rs | 6 +-- winit/src/application.rs | 20 +++++--- 4 files changed, 123 insertions(+), 58 deletions(-) diff --git a/core/src/subscription.rs b/core/src/subscription.rs index 4c021d75..e9559f3c 100644 --- a/core/src/subscription.rs +++ b/core/src/subscription.rs @@ -1,60 +1,125 @@ //! Generate events asynchronously for you application. /// An event subscription. -pub struct Subscription { - connections: Vec>>, +pub struct Subscription { + recipes: Vec>>, } -impl Subscription { +impl Subscription +where + H: std::hash::Hasher, +{ pub fn none() -> Self { Self { - connections: Vec::new(), + recipes: Vec::new(), + } + } + + pub fn from_recipe( + recipe: impl Recipe + 'static, + ) -> Self { + Self { + recipes: vec![Box::new(recipe)], } } pub fn batch( - subscriptions: impl Iterator>, + subscriptions: impl Iterator>, ) -> Self { Self { - connections: subscriptions - .flat_map(|subscription| subscription.connections) + recipes: subscriptions + .flat_map(|subscription| subscription.recipes) .collect(), } } - pub fn connections( - self, - ) -> Vec>> { - self.connections + pub fn recipes(self) -> Vec>> { + self.recipes } -} -impl From for Subscription -where - T: Connection + 'static, -{ - fn from(handle: T) -> Self { - Self { - connections: vec![Box::new(handle)], + pub fn map( + mut self, + f: impl Fn(O) -> A + Send + Sync + 'static, + ) -> Subscription + where + H: 'static, + I: 'static, + O: 'static, + A: 'static, + { + let function = std::sync::Arc::new(f); + + Subscription { + recipes: self + .recipes + .drain(..) + .map(|recipe| { + Box::new(Map::new(recipe, function.clone())) + as Box> + }) + .collect(), } } } +impl std::fmt::Debug for Subscription { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Subscription").finish() + } +} + /// The connection of an event subscription. -pub trait Connection { - type Input; +pub trait Recipe { type Output; - fn id(&self) -> u64; + fn hash(&self, state: &mut Hasher); fn stream( &self, - input: Self::Input, + input: Input, ) -> futures::stream::BoxStream<'static, Self::Output>; } -impl std::fmt::Debug for Subscription { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Subscription").finish() +struct Map { + recipe: Box>, + mapper: std::sync::Arc B + Send + Sync>, +} + +impl Map { + fn new( + recipe: Box>, + mapper: std::sync::Arc B + Send + Sync + 'static>, + ) -> Self { + Map { recipe, mapper } + } +} + +impl Recipe for Map +where + A: 'static, + B: 'static, + H: std::hash::Hasher, +{ + type Output = B; + + fn hash(&self, state: &mut H) { + use std::hash::Hash; + + std::any::TypeId::of::().hash(state); + self.recipe.hash(state); + } + + fn stream( + &self, + input: I, + ) -> futures::stream::BoxStream<'static, Self::Output> { + use futures::StreamExt; + + let mapper = self.mapper.clone(); + + self.recipe + .stream(input) + .map(move |element| mapper(element)) + .boxed() } } diff --git a/examples/events.rs b/examples/events.rs index 290aa975..10758519 100644 --- a/examples/events.rs +++ b/examples/events.rs @@ -51,7 +51,7 @@ impl Application for Events { fn subscriptions(&self) -> Subscription { if self.enabled { - events::all(Message::EventOccurred) + events::all().map(Message::EventOccurred) } else { Subscription::none() } @@ -89,41 +89,33 @@ impl Application for Events { } mod events { - use std::sync::Arc; - - pub fn all( - f: impl Fn(iced_native::Event) -> Message + 'static + Send + Sync, - ) -> iced::Subscription - where - Message: Send + 'static, - { - All(Arc::new(f)).into() + pub fn all() -> iced::Subscription { + iced::Subscription::from_recipe(All) } - struct All( - Arc Message + Send + Sync>, - ); + struct All; - impl iced_native::subscription::Connection for All + impl + iced_native::subscription::Recipe + for All where - Message: 'static, + H: std::hash::Hasher, { - type Input = iced_native::subscription::Input; - type Output = Message; + type Output = iced_native::Event; - fn id(&self) -> u64 { - 0 + fn hash(&self, state: &mut H) { + use std::hash::Hash; + + std::any::TypeId::of::().hash(state); } fn stream( &self, input: iced_native::subscription::Input, - ) -> futures::stream::BoxStream<'static, Message> { + ) -> futures::stream::BoxStream<'static, Self::Output> { use futures::StreamExt; - let function = self.0.clone(); - - input.map(move |event| function(event)).boxed() + input.boxed() } } } diff --git a/native/src/subscription.rs b/native/src/subscription.rs index 5fa026a2..4d000490 100644 --- a/native/src/subscription.rs +++ b/native/src/subscription.rs @@ -1,6 +1,6 @@ -use crate::Event; +use crate::{Event, Hasher}; -pub type Subscription = iced_core::Subscription; +pub type Subscription = iced_core::Subscription; pub type Input = futures::channel::mpsc::Receiver; -pub use iced_core::subscription::Connection; +pub use iced_core::subscription::Recipe; diff --git a/winit/src/application.rs b/winit/src/application.rs index 49a01320..fb31c44f 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -2,8 +2,8 @@ use crate::{ conversion, input::{keyboard, mouse}, renderer::{Target, Windowed}, - Cache, Command, Container, Debug, Element, Event, Length, MouseCursor, - Settings, Subscription, UserInterface, + Cache, Command, Container, Debug, Element, Event, Hasher, Length, + MouseCursor, Settings, Subscription, UserInterface, }; use std::collections::HashMap; @@ -448,11 +448,19 @@ impl Subscriptions { ) { use futures::{future::FutureExt, stream::StreamExt}; - let connections = subscriptions.connections(); + let recipes = subscriptions.recipes(); let mut alive = std::collections::HashSet::new(); - for connection in connections { - let id = connection.id(); + for recipe in recipes { + let id = { + use std::hash::Hasher as _; + + let mut hasher = Hasher::default(); + recipe.hash(&mut hasher); + + hasher.finish() + }; + let _ = alive.insert(id); if !self.alive.contains_key(&id) { @@ -460,7 +468,7 @@ impl Subscriptions { let (event_sender, event_receiver) = futures::channel::mpsc::channel(100); - let stream = connection.stream(event_receiver); + let stream = recipe.stream(event_receiver); let proxy = std::sync::Arc::new(std::sync::Mutex::new(proxy.clone())); -- cgit