Introducing LayoutableShadowNode

Summary: LayoutableShadowNode defines a unified interface (and set of primitives) essential for laying out shadow nodes.

Reviewed By: fkgozali

Differential Revision: D7230668

fbshipit-source-id: d8c1772d4c3bd1f87c41f7240a39aecebf3696ae
This commit is contained in:
Valentin Shergin 2018-03-18 19:04:20 -07:00 committed by Facebook Github Bot
parent 840638c441
commit 811d5bfc73
7 changed files with 334 additions and 0 deletions

View File

@ -24,6 +24,7 @@ rn_xplat_cxx_library(
[
("primitives", "*.h"),
("componentdescriptor", "*.h"),
("layout", "*.h"),
("shadownode", "*.h"),
],
prefix = "fabric/core",

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <fabric/core/LayoutPrimitives.h>
#include <fabric/graphics/Geometry.h>
namespace facebook {
namespace react {
/*
* Unified layout constraints for measuring.
*/
struct LayoutConstraints {
Size minimumSize;
Size maximumSize;
LayoutDirection layoutDirection;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <unordered_set>
#include <fabric/core/LayoutableShadowNode.h>
#include <fabric/graphics/Geometry.h>
namespace facebook {
namespace react {
/*
* LayoutContext: Additional contextual information useful for particular
* layout approaches.
*/
struct LayoutContext {
/*
* Compound absolute position of the node relative to the root node.
*/
Point absolutePosition {0, 0};
/*
* Collection of shadow nodes which were chanded during the layout pass,
* and which associated views might need to be updated.
*/
std::shared_ptr<std::unordered_set<SharedLayoutableShadowNode>> affectedShadowNodes {nullptr};
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <fabric/core/LayoutPrimitives.h>
#include <fabric/graphics/Geometry.h>
namespace facebook {
namespace react {
/*
* Describes results of layout process for partucular shadow node.
*/
struct LayoutMetrics {
Rect frame;
EdgeInsets contentInsets {0};
EdgeInsets borderWidth {0};
DisplayType displayType {Flex};
LayoutDirection layoutDirection {Undefined};
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,33 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
namespace facebook {
namespace react {
/*
* Defines visibility of the shadow node and partucular layout
* engine which should be used for laying out the node.
*/
enum DisplayType {
None,
Flex,
Inline,
};
/*
* User interface layout direction.
*/
enum LayoutDirection {
Undefined,
LeftToRight,
RightToLeft,
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,97 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "LayoutableShadowNode.h"
#include <fabric/core/LayoutConstraints.h>
#include <fabric/core/LayoutContext.h>
#include <fabric/core/LayoutMetrics.h>
namespace facebook {
namespace react {
LayoutMetrics LayoutableShadowNode::getLayoutMetrics() const {
return layoutMetrics_;
}
bool LayoutableShadowNode::setLayoutMetrics(LayoutMetrics layoutMetrics) {
layoutMetrics_ = layoutMetrics;
return true;
}
void LayoutableShadowNode::cleanLayout() {
isLayoutClean_ = true;
}
void LayoutableShadowNode::dirtyLayout() {
isLayoutClean_ = false;
}
bool LayoutableShadowNode::getIsLayoutClean() const {
return isLayoutClean_;
}
bool LayoutableShadowNode::getHasNewLayout() const {
return hasNewLayout_;
};
void LayoutableShadowNode::setHasNewLayout(bool hasNewLayout) {
hasNewLayout_ = hasNewLayout;
}
Size LayoutableShadowNode::measure(LayoutConstraints layoutConstraints) const {
return Size();
}
Float LayoutableShadowNode::firstBaseline(Size size) const {
return 0;
}
Float LayoutableShadowNode::lastBaseline(Size size) const {
return 0;
}
void LayoutableShadowNode::layout(LayoutContext layoutContext) {
ensureUnsealed();
layoutChildren(layoutContext);
for (auto child : getChildren()) {
if (!child->getHasNewLayout()) {
continue;
}
// The assumption:
// All `sealed` children were replaced with not-yet-sealed clones
// somewhere in `layoutChildren`.
auto nonConstChild = std::const_pointer_cast<LayoutableShadowNode>(child);
nonConstChild->setHasNewLayout(false);
const LayoutMetrics childLayoutMetrics = nonConstChild->getLayoutMetrics();
if (childLayoutMetrics.displayType == None) {
continue;
}
LayoutContext childLayoutContext = LayoutContext(layoutContext);
childLayoutContext.absolutePosition += childLayoutMetrics.frame.origin;
nonConstChild->layout(layoutContext);
}
}
void LayoutableShadowNode::layoutChildren(LayoutContext layoutContext) {
ensureUnsealed();
// Default implementation does nothing.
}
SharedLayoutableShadowNode LayoutableShadowNode::cloneAndReplaceChild(const SharedLayoutableShadowNode &child) {
return child;
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,113 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <array>
#include <cmath>
#include <vector>
#include <fabric/core/LayoutMetrics.h>
#include <fabric/core/Sealable.h>
namespace facebook {
namespace react {
struct LayoutConstraints;
struct LayoutContext;
class LayoutableShadowNode;
using SharedLayoutableShadowNode = std::shared_ptr<const LayoutableShadowNode>;
using SharedLayoutableShadowNodeList = std::vector<const SharedLayoutableShadowNode>;
using LayoutableShadowNodeIterator = std::iterator<std::input_iterator_tag, const SharedLayoutableShadowNode>;
/*
* Describes all sufficient layout API (in approach-agnostic way)
* which makes a concurrent layout possible.
*/
class LayoutableShadowNode:
public virtual Sealable {
public:
/*
* Measures the node (and node content, propbably recursivly) with
* given constrains and relying on possible layout.
* Default implementation returns zero size.
*/
virtual Size measure(LayoutConstraints layoutConstraints) const;
/*
* Computes layout recusively.
* Additional environmental constraints might be provided via `layoutContext`
* argument.
* Default implementation basically calls `layoutChildren()` and then `layout()`
* (recursively), and provides some obvious performance optimization.
*/
virtual void layout(LayoutContext layoutContext);
/*
* Returns layout metrics computed during previous layout pass.
*/
virtual LayoutMetrics getLayoutMetrics() const;
protected:
/*
* Clean or Dirty layout state:
* Indicates whether all nodes (and possibly their subtrees) along the path
* to the root node should be re-layouted.
*/
virtual void cleanLayout();
virtual void dirtyLayout();
virtual bool getIsLayoutClean() const;
/*
* Indicates does the shadow node (or any descendand node of the node)
* get a new layout metrics during a previous layout pass.
*/
virtual void setHasNewLayout(bool hasNewLayout);
virtual bool getHasNewLayout() const;
/*
* Applies layout for all children;
* does not call anything in recusive manner *by desing*.
*/
virtual void layoutChildren(LayoutContext layoutContext);
/*
* Unifed methods to access text layout metrics.
*/
virtual Float firstBaseline(Size size) const;
virtual Float lastBaseline(Size size) const;
/*
* Returns layoutable children to interate on.
*/
virtual SharedLayoutableShadowNodeList getChildren() const = 0;
/*
* In case layout algorithm needs to mutate this (probably sealed) node,
* it has to clone and replace it in the hierarchy before to do so.
* Default implementation does nothing and returns `child`.
*/
virtual SharedLayoutableShadowNode cloneAndReplaceChild(const SharedLayoutableShadowNode &child);
/*
* Sets layout metrics for the shadow node.
* Returns true if the metrics are different from previous ones.
*/
virtual bool setLayoutMetrics(LayoutMetrics layoutMetrics);
private:
LayoutMetrics layoutMetrics_ {};
bool hasNewLayout_ {false};
bool isLayoutClean_ {false};
};
} // namespace react
} // namespace facebook