wip: ric_raf wired up
This commit is contained in:
1 parent
f457b71131
commit
8b0d04ce58
3 files changed
+112
-33
No files matched your search
@@ -225,8 +225,8 @@ impl VirtualDom {
|
|||||||
/// If there are pending tasks, they will be progressed before returning. This is useful when rendering an application
|
/// If there are pending tasks, they will be progressed before returning. This is useful when rendering an application
|
||||||
/// that has suspended nodes or suspended tasks. Be warned - any async tasks running forever will prevent this method
|
/// that has suspended nodes or suspended tasks. Be warned - any async tasks running forever will prevent this method
|
||||||
/// from completing. Consider using `run` and specifing a deadline.
|
/// from completing. Consider using `run` and specifing a deadline.
|
||||||
pub async fn run_unbounded<'s>(&'s mut self) -> Result<Mutations<'s>> {
|
pub async fn run_unbounded<'s>(&'s mut self) -> Mutations<'s> {
|
||||||
self.run_with_deadline(async {}).await
|
self.run_with_deadline(async {}).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the virtualdom with a deadline.
|
/// Run the virtualdom with a deadline.
|
||||||
@@ -275,7 +275,7 @@ impl VirtualDom {
|
|||||||
pub async fn run_with_deadline<'s>(
|
pub async fn run_with_deadline<'s>(
|
||||||
&'s mut self,
|
&'s mut self,
|
||||||
deadline: impl Future<Output = ()>,
|
deadline: impl Future<Output = ()>,
|
||||||
) -> Result<Mutations<'s>> {
|
) -> Option<Mutations<'s>> {
|
||||||
let mut committed_mutations = Mutations::new();
|
let mut committed_mutations = Mutations::new();
|
||||||
let mut deadline = Box::pin(deadline.fuse());
|
let mut deadline = Box::pin(deadline.fuse());
|
||||||
|
|
||||||
@@ -293,12 +293,12 @@ impl VirtualDom {
|
|||||||
let deadline_expired = self.scheduler.wait_for_any_trigger(&mut deadline).await;
|
let deadline_expired = self.scheduler.wait_for_any_trigger(&mut deadline).await;
|
||||||
|
|
||||||
if deadline_expired {
|
if deadline_expired {
|
||||||
return Ok(committed_mutations);
|
return Some(committed_mutations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create work from the pending event queue
|
// Create work from the pending event queue
|
||||||
self.scheduler.consume_pending_events()?;
|
self.scheduler.consume_pending_events();
|
||||||
|
|
||||||
// Work through the current subtree, and commit the results when it finishes
|
// Work through the current subtree, and commit the results when it finishes
|
||||||
// When the deadline expires, give back the work
|
// When the deadline expires, give back the work
|
||||||
@@ -316,10 +316,10 @@ impl VirtualDom {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if !self.scheduler.has_any_work() {
|
if !self.scheduler.has_any_work() {
|
||||||
return Ok(committed_mutations);
|
return Some(committed_mutations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FiberResult::Interrupted => return Ok(committed_mutations),
|
FiberResult::Interrupted => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,6 +327,12 @@ impl VirtualDom {
|
|||||||
pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender<EventTrigger> {
|
pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender<EventTrigger> {
|
||||||
self.shared.ui_event_sender.clone()
|
self.shared.ui_event_sender.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_work(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn wait_for_any_work(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO!
|
// TODO!
|
||||||
|
|||||||
+16
-26
@@ -56,20 +56,16 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
pub use crate::cfg::WebConfig;
|
pub use crate::cfg::WebConfig;
|
||||||
use crate::dom::load_document;
|
use crate::dom::load_document;
|
||||||
use dioxus::prelude::{Context, Properties, VNode};
|
use dioxus::prelude::Properties;
|
||||||
use dioxus::virtual_dom::VirtualDom;
|
use dioxus::virtual_dom::VirtualDom;
|
||||||
pub use dioxus_core as dioxus;
|
pub use dioxus_core as dioxus;
|
||||||
use dioxus_core::error::Result;
|
use dioxus_core::prelude::FC;
|
||||||
use dioxus_core::{events::EventTrigger, prelude::FC};
|
|
||||||
use futures_util::{pin_mut, Stream, StreamExt};
|
|
||||||
use fxhash::FxHashMap;
|
|
||||||
use js_sys::Iterator;
|
|
||||||
use web_sys::{window, Document, Element, Event, Node, NodeList};
|
|
||||||
|
|
||||||
mod cache;
|
mod cache;
|
||||||
mod cfg;
|
mod cfg;
|
||||||
mod dom;
|
mod dom;
|
||||||
mod nodeslab;
|
mod nodeslab;
|
||||||
|
mod ric_raf;
|
||||||
|
|
||||||
/// Launches the VirtualDOM from the specified component function.
|
/// Launches the VirtualDOM from the specified component function.
|
||||||
///
|
///
|
||||||
@@ -100,15 +96,8 @@ where
|
|||||||
{
|
{
|
||||||
let config = config(WebConfig::default());
|
let config = config(WebConfig::default());
|
||||||
let fut = run_with_props(root, root_props, config);
|
let fut = run_with_props(root, root_props, config);
|
||||||
|
wasm_bindgen_futures::spawn_local(fut);
|
||||||
wasm_bindgen_futures::spawn_local(async {
|
|
||||||
match fut.await {
|
|
||||||
Ok(_) => log::error!("Your app completed running... somehow?"),
|
|
||||||
Err(e) => log::error!("Your app crashed! {}", e),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method is the primary entrypoint for Websys Dioxus apps. Will panic if an error occurs while rendering.
|
/// This method is the primary entrypoint for Websys Dioxus apps. Will panic if an error occurs while rendering.
|
||||||
/// See DioxusErrors for more information on how these errors could occour.
|
/// See DioxusErrors for more information on how these errors could occour.
|
||||||
///
|
///
|
||||||
@@ -122,11 +111,7 @@ where
|
|||||||
///
|
///
|
||||||
/// Run the app to completion, panicing if any error occurs while rendering.
|
/// Run the app to completion, panicing if any error occurs while rendering.
|
||||||
/// Pairs well with the wasm_bindgen async handler
|
/// Pairs well with the wasm_bindgen async handler
|
||||||
pub async fn run_with_props<T: Properties + 'static>(
|
pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T, cfg: WebConfig) {
|
||||||
root: FC<T>,
|
|
||||||
root_props: T,
|
|
||||||
cfg: WebConfig,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut dom = VirtualDom::new_with_props(root, root_props);
|
let mut dom = VirtualDom::new_with_props(root, root_props);
|
||||||
|
|
||||||
let hydrating = cfg.hydrate;
|
let hydrating = cfg.hydrate;
|
||||||
@@ -138,7 +123,6 @@ pub async fn run_with_props<T: Properties + 'static>(
|
|||||||
let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback);
|
let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback);
|
||||||
|
|
||||||
let mut mutations = dom.rebuild();
|
let mut mutations = dom.rebuild();
|
||||||
log::info!("Mutations: {:#?}", mutations);
|
|
||||||
|
|
||||||
// hydrating is simply running the dom for a single render. If the page is already written, then the corresponding
|
// hydrating is simply running the dom for a single render. If the page is already written, then the corresponding
|
||||||
// ElementIds should already line up because the web_sys dom has already loaded elements with the DioxusID into memory
|
// ElementIds should already line up because the web_sys dom has already loaded elements with the DioxusID into memory
|
||||||
@@ -146,12 +130,18 @@ pub async fn run_with_props<T: Properties + 'static>(
|
|||||||
websys_dom.process_edits(&mut mutations.edits);
|
websys_dom.process_edits(&mut mutations.edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let work_loop = ric_raf::RafLoop::new();
|
||||||
loop {
|
loop {
|
||||||
let deadline = gloo_timers::future::TimeoutFuture::new(16);
|
// if virtualdom has nothing, wait for it to have something before requesting idle time
|
||||||
let mut mutations = dom.run_with_deadline(deadline).await?;
|
if !dom.has_work() {
|
||||||
|
dom.wait_for_any_work().await;
|
||||||
|
}
|
||||||
|
|
||||||
websys_dom.process_edits(&mut mutations.edits);
|
let deadline = work_loop.wait_for_idle_time().await;
|
||||||
|
|
||||||
|
if let Some(mut mutations) = dom.run_with_deadline(deadline).await {
|
||||||
|
work_loop.wait_for_raf().await;
|
||||||
|
websys_dom.process_edits(&mut mutations.edits);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
//! RequestAnimationFrame and RequestIdleCallback port and polyfill.
|
||||||
|
|
||||||
|
use std::{cell::RefCell, fmt, rc::Rc};
|
||||||
|
|
||||||
|
use gloo_timers::future::TimeoutFuture;
|
||||||
|
use js_sys::Function;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
use wasm_bindgen::{prelude::Closure, JsValue};
|
||||||
|
use web_sys::Window;
|
||||||
|
|
||||||
|
pub struct RafLoop {
|
||||||
|
window: Window,
|
||||||
|
ric_receiver: async_channel::Receiver<()>,
|
||||||
|
raf_receiver: async_channel::Receiver<()>,
|
||||||
|
ric_closure: Closure<dyn Fn(JsValue)>,
|
||||||
|
raf_closure: Closure<dyn Fn(JsValue)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RafLoop {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let (raf_sender, raf_receiver) = async_channel::unbounded();
|
||||||
|
|
||||||
|
let raf_closure: Closure<dyn Fn(JsValue)> =
|
||||||
|
Closure::wrap(Box::new(move |v: JsValue| raf_sender.try_send(()).unwrap()));
|
||||||
|
|
||||||
|
let (ric_sender, ric_receiver) = async_channel::unbounded();
|
||||||
|
|
||||||
|
let ric_closure: Closure<dyn Fn(JsValue)> =
|
||||||
|
Closure::wrap(Box::new(move |v: JsValue| ric_sender.try_send(()).unwrap()));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
window: web_sys::window().unwrap(),
|
||||||
|
raf_receiver,
|
||||||
|
raf_closure,
|
||||||
|
ric_receiver,
|
||||||
|
ric_closure,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// waits for some idle time and returns a timeout future that expires after the idle time has passed
|
||||||
|
pub async fn wait_for_idle_time(&self) -> TimeoutFuture {
|
||||||
|
// comes with its own safari polyfill :)
|
||||||
|
|
||||||
|
let ric_fn = self.ric_closure.as_ref().dyn_ref::<Function>().unwrap();
|
||||||
|
let deadline: u32 = self.window.request_idle_callback(ric_fn).unwrap();
|
||||||
|
|
||||||
|
self.ric_receiver.recv().await.unwrap();
|
||||||
|
|
||||||
|
let deadline = TimeoutFuture::new(deadline);
|
||||||
|
deadline
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn wait_for_raf(&self) {
|
||||||
|
let raf_fn = self.raf_closure.as_ref().dyn_ref::<Function>().unwrap();
|
||||||
|
let id: i32 = self.window.request_animation_frame(raf_fn).unwrap();
|
||||||
|
self.raf_receiver.recv().await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AnimationFrame {
|
||||||
|
render_id: i32,
|
||||||
|
closure: Closure<dyn Fn(JsValue)>,
|
||||||
|
callback_wrapper: Rc<RefCell<Option<CallbackWrapper>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CallbackWrapper(Box<dyn FnOnce(f64) + 'static>);
|
||||||
|
impl fmt::Debug for CallbackWrapper {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("CallbackWrapper")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for AnimationFrame {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.callback_wrapper.borrow_mut().is_some() {
|
||||||
|
web_sys::window()
|
||||||
|
.unwrap_throw()
|
||||||
|
.cancel_animation_frame(self.render_id)
|
||||||
|
.unwrap_throw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user