wip: more work on deadline, support immediates
This commit is contained in:
1 parent
97745c6a7f
commit
5965bee1d1
1 file changed
+71
-100
@@ -18,18 +18,12 @@
|
||||
//!
|
||||
//! This module includes just the barebones for a complete VirtualDOM API.
|
||||
//! Additional functionality is defined in the respective files.
|
||||
use futures_util::{Future, StreamExt};
|
||||
use fxhash::FxHashMap;
|
||||
|
||||
use crate::hooks::{SuspendedContext, SuspenseHook};
|
||||
use crate::innerlude::*;
|
||||
|
||||
use std::any::Any;
|
||||
|
||||
use std::any::TypeId;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
|
||||
use std::pin::Pin;
|
||||
use futures_util::Future;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
/// An integrated virtual node system that progresses events and diffs UI trees.
|
||||
/// Differences are converted into patches which a renderer can use to draw the UI.
|
||||
@@ -63,69 +57,50 @@ pub struct VirtualDom {
|
||||
}
|
||||
|
||||
impl VirtualDom {
|
||||
/// Create a new instance of the Dioxus Virtual Dom with no properties for the root component.
|
||||
/// Create a new VirtualDOM with a component that does not have special props.
|
||||
///
|
||||
/// This means that the root component must either consumes its own context, or statics are used to generate the page.
|
||||
/// The root component can access things like routing in its context.
|
||||
/// # Description
|
||||
///
|
||||
/// As an end-user, you'll want to use the Renderer's "new" method instead of this method.
|
||||
/// Directly creating the VirtualDOM is only useful when implementing a new renderer.
|
||||
///
|
||||
///
|
||||
/// ```ignore
|
||||
/// // Directly from a closure
|
||||
///
|
||||
/// let dom = VirtualDom::new(|cx| cx.render(rsx!{ div {"hello world"} }));
|
||||
///
|
||||
/// // or pass in...
|
||||
///
|
||||
/// let root = |cx| {
|
||||
/// cx.render(rsx!{
|
||||
/// div {"hello world"}
|
||||
/// })
|
||||
/// }
|
||||
/// let dom = VirtualDom::new(root);
|
||||
///
|
||||
/// // or directly from a fn
|
||||
///
|
||||
/// fn Example(cx: Context<()>) -> DomTree {
|
||||
/// cx.render(rsx!{ div{"hello world"} })
|
||||
/// }
|
||||
///
|
||||
/// let dom = VirtualDom::new(Example);
|
||||
/// ```
|
||||
pub fn new(root: FC<()>) -> Self {
|
||||
Self::new_with_props(root, ())
|
||||
}
|
||||
|
||||
/// Start a new VirtualDom instance with a dependent cx.
|
||||
/// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
|
||||
///
|
||||
/// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
|
||||
/// to toss out the entire tree.
|
||||
///
|
||||
/// ```ignore
|
||||
/// // Directly from a closure
|
||||
///
|
||||
/// let dom = VirtualDom::new(|cx| cx.render(rsx!{ div {"hello world"} }));
|
||||
///
|
||||
/// // or pass in...
|
||||
///
|
||||
/// let root = |cx| {
|
||||
/// cx.render(rsx!{
|
||||
/// div {"hello world"}
|
||||
/// })
|
||||
/// }
|
||||
/// let dom = VirtualDom::new(root);
|
||||
///
|
||||
/// // or directly from a fn
|
||||
///
|
||||
/// fn Example(cx: Context, props: &SomeProps) -> VNode {
|
||||
/// # Example
|
||||
/// ```
|
||||
/// fn Example(cx: Context<SomeProps>) -> VNode {
|
||||
/// cx.render(rsx!{ div{"hello world"} })
|
||||
/// }
|
||||
///
|
||||
/// let dom = VirtualDom::new(Example);
|
||||
/// ```
|
||||
///
|
||||
/// Note: the VirtualDOM is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
|
||||
pub fn new(root: FC<()>) -> Self {
|
||||
Self::new_with_props(root, ())
|
||||
}
|
||||
|
||||
/// Create a new VirtualDOM with the given properties for the root component.
|
||||
///
|
||||
/// # Description
|
||||
///
|
||||
/// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
|
||||
///
|
||||
/// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
|
||||
/// to toss out the entire tree.
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// fn Example(cx: Context<SomeProps>) -> VNode {
|
||||
/// cx.render(rsx!{ div{"hello world"} })
|
||||
/// }
|
||||
///
|
||||
/// let dom = VirtualDom::new(Example);
|
||||
/// ```
|
||||
///
|
||||
/// Note: the VirtualDOM is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
|
||||
pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
|
||||
let components = SharedResources::new();
|
||||
|
||||
@@ -150,7 +125,7 @@ impl VirtualDom {
|
||||
|
||||
pub fn launch_in_place(root: FC<()>) -> Self {
|
||||
let mut s = Self::new(root);
|
||||
s.rebuild_in_place().unwrap();
|
||||
s.rebuild().unwrap();
|
||||
s
|
||||
}
|
||||
|
||||
@@ -158,7 +133,7 @@ impl VirtualDom {
|
||||
///
|
||||
pub fn launch_with_props_in_place<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
|
||||
let mut s = Self::new_with_props(root, root_props);
|
||||
s.rebuild_in_place().unwrap();
|
||||
s.rebuild().unwrap();
|
||||
s
|
||||
}
|
||||
|
||||
@@ -170,23 +145,6 @@ impl VirtualDom {
|
||||
unsafe { self.shared.get_scope(id) }
|
||||
}
|
||||
|
||||
/// Rebuilds the VirtualDOM from scratch, but uses a "dummy" RealDom.
|
||||
///
|
||||
/// Used in contexts where a real copy of the structure doesn't matter, and the VirtualDOM is the source of truth.
|
||||
///
|
||||
/// ## Why?
|
||||
///
|
||||
/// This method uses the `DebugDom` under the hood - essentially making the VirtualDOM's diffing patches a "no-op".
|
||||
///
|
||||
/// SSR takes advantage of this by using Dioxus itself as the source of truth, and rendering from the tree directly.
|
||||
pub fn rebuild_in_place(&mut self) -> Result<Vec<DomEdit>> {
|
||||
todo!();
|
||||
// let mut realdom = DebugDom::new();
|
||||
// let mut edits = Vec::new();
|
||||
// self.rebuild(&mut realdom, &mut edits)?;
|
||||
// Ok(edits)
|
||||
}
|
||||
|
||||
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
|
||||
///
|
||||
/// The diff machine expects the RealDom's stack to be the root of the application
|
||||
@@ -219,11 +177,13 @@ impl VirtualDom {
|
||||
|
||||
/// Runs the virtualdom immediately, not waiting for any suspended nodes to complete.
|
||||
///
|
||||
/// This method will not wait for any suspended tasks, completely skipping over
|
||||
/// This method will not wait for any suspended nodes to complete.
|
||||
pub fn run_immediate<'s>(&'s mut self) -> Result<Mutations<'s>> {
|
||||
//
|
||||
|
||||
todo!()
|
||||
use futures_util::FutureExt;
|
||||
let mut is_ready = || false;
|
||||
self.run_with_deadline_and_is_ready(futures_util::future::ready(()), &mut is_ready)
|
||||
.now_or_never()
|
||||
.expect("this future will always resolve immediately")
|
||||
}
|
||||
|
||||
/// Runs the virtualdom with no time limit.
|
||||
@@ -281,6 +241,28 @@ impl VirtualDom {
|
||||
pub async fn run_with_deadline<'s>(
|
||||
&'s mut self,
|
||||
mut deadline: impl Future<Output = ()>,
|
||||
) -> Result<Mutations<'s>> {
|
||||
// let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
|
||||
|
||||
// Configure our deadline
|
||||
use futures_util::FutureExt;
|
||||
let mut deadline_future = deadline.shared();
|
||||
|
||||
let mut is_ready_deadline = deadline_future.clone();
|
||||
let mut is_ready = || -> bool { (&mut is_ready_deadline).now_or_never().is_some() };
|
||||
|
||||
todo!()
|
||||
// Ok(diff_machine.mutations)
|
||||
}
|
||||
|
||||
/// Runs the virtualdom with a deadline and a custom "check" function.
|
||||
///
|
||||
/// Designed this way so "run_immediate" can re-use all the same rendering logic as "run_with_deadline" but the work
|
||||
/// queue is completely drained;
|
||||
async fn run_with_deadline_and_is_ready<'s>(
|
||||
&'s mut self,
|
||||
mut deadline_future: impl Future<Output = ()>,
|
||||
mut is_ready: &mut impl FnMut() -> bool,
|
||||
) -> Result<Mutations<'s>> {
|
||||
/*
|
||||
Strategy:
|
||||
@@ -324,15 +306,6 @@ impl VirtualDom {
|
||||
and listeners hold references to hook data, it is wrong to run a scope that is already being diffed.
|
||||
*/
|
||||
|
||||
// let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
|
||||
|
||||
// Configure our deadline
|
||||
use futures_util::FutureExt;
|
||||
let mut deadline_future = deadline.shared();
|
||||
|
||||
let mut is_ready_deadline = deadline_future.clone();
|
||||
let mut is_ready = || -> bool { (&mut is_ready_deadline).now_or_never().is_some() };
|
||||
|
||||
loop {
|
||||
// 1. Drain the existing immediates.
|
||||
//
|
||||
@@ -427,9 +400,13 @@ impl VirtualDom {
|
||||
}
|
||||
}
|
||||
|
||||
// todo: for these, make these methods return results where deadlinereached is an error type
|
||||
|
||||
// 3. Work through the fibers, and wait for any future work to be ready
|
||||
let mut machine = DiffMachine::new_headless(&self.shared);
|
||||
self.scheduler.progress_work(&mut machine, &mut is_ready);
|
||||
if self.scheduler.progress_work(&mut machine, &mut is_ready) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 4. Wait for any new triggers before looping again
|
||||
if self
|
||||
@@ -440,18 +417,12 @@ impl VirtualDom {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
todo!()
|
||||
// Ok(diff_machine.mutations)
|
||||
}
|
||||
|
||||
pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender<EventTrigger> {
|
||||
self.shared.ui_event_sender.clone()
|
||||
}
|
||||
|
||||
fn get_scope_mut(&mut self, id: ScopeId) -> Option<&mut Scope> {
|
||||
unsafe { self.shared.get_scope_mut(id) }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO!
|
||||
|
||||
Reference in new issue
Block a user