diff options
author | 2020-02-04 03:28:47 +0100 | |
---|---|---|
committer | 2020-02-04 03:28:47 +0100 | |
commit | 6d46833eb2a068bd3655859ea828dad04293e5ba (patch) | |
tree | 42cbe1d9a65a2e03e63887611251ed8532f49872 /web/src/lib.rs | |
parent | f5186f31f1e5eed8fe20c5d6e62e2f531fee6365 (diff) | |
download | iced-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.rs | 136 |
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%") |