diff options
Diffstat (limited to '')
| -rw-r--r-- | web/src/bus.rs | 25 | ||||
| -rw-r--r-- | web/src/lib.rs | 136 | ||||
| -rw-r--r-- | web/src/widget/button.rs | 6 | ||||
| -rw-r--r-- | web/src/widget/checkbox.rs | 4 | ||||
| -rw-r--r-- | web/src/widget/radio.rs | 6 | ||||
| -rw-r--r-- | web/src/widget/slider.rs | 5 | ||||
| -rw-r--r-- | web/src/widget/text_input.rs | 5 | 
7 files changed, 84 insertions, 103 deletions
| diff --git a/web/src/bus.rs b/web/src/bus.rs index b3984aff..c66e9659 100644 --- a/web/src/bus.rs +++ b/web/src/bus.rs @@ -1,5 +1,4 @@ -use crate::Instance; - +use iced_futures::futures::channel::mpsc;  use std::rc::Rc;  /// A publisher of messages. @@ -9,13 +8,13 @@ use std::rc::Rc;  /// [`Application`]: trait.Application.html  #[allow(missing_debug_implementations)]  pub struct Bus<Message> { -    publish: Rc<Box<dyn Fn(Message, &mut dyn dodrio::RootRender)>>, +    publish: Rc<Box<dyn Fn(Message) -> ()>>,  }  impl<Message> Clone for Bus<Message> {      fn clone(&self) -> Self { -        Self { -            publish: Rc::clone(&self.publish), +        Bus { +            publish: self.publish.clone(),          }      }  } @@ -24,12 +23,10 @@ impl<Message> Bus<Message>  where      Message: 'static,  { -    pub(crate) fn new() -> Self { +    pub(crate) fn new(publish: mpsc::UnboundedSender<Message>) -> Self {          Self { -            publish: Rc::new(Box::new(|message, root| { -                let app = root.unwrap_mut::<Instance<Message>>(); - -                app.update(message) +            publish: Rc::new(Box::new(move |message| { +                publish.unbounded_send(message).expect("Send message");              })),          }      } @@ -37,8 +34,8 @@ where      /// Publishes a new message for the [`Application`].      ///      /// [`Application`]: trait.Application.html -    pub fn publish(&self, message: Message, root: &mut dyn dodrio::RootRender) { -        (self.publish)(message, root); +    pub fn publish(&self, message: Message) { +        (self.publish)(message)      }      /// Creates a new [`Bus`] that applies the given function to the messages @@ -52,9 +49,7 @@ where          let publish = self.publish.clone();          Bus { -            publish: Rc::new(Box::new(move |message, root| { -                publish(mapper(message), root) -            })), +            publish: Rc::new(Box::new(move |message| publish(mapper(message)))),          }      }  } 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%") diff --git a/web/src/widget/button.rs b/web/src/widget/button.rs index e628bd18..6fef48ce 100644 --- a/web/src/widget/button.rs +++ b/web/src/widget/button.rs @@ -163,10 +163,8 @@ where          if let Some(on_press) = self.on_press.clone() {              let event_bus = bus.clone(); -            node = node.on("click", move |root, vdom, _event| { -                event_bus.publish(on_press.clone(), root); - -                vdom.schedule_render(); +            node = node.on("click", move |_root, _vdom, _event| { +                event_bus.publish(on_press.clone());              });          } diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs index 34d13a1b..1e864875 100644 --- a/web/src/widget/checkbox.rs +++ b/web/src/widget/checkbox.rs @@ -84,9 +84,9 @@ where                  input(bump)                      .attr("type", "checkbox")                      .bool_attr("checked", self.is_checked) -                    .on("click", move |root, vdom, _event| { +                    .on("click", move |_root, vdom, _event| {                          let msg = on_toggle(!is_checked); -                        event_bus.publish(msg, root); +                        event_bus.publish(msg);                          vdom.schedule_render();                      }) diff --git a/web/src/widget/radio.rs b/web/src/widget/radio.rs index 4e7d02b8..6dd0ad45 100644 --- a/web/src/widget/radio.rs +++ b/web/src/widget/radio.rs @@ -93,10 +93,8 @@ where                      .attr("type", "radio")                      .attr("style", "margin-right: 10px")                      .bool_attr("checked", self.is_selected) -                    .on("click", move |root, vdom, _event| { -                        event_bus.publish(on_click.clone(), root); - -                        vdom.schedule_render(); +                    .on("click", move |_root, _vdom, _event| { +                        event_bus.publish(on_click.clone());                      })                      .finish(),                  text(radio_label.into_bump_str()), diff --git a/web/src/widget/slider.rs b/web/src/widget/slider.rs index fc955781..25c57933 100644 --- a/web/src/widget/slider.rs +++ b/web/src/widget/slider.rs @@ -111,7 +111,7 @@ where              .attr("max", max.into_bump_str())              .attr("value", value.into_bump_str())              .attr("style", "width: 100%") -            .on("input", move |root, vdom, event| { +            .on("input", move |_root, _vdom, event| {                  let slider = match event.target().and_then(|t| {                      t.dyn_into::<web_sys::HtmlInputElement>().ok()                  }) { @@ -120,8 +120,7 @@ where                  };                  if let Ok(value) = slider.value().parse::<f32>() { -                    event_bus.publish(on_change(value), root); -                    vdom.schedule_render(); +                    event_bus.publish(on_change(value));                  }              })              .finish() diff --git a/web/src/widget/text_input.rs b/web/src/widget/text_input.rs index a478874a..078e05b2 100644 --- a/web/src/widget/text_input.rs +++ b/web/src/widget/text_input.rs @@ -175,7 +175,7 @@ where                  "type",                  bumpalo::format!(in bump, "{}", if self.is_secure { "password" } else { "text" }).into_bump_str(),              ) -            .on("input", move |root, vdom, event| { +            .on("input", move |_root, _vdom, event| {                  let text_input = match event.target().and_then(|t| {                      t.dyn_into::<web_sys::HtmlInputElement>().ok()                  }) { @@ -183,8 +183,7 @@ where                      Some(text_input) => text_input,                  }; -                event_bus.publish(on_change(text_input.value()), root); -                vdom.schedule_render(); +                event_bus.publish(on_change(text_input.value()));              })              .finish()      } | 
