From 9b7f6abd877cb8f39a01eae716919ae821436c4d Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Thu, 24 May 2018 18:23:13 -0700 Subject: [PATCH] Fabric: Support for `onLayout` event Summary: `onLayout` event is fired when some layoutable node got inserted or updated (with changed layout) during commit phase. Reviewed By: fkgozali Differential Revision: D8119826 fbshipit-source-id: 831003c88fe5c4358a80328aa715be80e484b52e --- ReactCommon/fabric/uimanager/ShadowTree.cpp | 42 +++++++++++++++++++ ReactCommon/fabric/uimanager/ShadowTree.h | 1 + ReactCommon/fabric/view/ViewEventHandlers.cpp | 14 +++++++ ReactCommon/fabric/view/ViewEventHandlers.h | 7 ++++ 4 files changed, 64 insertions(+) diff --git a/ReactCommon/fabric/uimanager/ShadowTree.cpp b/ReactCommon/fabric/uimanager/ShadowTree.cpp index 439369b9e55..4422b43c0c7 100644 --- a/ReactCommon/fabric/uimanager/ShadowTree.cpp +++ b/ReactCommon/fabric/uimanager/ShadowTree.cpp @@ -76,6 +76,8 @@ void ShadowTree::complete(UnsharedRootShadowNode newRootShadowNode) { ); if (commit(newRootShadowNode)) { + emitLayoutEvents(instructions); + if (delegate_) { delegate_->shadowTreeDidCommit(shared_from_this(), instructions); } @@ -93,6 +95,46 @@ bool ShadowTree::commit(const SharedRootShadowNode &newRootShadowNode) { return true; } +void ShadowTree::emitLayoutEvents(const TreeMutationInstructionList &instructions) { + for (auto &&instruction : instructions) { + auto &&type = instruction.getType(); + + // Only `Insertion` and `Replacement` instructions can affect layout metrics. + if ( + type == TreeMutationInstruction::Insertion || + type == TreeMutationInstruction::Replacement + ) { + auto &&newShadowNode = instruction.getNewChildNode(); + auto &&eventHandlers = newShadowNode->getEventHandlers(); + auto &&viewEventHandlers = std::dynamic_pointer_cast(eventHandlers); + + // Checking if particular shadow node supports `onLayout` event (part of `ViewEventHandlers`). + if (viewEventHandlers) { + // Now we know that both (old and new) shadow nodes must be `LayoutableShadowNode` subclasses. + assert(std::dynamic_pointer_cast(newShadowNode)); + // TODO(T29661055): Consider using `std::reinterpret_pointer_cast`. + auto &&newLayoutableShadowNode = + std::dynamic_pointer_cast(newShadowNode); + + // In case if we have `oldShadowNode`, we have to check that layout metrics have changed. + if (type == TreeMutationInstruction::Replacement) { + auto &&oldShadowNode = instruction.getOldChildNode(); + assert(std::dynamic_pointer_cast(oldShadowNode)); + // TODO(T29661055): Consider using `std::reinterpret_pointer_cast`. + auto &&oldLayoutableShadowNode = + std::dynamic_pointer_cast(oldShadowNode); + + if (oldLayoutableShadowNode->getLayoutMetrics() == newLayoutableShadowNode->getLayoutMetrics()) { + continue; + } + } + + viewEventHandlers->onLayout(newLayoutableShadowNode->getLayoutMetrics()); + } + } + } +} + #pragma mark - Delegate void ShadowTree::setDelegate(ShadowTreeDelegate *delegate) { diff --git a/ReactCommon/fabric/uimanager/ShadowTree.h b/ReactCommon/fabric/uimanager/ShadowTree.h index e2abea7a37c..cc20d697d68 100644 --- a/ReactCommon/fabric/uimanager/ShadowTree.h +++ b/ReactCommon/fabric/uimanager/ShadowTree.h @@ -76,6 +76,7 @@ private: UnsharedRootShadowNode cloneRootShadowNode(const LayoutConstraints &layoutConstraints, const LayoutContext &layoutContext) const; void complete(UnsharedRootShadowNode newRootShadowNode); bool commit(const SharedRootShadowNode &newRootShadowNode); + void emitLayoutEvents(const TreeMutationInstructionList &instructions); const Tag rootTag_; SharedRootShadowNode rootShadowNode_; diff --git a/ReactCommon/fabric/view/ViewEventHandlers.cpp b/ReactCommon/fabric/view/ViewEventHandlers.cpp index 261f38c3ab1..e673d077efb 100644 --- a/ReactCommon/fabric/view/ViewEventHandlers.cpp +++ b/ReactCommon/fabric/view/ViewEventHandlers.cpp @@ -24,5 +24,19 @@ void ViewEventHandlers::onAccessibilityMagicTap() const { dispatchEvent("magicTap"); } +#pragma mark - Layout + +void ViewEventHandlers::onLayout(const LayoutMetrics &layoutMetrics) const { + folly::dynamic payload = folly::dynamic::object(); + auto &&frame = layoutMetrics.frame; + payload["layout"] = folly::dynamic::object + ("x", frame.origin.x) + ("y", frame.origin.y) + ("width", frame.size.width) + ("height", frame.size.height); + + dispatchEvent("layout", payload); +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/fabric/view/ViewEventHandlers.h b/ReactCommon/fabric/view/ViewEventHandlers.h index 3e8236b4b15..e72e41f345b 100644 --- a/ReactCommon/fabric/view/ViewEventHandlers.h +++ b/ReactCommon/fabric/view/ViewEventHandlers.h @@ -9,6 +9,7 @@ #include #include +#include namespace facebook { namespace react { @@ -24,9 +25,15 @@ public: using EventHandlers::EventHandlers; +#pragma mark - Accessibility + void onAccessibilityAction(const std::string &name) const; void onAccessibilityTap() const; void onAccessibilityMagicTap() const; + +#pragma mark - Layout + + void onLayout(const LayoutMetrics &layoutMetrics) const; }; } // namespace react