Fabric: Introducing `ShadowTree`
Summary: ShadowTree is an abstraction around (commited) root shadow node and managing its lifecycle. Reviewed By: mdvacca Differential Revision: D7857049 fbshipit-source-id: 8d530e0366fc703e4aef4ec88dd8ea990dafaaf1
This commit is contained in:
parent
2bb41031ba
commit
a879842033
|
@ -19,7 +19,6 @@ namespace react {
|
||||||
|
|
||||||
Scheduler::Scheduler() {
|
Scheduler::Scheduler() {
|
||||||
auto componentDescriptorRegistry = std::make_shared<ComponentDescriptorRegistry>();
|
auto componentDescriptorRegistry = std::make_shared<ComponentDescriptorRegistry>();
|
||||||
|
|
||||||
componentDescriptorRegistry->registerComponentDescriptor(std::make_shared<ViewComponentDescriptor>());
|
componentDescriptorRegistry->registerComponentDescriptor(std::make_shared<ViewComponentDescriptor>());
|
||||||
componentDescriptorRegistry->registerComponentDescriptor(std::make_shared<ParagraphComponentDescriptor>());
|
componentDescriptorRegistry->registerComponentDescriptor(std::make_shared<ParagraphComponentDescriptor>());
|
||||||
componentDescriptorRegistry->registerComponentDescriptor(std::make_shared<TextComponentDescriptor>());
|
componentDescriptorRegistry->registerComponentDescriptor(std::make_shared<TextComponentDescriptor>());
|
||||||
|
@ -34,12 +33,29 @@ Scheduler::~Scheduler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::registerRootTag(Tag rootTag) {
|
void Scheduler::registerRootTag(Tag rootTag) {
|
||||||
auto rootShadowNode = std::make_shared<RootShadowNode>(rootTag, rootTag, nullptr);
|
auto &&shadowTree = std::make_shared<ShadowTree>(rootTag);
|
||||||
rootNodeRegistry_.insert({rootTag, rootShadowNode});
|
shadowTree->setDelegate(this);
|
||||||
|
shadowTreeRegistry_.insert({rootTag, shadowTree});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::unregisterRootTag(Tag rootTag) {
|
void Scheduler::unregisterRootTag(Tag rootTag) {
|
||||||
rootNodeRegistry_.erase(rootTag);
|
auto &&iterator = shadowTreeRegistry_.find(rootTag);
|
||||||
|
auto &&shadowTree = iterator->second;
|
||||||
|
assert(shadowTree);
|
||||||
|
shadowTree->setDelegate(nullptr);
|
||||||
|
shadowTreeRegistry_.erase(iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
Size Scheduler::measure(const Tag &rootTag, const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const {
|
||||||
|
auto &&shadowTree = shadowTreeRegistry_.at(rootTag);
|
||||||
|
assert(shadowTree);
|
||||||
|
return shadowTree->measure(layoutConstraints, layoutContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scheduler::constraintLayout(const Tag &rootTag, const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) {
|
||||||
|
auto &&shadowTree = shadowTreeRegistry_.at(rootTag);
|
||||||
|
assert(shadowTree);
|
||||||
|
return shadowTree->constraintLayout(layoutConstraints, layoutContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Delegate
|
#pragma mark - Delegate
|
||||||
|
@ -48,53 +64,25 @@ void Scheduler::setDelegate(SchedulerDelegate *delegate) {
|
||||||
delegate_ = delegate;
|
delegate_ = delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
SchedulerDelegate *Scheduler::getDelegate() {
|
SchedulerDelegate *Scheduler::getDelegate() const {
|
||||||
return delegate_;
|
return delegate_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - ShadowTreeDelegate
|
||||||
|
|
||||||
|
void Scheduler::shadowTreeDidCommit(const SharedShadowTree &shadowTree, const TreeMutationInstructionList &instructions) {
|
||||||
|
if (delegate_) {
|
||||||
|
delegate_->schedulerDidComputeMutationInstructions(shadowTree->getRootTag(), instructions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - UIManagerDelegate
|
#pragma mark - UIManagerDelegate
|
||||||
|
|
||||||
void Scheduler::uiManagerDidFinishTransaction(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes) {
|
void Scheduler::uiManagerDidFinishTransaction(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes) {
|
||||||
SharedRootShadowNode oldRootShadowNode = rootNodeRegistry_[rootTag];
|
auto &&iterator = shadowTreeRegistry_.find(rootTag);
|
||||||
assert(oldRootShadowNode);
|
auto &&shadowTree = iterator->second;
|
||||||
|
assert(shadowTree);
|
||||||
SharedRootShadowNode newRootShadowNode =
|
return shadowTree->complete(rootChildNodes);
|
||||||
std::make_shared<RootShadowNode>(oldRootShadowNode, nullptr, SharedShadowNodeSharedList(rootChildNodes));
|
|
||||||
|
|
||||||
auto nonConstOldRootShadowNode = std::const_pointer_cast<RootShadowNode>(oldRootShadowNode);
|
|
||||||
auto nonConstNewRootShadowNode = std::const_pointer_cast<RootShadowNode>(newRootShadowNode);
|
|
||||||
|
|
||||||
LayoutContext layoutContext = LayoutContext();
|
|
||||||
|
|
||||||
LOG(INFO) << "Old Shadow Tree: \n" << oldRootShadowNode->getDebugDescription();
|
|
||||||
LOG(INFO) << "New Shadow Tree *before* layout: \n" << newRootShadowNode->getDebugDescription();
|
|
||||||
|
|
||||||
nonConstNewRootShadowNode->layout(layoutContext);
|
|
||||||
|
|
||||||
nonConstNewRootShadowNode->sealRecursive();
|
|
||||||
|
|
||||||
LOG(INFO) << "New Shadow Tree *after* layout: \n" << nonConstNewRootShadowNode->getDebugDescription();
|
|
||||||
|
|
||||||
TreeMutationInstructionList instructions = TreeMutationInstructionList();
|
|
||||||
|
|
||||||
calculateMutationInstructions(
|
|
||||||
instructions,
|
|
||||||
oldRootShadowNode,
|
|
||||||
oldRootShadowNode->ShadowNode::getChildren(),
|
|
||||||
newRootShadowNode->ShadowNode::getChildren()
|
|
||||||
);
|
|
||||||
|
|
||||||
LOG(INFO) << "TreeMutationInstructionList:";
|
|
||||||
|
|
||||||
for (auto instruction : instructions) {
|
|
||||||
LOG(INFO) << "Instruction: " << instruction.getDebugDescription();
|
|
||||||
}
|
|
||||||
|
|
||||||
rootNodeRegistry_[rootTag] = newRootShadowNode;
|
|
||||||
|
|
||||||
if (delegate_) {
|
|
||||||
delegate_->schedulerDidComputeMutationInstructions(rootTag, instructions);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::uiManagerDidCreateShadowNode(const SharedShadowNode &shadowNode) {
|
void Scheduler::uiManagerDidCreateShadowNode(const SharedShadowNode &shadowNode) {
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include <fabric/core/LayoutConstraints.h>
|
#include <fabric/core/LayoutConstraints.h>
|
||||||
#include <fabric/uimanager/SchedulerDelegate.h>
|
#include <fabric/uimanager/SchedulerDelegate.h>
|
||||||
#include <fabric/uimanager/UIManagerDelegate.h>
|
#include <fabric/uimanager/UIManagerDelegate.h>
|
||||||
|
#include <fabric/uimanager/ShadowTree.h>
|
||||||
|
#include <fabric/uimanager/ShadowTreeDelegate.h>
|
||||||
#include <fabric/view/ViewShadowNode.h>
|
#include <fabric/view/ViewShadowNode.h>
|
||||||
#include <fabric/view/RootShadowNode.h>
|
#include <fabric/view/RootShadowNode.h>
|
||||||
|
|
||||||
|
@ -20,18 +22,21 @@ class FabricUIManager;
|
||||||
* Scheduler coordinates Shadow Tree updates and event flows.
|
* Scheduler coordinates Shadow Tree updates and event flows.
|
||||||
*/
|
*/
|
||||||
class Scheduler:
|
class Scheduler:
|
||||||
public UIManagerDelegate {
|
public UIManagerDelegate,
|
||||||
|
public ShadowTreeDelegate {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Scheduler();
|
|
||||||
virtual ~Scheduler();
|
|
||||||
|
|
||||||
#pragma mark - Root Nodes Managerment
|
Scheduler();
|
||||||
|
~Scheduler();
|
||||||
|
|
||||||
|
#pragma mark - Shadow Tree Management
|
||||||
|
|
||||||
void registerRootTag(Tag rootTag);
|
void registerRootTag(Tag rootTag);
|
||||||
void unregisterRootTag(Tag rootTag);
|
void unregisterRootTag(Tag rootTag);
|
||||||
|
|
||||||
void setLayoutConstraints(Tag rootTag, LayoutConstraints layoutConstraints);
|
Size measure(const Tag &rootTag, const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const;
|
||||||
|
void constraintLayout(const Tag &rootTag, const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext);
|
||||||
|
|
||||||
#pragma mark - Delegate
|
#pragma mark - Delegate
|
||||||
|
|
||||||
|
@ -41,13 +46,17 @@ public:
|
||||||
* the pointer before being destroyed.
|
* the pointer before being destroyed.
|
||||||
*/
|
*/
|
||||||
void setDelegate(SchedulerDelegate *delegate);
|
void setDelegate(SchedulerDelegate *delegate);
|
||||||
SchedulerDelegate *getDelegate();
|
SchedulerDelegate *getDelegate() const;
|
||||||
|
|
||||||
#pragma mark - UIManagerDelegate
|
#pragma mark - UIManagerDelegate
|
||||||
|
|
||||||
void uiManagerDidFinishTransaction(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes) override;
|
void uiManagerDidFinishTransaction(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes) override;
|
||||||
void uiManagerDidCreateShadowNode(const SharedShadowNode &shadowNode) override;
|
void uiManagerDidCreateShadowNode(const SharedShadowNode &shadowNode) override;
|
||||||
|
|
||||||
|
#pragma mark - ShadowTreeDelegate
|
||||||
|
|
||||||
|
void shadowTreeDidCommit(const SharedShadowTree &shadowTree, const TreeMutationInstructionList &instructions) override;
|
||||||
|
|
||||||
#pragma mark - Deprecated
|
#pragma mark - Deprecated
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -56,13 +65,10 @@ public:
|
||||||
std::shared_ptr<FabricUIManager> getUIManager_DO_NOT_USE();
|
std::shared_ptr<FabricUIManager> getUIManager_DO_NOT_USE();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
SchedulerDelegate *delegate_;
|
SchedulerDelegate *delegate_;
|
||||||
std::shared_ptr<FabricUIManager> uiManager_;
|
std::shared_ptr<FabricUIManager> uiManager_;
|
||||||
|
std::unordered_map<Tag, SharedShadowTree> shadowTreeRegistry_;
|
||||||
/*
|
|
||||||
* All commited `RootShadowNode` instances to differentiate against.
|
|
||||||
*/
|
|
||||||
std::unordered_map<Tag, SharedRootShadowNode> rootNodeRegistry_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace react
|
} // namespace react
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "ShadowTree.h"
|
||||||
|
|
||||||
|
#include <fabric/core/LayoutContext.h>
|
||||||
|
#include <fabric/core/LayoutPrimitives.h>
|
||||||
|
|
||||||
|
#include "ShadowTreeDelegate.h"
|
||||||
|
#include "Differentiator.h"
|
||||||
|
#include "TreeMutationInstruction.h"
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace react {
|
||||||
|
|
||||||
|
ShadowTree::ShadowTree(Tag rootTag):
|
||||||
|
rootTag_(rootTag) {
|
||||||
|
|
||||||
|
rootShadowNode_ = std::make_shared<RootShadowNode>(
|
||||||
|
rootTag,
|
||||||
|
rootTag,
|
||||||
|
nullptr,
|
||||||
|
RootShadowNode::defaultSharedProps()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag ShadowTree::getRootTag() const {
|
||||||
|
return rootTag_;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Layout
|
||||||
|
|
||||||
|
Size ShadowTree::measure(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const {
|
||||||
|
auto newRootShadowNode = cloneRootShadowNode(layoutConstraints, layoutContext);
|
||||||
|
newRootShadowNode->layout();
|
||||||
|
return newRootShadowNode->getLayoutMetrics().frame.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShadowTree::constraintLayout(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) {
|
||||||
|
auto newRootShadowNode = cloneRootShadowNode(layoutConstraints, layoutContext);
|
||||||
|
complete(newRootShadowNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Commiting
|
||||||
|
|
||||||
|
UnsharedRootShadowNode ShadowTree::cloneRootShadowNode(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const {
|
||||||
|
auto oldRootShadowNode = rootShadowNode_;
|
||||||
|
auto &&props = std::make_shared<RootProps>(*oldRootShadowNode->getProps());
|
||||||
|
props->applyLayoutConstraints(layoutConstraints);
|
||||||
|
props->applyLayoutContext(layoutContext);
|
||||||
|
auto newRootShadowNode = std::make_shared<RootShadowNode>(oldRootShadowNode, props, nullptr);
|
||||||
|
return newRootShadowNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShadowTree::complete(const SharedShadowNodeUnsharedList &rootChildNodes) {
|
||||||
|
auto oldRootShadowNode = rootShadowNode_;
|
||||||
|
auto newRootShadowNode =
|
||||||
|
std::make_shared<RootShadowNode>(oldRootShadowNode, nullptr, SharedShadowNodeSharedList(rootChildNodes));
|
||||||
|
|
||||||
|
complete(newRootShadowNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShadowTree::complete(UnsharedRootShadowNode newRootShadowNode) {
|
||||||
|
SharedRootShadowNode oldRootShadowNode = rootShadowNode_;
|
||||||
|
|
||||||
|
newRootShadowNode->layout();
|
||||||
|
|
||||||
|
newRootShadowNode->sealRecursive();
|
||||||
|
|
||||||
|
TreeMutationInstructionList instructions = TreeMutationInstructionList();
|
||||||
|
|
||||||
|
calculateMutationInstructions(
|
||||||
|
instructions,
|
||||||
|
oldRootShadowNode,
|
||||||
|
oldRootShadowNode->ShadowNode::getChildren(),
|
||||||
|
newRootShadowNode->ShadowNode::getChildren()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (commit(newRootShadowNode)) {
|
||||||
|
if (delegate_) {
|
||||||
|
delegate_->shadowTreeDidCommit(shared_from_this(), instructions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShadowTree::commit(const SharedRootShadowNode &newRootShadowNode) {
|
||||||
|
std::lock_guard<std::mutex> lock(commitMutex_);
|
||||||
|
|
||||||
|
if (newRootShadowNode->getSourceNode() != rootShadowNode_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rootShadowNode_ = newRootShadowNode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Delegate
|
||||||
|
|
||||||
|
void ShadowTree::setDelegate(ShadowTreeDelegate *delegate) {
|
||||||
|
delegate_ = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShadowTreeDelegate *ShadowTree::getDelegate() const {
|
||||||
|
return delegate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace react
|
||||||
|
} // namespace facebook
|
|
@ -0,0 +1,87 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
|
#include <fabric/core/LayoutConstraints.h>
|
||||||
|
#include <fabric/core/ReactPrimitives.h>
|
||||||
|
#include <fabric/core/ShadowNode.h>
|
||||||
|
#include <fabric/uimanager/ShadowTreeDelegate.h>
|
||||||
|
#include <fabric/view/RootShadowNode.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace react {
|
||||||
|
|
||||||
|
class ShadowTree;
|
||||||
|
|
||||||
|
using SharedShadowTree = std::shared_ptr<ShadowTree>;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents the shadow tree and its lifecycle.
|
||||||
|
*/
|
||||||
|
class ShadowTree final:
|
||||||
|
public std::enable_shared_from_this<ShadowTree> {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a new shadow tree instance with given `rootTag`.
|
||||||
|
*/
|
||||||
|
ShadowTree(Tag rootTag);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the rootTag associated with the shadow tree (the tag of the
|
||||||
|
* root shadow node).
|
||||||
|
*/
|
||||||
|
Tag getRootTag() const;
|
||||||
|
|
||||||
|
#pragma mark - Layout
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Measures the shadow tree with given `layoutConstraints` and `layoutContext`.
|
||||||
|
* Can be called from any thread, side-effect-less.
|
||||||
|
*/
|
||||||
|
Size measure(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Applies given `layoutConstraints` and `layoutContext` and commit
|
||||||
|
* the new shadow tree.
|
||||||
|
* Can be called from any thread.
|
||||||
|
*/
|
||||||
|
void constraintLayout(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext);
|
||||||
|
|
||||||
|
#pragma mark - Application
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new shadow tree with given `rootChildNodes` and commit.
|
||||||
|
* Can be called from any thread.
|
||||||
|
*/
|
||||||
|
void complete(const SharedShadowNodeUnsharedList &rootChildNodes);
|
||||||
|
|
||||||
|
#pragma mark - Delegate
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets and gets the delegate.
|
||||||
|
* The delegate is stored as a raw pointer, so the owner must null
|
||||||
|
* the pointer before being destroyed.
|
||||||
|
*/
|
||||||
|
void setDelegate(ShadowTreeDelegate *delegate);
|
||||||
|
ShadowTreeDelegate *getDelegate() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
UnsharedRootShadowNode cloneRootShadowNode(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const;
|
||||||
|
void complete(UnsharedRootShadowNode newRootShadowNode);
|
||||||
|
bool commit(const SharedRootShadowNode &newRootShadowNode);
|
||||||
|
|
||||||
|
const Tag rootTag_;
|
||||||
|
SharedRootShadowNode rootShadowNode_;
|
||||||
|
ShadowTreeDelegate *delegate_;
|
||||||
|
mutable std::mutex commitMutex_ {};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace react
|
||||||
|
} // namespace facebook
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fabric/uimanager/TreeMutationInstruction.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace react {
|
||||||
|
|
||||||
|
class ShadowTree;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Abstract class for ShadowTree's delegate.
|
||||||
|
*/
|
||||||
|
class ShadowTreeDelegate {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called right after Shadow Tree commit a new state of the the tree.
|
||||||
|
*/
|
||||||
|
virtual void shadowTreeDidCommit(const std::shared_ptr<ShadowTree> &shadowTree, const TreeMutationInstructionList &instructions) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace react
|
||||||
|
} // namespace facebook
|
Loading…
Reference in New Issue