summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2019-12-05 06:10:13 +0100
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2019-12-05 06:10:13 +0100
commitd575f4541126e2ab25908fe55c6805f16716b2a5 (patch)
tree9d5221fe201b64e2ec649228ebae26be819dbf58
parente92ea48e8814b42fc566017db085ca9bdaf3c272 (diff)
downloadiced-d575f4541126e2ab25908fe55c6805f16716b2a5.tar.gz
iced-d575f4541126e2ab25908fe55c6805f16716b2a5.tar.bz2
iced-d575f4541126e2ab25908fe55c6805f16716b2a5.zip
Draft first version of event subscriptions :tada:
Diffstat (limited to '')
-rw-r--r--Cargo.toml2
-rw-r--r--core/Cargo.toml2
-rw-r--r--core/src/lib.rs8
-rw-r--r--core/src/subscription.rs61
-rw-r--r--native/Cargo.toml2
-rw-r--r--native/src/lib.rs4
-rw-r--r--native/src/widget/checkbox.rs15
-rw-r--r--src/application.rs11
-rw-r--r--src/native.rs4
-rw-r--r--src/sandbox.rs6
-rw-r--r--winit/src/application.rs79
11 files changed, 182 insertions, 12 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 888a1c07..a07da082 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -41,6 +41,8 @@ serde_json = "1.0"
directories = "2.0"
reqwest = "0.9"
rand = "0.7"
+chrono = "0.4"
+futures = "0.3"
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen = "0.2.51"
diff --git a/core/Cargo.toml b/core/Cargo.toml
index c623ba78..0a8fd8ef 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -10,6 +10,8 @@ repository = "https://github.com/hecrj/iced"
[features]
# Exposes a future-based `Command` type
command = ["futures"]
+# Exposes a future-based `Subscription` type
+subscription = ["futures"]
[dependencies]
futures = { version = "0.3", optional = true }
diff --git a/core/src/lib.rs b/core/src/lib.rs
index 65304e8b..6f13c310 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -9,7 +9,7 @@
//! [Iced]: https://github.com/hecrj/iced
//! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
//! [`iced_web`]: https://github.com/hecrj/iced/tree/master/web
-#![deny(missing_docs)]
+//#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(unused_results)]
#![deny(unsafe_code)]
@@ -38,3 +38,9 @@ mod command;
#[cfg(feature = "command")]
pub use command::Command;
+
+#[cfg(feature = "subscription")]
+pub mod subscription;
+
+#[cfg(feature = "subscription")]
+pub use subscription::Subscription;
diff --git a/core/src/subscription.rs b/core/src/subscription.rs
new file mode 100644
index 00000000..1e6695d6
--- /dev/null
+++ b/core/src/subscription.rs
@@ -0,0 +1,61 @@
+//! Generate events asynchronously for you application.
+
+/// An event subscription.
+pub struct Subscription<T> {
+ definitions: Vec<Box<dyn Definition<Message = T>>>,
+}
+
+impl<T> Subscription<T> {
+ pub fn none() -> Self {
+ Self {
+ definitions: Vec::new(),
+ }
+ }
+
+ pub fn batch(subscriptions: impl Iterator<Item = Subscription<T>>) -> Self {
+ Self {
+ definitions: subscriptions
+ .flat_map(|subscription| subscription.definitions)
+ .collect(),
+ }
+ }
+
+ pub fn definitions(self) -> Vec<Box<dyn Definition<Message = T>>> {
+ self.definitions
+ }
+}
+
+impl<T, A> From<A> for Subscription<T>
+where
+ A: Definition<Message = T> + 'static,
+{
+ fn from(definition: A) -> Self {
+ Self {
+ definitions: vec![Box::new(definition)],
+ }
+ }
+}
+
+/// The definition of an event subscription.
+pub trait Definition {
+ type Message;
+
+ fn id(&self) -> u64;
+
+ fn stream(
+ &self,
+ ) -> (
+ futures::stream::BoxStream<'static, Self::Message>,
+ Box<dyn Handle>,
+ );
+}
+
+pub trait Handle {
+ fn cancel(&mut self);
+}
+
+impl<T> std::fmt::Debug for Subscription<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Command").finish()
+ }
+}
diff --git a/native/Cargo.toml b/native/Cargo.toml
index 7993676e..c5f9c7ac 100644
--- a/native/Cargo.toml
+++ b/native/Cargo.toml
@@ -8,6 +8,6 @@ license = "MIT"
repository = "https://github.com/hecrj/iced"
[dependencies]
-iced_core = { version = "0.1.0", path = "../core", features = ["command"] }
+iced_core = { version = "0.1.0", path = "../core", features = ["command", "subscription"] }
twox-hash = "1.5"
raw-window-handle = "0.3"
diff --git a/native/src/lib.rs b/native/src/lib.rs
index 45c3c699..9afb3bc9 100644
--- a/native/src/lib.rs
+++ b/native/src/lib.rs
@@ -52,8 +52,8 @@ mod size;
mod user_interface;
pub use iced_core::{
- Align, Background, Color, Command, Font, HorizontalAlignment, Length,
- Point, Rectangle, Vector, VerticalAlignment,
+ subscription, Align, Background, Color, Command, Font, HorizontalAlignment,
+ Length, Point, Rectangle, Subscription, Vector, VerticalAlignment,
};
pub use element::Element;
diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs
index 9563291c..159cba84 100644
--- a/native/src/widget/checkbox.rs
+++ b/native/src/widget/checkbox.rs
@@ -31,6 +31,7 @@ pub struct Checkbox<Message> {
on_toggle: Box<dyn Fn(bool) -> Message>,
label: String,
label_color: Option<Color>,
+ width: Length,
}
impl<Message> Checkbox<Message> {
@@ -53,6 +54,7 @@ impl<Message> Checkbox<Message> {
on_toggle: Box::new(f),
label: String::from(label),
label_color: None,
+ width: Length::Fill,
}
}
@@ -63,6 +65,14 @@ impl<Message> Checkbox<Message> {
self.label_color = Some(color.into());
self
}
+
+ /// Sets the width of the [`Checkbox`].
+ ///
+ /// [`Checkbox`]: struct.Checkbox.html
+ pub fn width(mut self, width: Length) -> Self {
+ self.width = width;
+ self
+ }
}
impl<Message, Renderer> Widget<Message, Renderer> for Checkbox<Message>
@@ -70,7 +80,7 @@ where
Renderer: self::Renderer + text::Renderer + row::Renderer,
{
fn width(&self) -> Length {
- Length::Fill
+ Length::Shrink
}
fn height(&self) -> Length {
@@ -85,6 +95,7 @@ where
let size = self::Renderer::default_size(renderer);
Row::<(), Renderer>::new()
+ .width(self.width)
.spacing(15)
.align_items(Align::Center)
.push(
@@ -92,7 +103,7 @@ where
.width(Length::Units(size as u16))
.height(Length::Units(size as u16)),
)
- .push(Text::new(&self.label))
+ .push(Text::new(&self.label).width(self.width))
.layout(renderer, limits)
}
diff --git a/src/application.rs b/src/application.rs
index a4d20e68..95113344 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -1,4 +1,4 @@
-use crate::{Command, Element, Settings};
+use crate::{Command, Element, Settings, Subscription};
/// An interactive cross-platform application.
///
@@ -117,6 +117,11 @@ pub trait Application: Sized {
/// [`Command`]: struct.Command.html
fn update(&mut self, message: Self::Message) -> Command<Self::Message>;
+ /// TODO
+ fn subscriptions(&self) -> Subscription<Self::Message> {
+ Subscription::none()
+ }
+
/// Returns the widgets to display in the [`Application`].
///
/// These widgets can produce __messages__ based on user interaction.
@@ -168,6 +173,10 @@ where
self.0.update(message)
}
+ fn subscriptions(&self) -> Subscription<Self::Message> {
+ self.0.subscriptions()
+ }
+
fn view(&mut self) -> Element<'_, Self::Message> {
self.0.view()
}
diff --git a/src/native.rs b/src/native.rs
index 3537dd52..e3733955 100644
--- a/src/native.rs
+++ b/src/native.rs
@@ -1,6 +1,6 @@
pub use iced_winit::{
- Align, Background, Color, Command, Font, HorizontalAlignment, Length,
- VerticalAlignment,
+ subscription, Align, Background, Color, Command, Font, HorizontalAlignment,
+ Length, Subscription, VerticalAlignment,
};
pub mod widget {
diff --git a/src/sandbox.rs b/src/sandbox.rs
index acf7f5e0..248aa152 100644
--- a/src/sandbox.rs
+++ b/src/sandbox.rs
@@ -1,4 +1,4 @@
-use crate::{Application, Command, Element, Settings};
+use crate::{Application, Command, Element, Settings, Subscription};
/// A sandboxed [`Application`].
///
@@ -149,6 +149,10 @@ where
Command::none()
}
+ fn subscriptions(&self) -> Subscription<T::Message> {
+ Subscription::none()
+ }
+
fn view(&mut self) -> Element<'_, T::Message> {
T::view(self)
}
diff --git a/winit/src/application.rs b/winit/src/application.rs
index 3772a667..959e142d 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -2,9 +2,10 @@ use crate::{
conversion,
input::{keyboard, mouse},
renderer::{Target, Windowed},
- Cache, Command, Container, Debug, Element, Event, Length, MouseCursor,
- Settings, UserInterface,
+ subscription, Cache, Command, Container, Debug, Element, Event, Length,
+ MouseCursor, Settings, Subscription, UserInterface,
};
+use std::collections::HashMap;
/// An interactive, native cross-platform application.
///
@@ -57,6 +58,9 @@ pub trait Application: Sized {
/// [`Command`]: struct.Command.html
fn update(&mut self, message: Self::Message) -> Command<Self::Message>;
+ /// TODO
+ fn subscriptions(&self) -> Subscription<Self::Message>;
+
/// Returns the widgets to display in the [`Application`].
///
/// These widgets can produce __messages__ based on user interaction.
@@ -89,11 +93,15 @@ pub trait Application: Sized {
let proxy = event_loop.create_proxy();
let mut thread_pool =
futures::executor::ThreadPool::new().expect("Create thread pool");
+ let mut alive_subscriptions = Subscriptions::new();
let mut external_messages = Vec::new();
let (mut application, init_command) = Self::new();
spawn(init_command, &mut thread_pool, &proxy);
+ let subscriptions = application.subscriptions();
+ alive_subscriptions.update(subscriptions, &mut thread_pool, &proxy);
+
let mut title = application.title();
let window = {
@@ -204,6 +212,13 @@ pub trait Application: Sized {
debug.update_finished();
}
+ let subscriptions = application.subscriptions();
+ alive_subscriptions.update(
+ subscriptions,
+ &mut thread_pool,
+ &proxy,
+ );
+
// Update window title
let new_title = application.title();
@@ -404,6 +419,66 @@ fn spawn<Message: Send>(
}
}
+pub struct Subscriptions {
+ alive: HashMap<u64, Box<dyn subscription::Handle>>,
+}
+
+impl Subscriptions {
+ fn new() -> Self {
+ Self {
+ alive: HashMap::new(),
+ }
+ }
+
+ fn update<Message: Send>(
+ &mut self,
+ subscriptions: Subscription<Message>,
+ thread_pool: &mut futures::executor::ThreadPool,
+ proxy: &winit::event_loop::EventLoopProxy<Message>,
+ ) {
+ use futures::stream::StreamExt;
+
+ let definitions = subscriptions.definitions();
+ let mut alive = std::collections::HashSet::new();
+
+ for definition in definitions {
+ let id = definition.id();
+ let _ = alive.insert(id);
+
+ if !self.alive.contains_key(&id) {
+ let (stream, handle) = definition.stream();
+
+ let proxy =
+ std::sync::Arc::new(std::sync::Mutex::new(proxy.clone()));
+
+ let future = stream.for_each(move |message| {
+ proxy
+ .lock()
+ .expect("Acquire event loop proxy lock")
+ .send_event(message)
+ .expect("Send subscription result to event loop");
+
+ futures::future::ready(())
+ });
+
+ thread_pool.spawn_ok(future);
+
+ let _ = self.alive.insert(id, handle);
+ }
+ }
+
+ self.alive.retain(|id, handle| {
+ let is_still_alive = alive.contains(&id);
+
+ if !is_still_alive {
+ handle.cancel();
+ }
+
+ is_still_alive
+ });
+ }
+}
+
// As defined in: http://www.unicode.org/faq/private_use.html
// TODO: Remove once https://github.com/rust-windowing/winit/pull/1254 lands
fn is_private_use_character(c: char) -> bool {