Skip to content

Commit

Permalink
Fix component rendering process (#913)
Browse files Browse the repository at this point in the history
* wip

* Fix component rendering process
  • Loading branch information
jstarry committed Feb 10, 2020
1 parent 0ed0241 commit ecf79b2
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 50 deletions.
2 changes: 1 addition & 1 deletion src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ impl<AGN: Agent> AgentScope<AGN> {
update,
};
let runnable: Box<dyn Runnable> = Box::new(envelope);
scheduler().put_and_try_run(runnable);
scheduler().push(runnable);
}
}

Expand Down
18 changes: 15 additions & 3 deletions src/html/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,12 +315,19 @@ where
/// }
/// }
#[derive(PartialEq, Debug, Default, Clone)]
pub struct NodeRef(Rc<RefCell<Option<Node>>>);
pub struct NodeRef(Rc<RefCell<NodeRefInner>>);

#[derive(PartialEq, Debug, Default, Clone)]
struct NodeRefInner {
node: Option<Node>,
link: Option<NodeRef>,
}

impl NodeRef {
/// Get the wrapped Node reference if it exists
pub fn get(&self) -> Option<Node> {
self.0.borrow().clone()
let inner = self.0.borrow();
inner.node.clone().or_else(|| inner.link.as_ref()?.get())
}

/// Try converting the node reference into another form
Expand All @@ -339,7 +346,12 @@ impl NodeRef {

/// Place a Node in a reference for later use
pub(crate) fn set(&self, node: Option<Node>) {
*self.0.borrow_mut() = node;
self.0.borrow_mut().node = node;
}

/// Link a downstream `NodeRef`
pub(crate) fn link(&self, node_ref: Self) {
self.0.borrow_mut().link = Some(node_ref);
}
}

Expand Down
25 changes: 16 additions & 9 deletions src/html/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ impl<COMP: Component> Scope<COMP> {
/// Schedules a task to call the mounted method on a component and optionally re-render
pub(crate) fn mounted(&mut self) {
let shared_state = self.shared_state.clone();
let mounted = Box::new(MountedComponent { shared_state });
scheduler().put_and_try_run(mounted);
let mounted = MountedComponent { shared_state };
scheduler().push_mount(Box::new(mounted));
}

/// Schedules a task to create and render a component and then mount it to the DOM
pub(crate) fn create(&mut self) {
let shared_state = self.shared_state.clone();
let create = CreateComponent { shared_state };
scheduler().put_and_try_run(Box::new(create));
scheduler().push_create(Box::new(create));
}

/// Schedules a task to send a message or new props to a component
Expand All @@ -101,14 +101,14 @@ impl<COMP: Component> Scope<COMP> {
shared_state: self.shared_state.clone(),
update,
};
scheduler().put_and_try_run(Box::new(update));
scheduler().push(Box::new(update));
}

/// Schedules a task to destroy a component
pub(crate) fn destroy(&mut self) {
let shared_state = self.shared_state.clone();
let destroy = DestroyComponent { shared_state };
scheduler().put_and_try_run(Box::new(destroy));
scheduler().push(Box::new(destroy));
}

/// Send a message to the component
Expand Down Expand Up @@ -180,10 +180,17 @@ impl<COMP: Component> CreatedState<COMP> {
}

fn update(mut self) -> Self {
let mut vnode = self.component.render();
let node = vnode.apply(&self.element, None, self.last_frame);
self.node_ref.set(node);
self.last_frame = Some(vnode);
let mut root = self.component.render();
if let Some(node) = root.apply(&self.element, None, self.last_frame) {
self.node_ref.set(Some(node));
} else if let VNode::VComp(child) = &root {
// If the root VNode is a VComp, we won't have access to the rendered DOM node
// because components render asynchronously. In order to bubble up the DOM node
// from the VComp, we need to link the currently rendering component with its
// root child component.
self.node_ref.link(child.node_ref.clone());
}
self.last_frame = Some(root);
self
}
}
Expand Down
54 changes: 33 additions & 21 deletions src/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};

pub(crate) type Shared<T> = Rc<RefCell<T>>;

Expand All @@ -23,44 +22,57 @@ pub(crate) trait Runnable {
}

/// This is a global scheduler suitable to schedule and run any tasks.
#[derive(Clone)]
pub(crate) struct Scheduler {
lock: Rc<AtomicBool>,
sequence: Shared<VecDeque<Box<dyn Runnable>>>,
}

impl Clone for Scheduler {
fn clone(&self) -> Self {
Scheduler {
lock: self.lock.clone(),
sequence: self.sequence.clone(),
}
}
lock: Rc<RefCell<()>>,
main: Shared<VecDeque<Box<dyn Runnable>>>,
create_component: Shared<VecDeque<Box<dyn Runnable>>>,
mount_component: Shared<Vec<Box<dyn Runnable>>>,
}

impl Scheduler {
/// Creates a new scheduler with a context.
fn new() -> Self {
let sequence = VecDeque::new();
Scheduler {
lock: Rc::new(AtomicBool::new(false)),
sequence: Rc::new(RefCell::new(sequence)),
lock: Rc::new(RefCell::new(())),
main: Rc::new(RefCell::new(VecDeque::new())),
create_component: Rc::new(RefCell::new(VecDeque::new())),
mount_component: Rc::new(RefCell::new(Vec::new())),
}
}

pub(crate) fn put_and_try_run(&self, runnable: Box<dyn Runnable>) {
self.sequence.borrow_mut().push_back(runnable);
if self.lock.compare_and_swap(false, true, Ordering::Relaxed) {
pub(crate) fn push(&self, runnable: Box<dyn Runnable>) {
self.main.borrow_mut().push_back(runnable);
self.start();
}

pub(crate) fn push_create(&self, runnable: Box<dyn Runnable>) {
self.create_component.borrow_mut().push_back(runnable);
self.start();
}

pub(crate) fn push_mount(&self, runnable: Box<dyn Runnable>) {
self.mount_component.borrow_mut().push(runnable);
self.start();
}

pub(crate) fn start(&self) {
let lock = self.lock.try_borrow_mut();
if lock.is_err() {
return;
}

loop {
let do_next = self.sequence.borrow_mut().pop_front();
let do_next = self
.create_component
.borrow_mut()
.pop_front()
.or_else(|| self.mount_component.borrow_mut().pop())
.or_else(|| self.main.borrow_mut().pop_front());
if let Some(runnable) = do_next {
runnable.run();
} else {
break;
}
}
self.lock.store(false, Ordering::Relaxed);
}
}
24 changes: 8 additions & 16 deletions src/virtual_dom/vcomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,11 @@ enum GeneratorType {
}

/// A virtual component.
#[derive(Clone)]
pub struct VComp {
type_id: TypeId,
state: Rc<RefCell<MountState>>,
}

impl Clone for VComp {
fn clone(&self) -> Self {
VComp {
type_id: self.type_id,
state: self.state.clone(),
}
}
pub(crate) node_ref: NodeRef,
}

/// A virtual child component.
Expand Down Expand Up @@ -100,6 +93,7 @@ impl VComp {
where
COMP: Component,
{
let node_ref_clone = node_ref.clone();
let generator = move |generator_type: GeneratorType| -> Mounted {
match generator_type {
GeneratorType::Mount(element, dummy_node) => {
Expand All @@ -108,12 +102,12 @@ impl VComp {
let mut scope = scope.mount_in_place(
element,
Some(VNode::VRef(dummy_node.into())),
node_ref.clone(),
node_ref_clone.clone(),
props.clone(),
);

Mounted {
node_ref: node_ref.clone(),
node_ref: node_ref_clone.clone(),
scope: scope.clone().into(),
destroyer: Box::new(move || scope.destroy()),
}
Expand All @@ -123,7 +117,7 @@ impl VComp {
scope.update(ComponentUpdate::Properties(props.clone()));

Mounted {
node_ref: node_ref.clone(),
node_ref: node_ref_clone.clone(),
scope: scope.clone().into(),
destroyer: Box::new(move || scope.destroy()),
}
Expand All @@ -136,6 +130,7 @@ impl VComp {
state: Rc::new(RefCell::new(MountState::Unmounted(Unmounted {
generator: Box::new(generator),
}))),
node_ref,
}
}
}
Expand Down Expand Up @@ -236,16 +231,13 @@ impl VDiff for VComp {
this.mount(parent.to_owned(), dummy_node)
}
};

let node = mounted.node_ref.get();
self.state.replace(MountState::Mounted(mounted));
node
}
state => {
self.state.replace(state);
None
}
}
None
}
}

Expand Down

0 comments on commit ecf79b2

Please sign in to comment.