summaryrefslogtreecommitdiffstats
path: root/web/src/lib.rs
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2020-02-04 03:28:47 +0100
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2020-02-04 03:28:47 +0100
commit6d46833eb2a068bd3655859ea828dad04293e5ba (patch)
tree42cbe1d9a65a2e03e63887611251ed8532f49872 /web/src/lib.rs
parentf5186f31f1e5eed8fe20c5d6e62e2f531fee6365 (diff)
downloadiced-6d46833eb2a068bd3655859ea828dad04293e5ba.tar.gz
iced-6d46833eb2a068bd3655859ea828dad04293e5ba.tar.bz2
iced-6d46833eb2a068bd3655859ea828dad04293e5ba.zip
Support event subscriptions in `iced_web`
Also improves the overall web runtime, avoiding nested update loops.
Diffstat (limited to 'web/src/lib.rs')
-rw-r--r--web/src/lib.rs136
1 files changed, 64 insertions, 72 deletions
diff --git a/web/src/lib.rs b/web/src/lib.rs
index 1b265bc9..0b9c0c3d 100644
--- a/web/src/lib.rs
+++ b/web/src/lib.rs
@@ -97,7 +97,15 @@ pub trait Application {
/// The type of __messages__ your [`Application`] will produce.
///
/// [`Application`]: trait.Application.html
- type Message;
+ type Message: Send;
+
+ /// The [`Executor`] that will run commands and subscriptions.
+ ///
+ /// The [`executor::WasmBindgen`] can be a good choice for the Web.
+ ///
+ /// [`Executor`]: trait.Executor.html
+ /// [`executor::Default`]: executor/struct.Default.html
+ type Executor: Executor;
/// Initializes the [`Application`].
///
@@ -140,6 +148,20 @@ pub trait Application {
/// [`Application`]: trait.Application.html
fn view(&mut self) -> Element<'_, Self::Message>;
+ /// Returns the event [`Subscription`] for the current state of the
+ /// application.
+ ///
+ /// A [`Subscription`] will be kept alive as long as you keep returning it,
+ /// and the __messages__ produced will be handled by
+ /// [`update`](#tymethod.update).
+ ///
+ /// By default, this method returns an empty [`Subscription`].
+ ///
+ /// [`Subscription`]: struct.Subscription.html
+ fn subscription(&self) -> Subscription<Self::Message> {
+ Subscription::none()
+ }
+
/// Runs the [`Application`].
///
/// [`Application`]: trait.Application.html
@@ -147,96 +169,66 @@ pub trait Application {
where
Self: 'static + Sized,
{
- let (app, command) = Self::new();
-
- let instance = Instance::new(app);
- instance.run(command);
- }
-}
+ use futures::stream::StreamExt;
-struct Instance<Message> {
- title: String,
- ui: Rc<RefCell<Box<dyn Application<Message = Message>>>>,
- vdom: Rc<RefCell<Option<dodrio::VdomWeak>>>,
-}
-
-impl<Message> Clone for Instance<Message> {
- fn clone(&self) -> Self {
- Self {
- title: self.title.clone(),
- ui: Rc::clone(&self.ui),
- vdom: Rc::clone(&self.vdom),
- }
- }
-}
-
-impl<Message> Instance<Message>
-where
- Message: 'static,
-{
- fn new(ui: impl Application<Message = Message> + 'static) -> Self {
- Self {
- title: ui.title(),
- ui: Rc::new(RefCell::new(Box::new(ui))),
- vdom: Rc::new(RefCell::new(None)),
- }
- }
-
- fn update(&mut self, message: Message) {
- let command = self.ui.borrow_mut().update(message);
- let title = self.ui.borrow().title();
-
- self.spawn(command);
+ let (app, command) = Self::new();
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
+ let body = document.body().unwrap();
- if self.title != title {
- document.set_title(&title);
+ let mut title = app.title();
+ document.set_title(&title);
- self.title = title;
- }
- }
+ let (sender, receiver) =
+ iced_futures::futures::channel::mpsc::unbounded();
- fn spawn(&mut self, command: Command<Message>) {
- use futures::FutureExt;
+ let mut runtime = iced_futures::Runtime::new(
+ Self::Executor::new().expect("Create executor"),
+ sender.clone(),
+ );
+ runtime.spawn(command);
- for future in command.futures() {
- let mut instance = self.clone();
+ let application = Rc::new(RefCell::new(app));
- let future = future.map(move |message| {
- instance.update(message);
+ let instance = Instance {
+ application: application.clone(),
+ bus: Bus::new(sender),
+ };
- if let Some(ref vdom) = *instance.vdom.borrow() {
- vdom.schedule_render();
- }
- });
+ let vdom = dodrio::Vdom::new(&body, instance);
- wasm_bindgen_futures::spawn_local(future);
- }
- }
+ let event_loop = receiver.for_each(move |message| {
+ let command = application.borrow_mut().update(message);
+ let subscription = application.borrow().subscription();
+ let new_title = application.borrow().title();
- fn run(mut self, command: Command<Message>) {
- let window = web_sys::window().unwrap();
+ runtime.spawn(command);
+ runtime.track(subscription);
- let document = window.document().unwrap();
- document.set_title(&self.title);
+ if title != new_title {
+ document.set_title(&new_title);
- let body = document.body().unwrap();
+ title = new_title;
+ }
- let weak = self.vdom.clone();
- self.spawn(command);
+ vdom.weak().schedule_render();
- let vdom = dodrio::Vdom::new(&body, self);
- *weak.borrow_mut() = Some(vdom.weak());
+ futures::future::ready(())
+ });
- vdom.forget();
+ wasm_bindgen_futures::spawn_local(event_loop);
}
}
-impl<Message> dodrio::Render for Instance<Message>
+struct Instance<A: Application> {
+ application: Rc<RefCell<A>>,
+ bus: Bus<A::Message>,
+}
+
+impl<A> dodrio::Render for Instance<A>
where
- Message: 'static,
+ A: Application,
{
fn render<'a, 'bump>(
&'a self,
@@ -247,11 +239,11 @@ where
{
use dodrio::builder::*;
- let mut ui = self.ui.borrow_mut();
+ let mut ui = self.application.borrow_mut();
let element = ui.view();
let mut style_sheet = style::Sheet::new();
- let node = element.widget.node(bump, &Bus::new(), &mut style_sheet);
+ let node = element.widget.node(bump, &self.bus, &mut style_sheet);
div(bump)
.attr("style", "width: 100%; height: 100%")