blob: eeac11c847efb1f174466042ae7598844eda2d45 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "layout.h"
#include <tuple>
using namespace WeexCore;
namespace WeexCore {
/**
* Entry function to calculate layout
*/
void WXCoreLayoutNode::calculateLayout(const std::pair<float,float> &renderPageSize) {
BFCs.clear();
initFormatingContext(BFCs);
auto bfcDimension = calculateBFCDimension(renderPageSize);
if (std::get<0>(bfcDimension) || isDirty()) {
mChildrenFrozen.assign(getChildCount(kNonBFC), false);
measure(std::get<1>(bfcDimension), std::get<2>(bfcDimension), true);
checkSizeConstraints(this, false);
}
layout(mCssStyle->mMargin.getMargin(kMarginLeft),
mCssStyle->mMargin.getMargin(kMarginTop),
mCssStyle->mMargin.getMargin(kMarginLeft) + getLayoutWidth(),
mCssStyle->mMargin.getMargin(kMarginTop) + getLayoutHeight(),
false, &renderPageSize);
for (Index i = 0; i < getChildCount(kBFC); ++i) {
WXCoreLayoutNode *child = getChildAt(kBFC, i);
child->calculateLayout(renderPageSize);
}
}
void WXCoreLayoutNode::initFormatingContext(std::vector<WXCoreLayoutNode *> &BFCs) {
NonBFCs.clear();
for(auto it = ChildListIterBegin(); it != ChildListIterEnd(); it++) {
WXCoreLayoutNode* child = *it;
if (child != nullptr) {
if (isBFC(child)) {
BFCs.push_back(child);
} else {
NonBFCs.push_back(child);
child->initFormatingContext(BFCs);
}
}
}
reset();
}
std::tuple<bool, float, float> WXCoreLayoutNode::calculateBFCDimension(const std::pair<float,float>& renderPageSize) {
bool sizeChanged = false;
float width = mCssStyle->mStyleWidth, height = mCssStyle->mStyleHeight;
std::pair<bool,float> ret;
if (isBFC(this)) {
ret = calculateBFCWidth(width, renderPageSize.first);
sizeChanged |=ret.first;
width = ret.second;
ret = calculateBFCHeight(height,renderPageSize.second);
sizeChanged |=ret.first;
height = ret.second;
}
return std::make_tuple(sizeChanged, width, height);
}
std::pair<bool,float> WXCoreLayoutNode::calculateBFCWidth(float width, const float renderPageWidth){
bool sizeChanged = false;
if (isnan(width) &&
mParent != nullptr &&
!isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft)) &&
!isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeRight))) {
float containingBlockWidth = NAN;
switch (mCssStyle->mPositionType) {
case kAbsolute:
containingBlockWidth = mParent->mLayoutResult->mLayoutSize.width;
break;
case kFixed:
if (!isnan(renderPageWidth)) {
containingBlockWidth = renderPageWidth;
}
break;
}
if (!isnan(containingBlockWidth)) {
width = containingBlockWidth -
mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft) -
mCssStyle->mStylePosition.getPosition(kPositionEdgeRight);
if (!isnan(mCssStyle->mMargin.getMargin(kMarginLeft))) {
width -= mCssStyle->mMargin.getMargin(kMarginLeft);
}
if (!isnan(mCssStyle->mMargin.getMargin(kMarginRight))) {
width -= mCssStyle->mMargin.getMargin(kMarginRight);
}
setWidthMeasureMode(kExactly);
sizeChanged = true;
}
}
return std::make_pair(sizeChanged, width);
}
std::pair<bool,float> WXCoreLayoutNode::calculateBFCHeight(float height, const float renderPageHeight){
bool sizeChanged = false;
if (isnan(mCssStyle->mStyleHeight) &&
mParent != nullptr &&
!isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeTop)) &&
!isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom))) {
float containingBlockHeight = NAN;
switch (mCssStyle->mPositionType) {
case kAbsolute:
containingBlockHeight = mParent->mLayoutResult->mLayoutSize.height;
break;
case kFixed:
if (!isnan(renderPageHeight)) {
containingBlockHeight = renderPageHeight;
}
break;
}
if (!isnan(containingBlockHeight)) {
height = containingBlockHeight -
mCssStyle->mStylePosition.getPosition(kPositionEdgeTop) -
mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom);
if (!isnan(mCssStyle->mMargin.getMargin(kMarginTop))) {
height -= mCssStyle->mMargin.getMargin(kMarginTop);
}
if (!isnan(mCssStyle->mMargin.getMargin(kMarginBottom))) {
height -= mCssStyle->mMargin.getMargin(kMarginBottom);
}
setHeightMeasureMode(kExactly);
sizeChanged = true;
}
}
return std::make_pair(sizeChanged, height);
}
void WXCoreLayoutNode::measure(const float width, const float height, const bool hypotheticalMeasurment){
if(hypotheticalMeasurment){
//Only BFC will enter this case.
hypotheticalMeasure(width, height);
}
if(getChildCount(kNonBFC) > 0){
if((isMainAxisHorizontal(this) && widthDirty) || (!isMainAxisHorizontal(this) && heightDirty)){
measureInternalNode(width, height, false, false);
}
determineMainSize(width, height);
determineCrossSize(width, height, true);
measureInternalNode(width, height, true, false);
determineCrossSize(width, height, false);
}
else {
if (widthDirty || heightDirty) {
measureLeafNode(width, height, hypotheticalMeasurment, false);
}
}
clearDirty();
}
void WXCoreLayoutNode::hypotheticalMeasure(const float width, const float height, const bool stretch){
if (getChildCount(kNonBFC) > 0) {
measureInternalNode(width, height, true, true);
} else {
measureLeafNode(width, height, true, stretch);
}
widthDirty = false;
heightDirty = false;
mLayoutResult->mLayoutSize.hypotheticalWidth = mLayoutResult->mLayoutSize.width;
mLayoutResult->mLayoutSize.hypotheticalHeight = mLayoutResult->mLayoutSize.height;
}
void WXCoreLayoutNode::measureLeafNode(float width, float height, const bool hypothetical, const bool stretch) {
if ((measureFunc != nullptr) &&
(widthMeasureMode == kUnspecified
|| heightMeasureMode == kUnspecified)) {
float constrainsWidth = width;
if(isnan(width)){
if(!isnan(mCssStyle->mMaxWidth)){
constrainsWidth = mCssStyle->mMaxWidth;
}
}
if((!isnan(width)&&widthMeasureMode == kExactly) ||
(isnan(width) && !isnan(mCssStyle->mMaxWidth))) {
constrainsWidth -= sumPaddingBorderAlongAxis(this, true);
}
WXCoreSize dimension = measureFunc(this, constrainsWidth,
(stretch && !isnan(width)) ? kExactly:widthMeasureMode,
height, heightMeasureMode);
if (widthMeasureMode == kUnspecified) {
float actualWidth = dimension.width + sumPaddingBorderAlongAxis(this, true);
if (isnan(width)) {
width = actualWidth;
} else if (!stretch) {
width = std::min(width, actualWidth);
}
}
if (heightMeasureMode == kUnspecified) {
float actualHeight = dimension.height + sumPaddingBorderAlongAxis(this, false);
if (isnan(height)) {
height = actualHeight;
} else if (!stretch) {
height = std::min(height, actualHeight);
}
}
} else {
width = widthMeasureMode == kUnspecified ? sumPaddingBorderAlongAxis(this, true)
: width;
height = heightMeasureMode == kUnspecified ? sumPaddingBorderAlongAxis(this, false)
: height;
}
setMeasuredDimension(width, height);
}
/**
* Determine the main size by expanding the individual flexGrow attribute.
*/
void WXCoreLayoutNode::determineMainSize(const float width, const float height) {
bool horizontal = isMainAxisHorizontal(this);
if((horizontal && widthMeasureMode == kExactly) || (!horizontal && heightMeasureMode == kExactly)) {
//The measureMode along main axis is exactly
float maxMainSize = horizontal ? width: height;
maxMainSize -= sumPaddingBorderAlongAxis(this, isMainAxisHorizontal(this));
Index childIndex = 0;
for (WXCoreFlexLine *flexLine : mFlexLines) {
childIndex = expandItemsInFlexLine(flexLine, maxMainSize, childIndex);
}
}
}
/**
* @param flexDirection the flex direction attribute
* @param width stylewidth by this node
* @param height styleheight by this node
* @param paddingAlongCrossAxis the padding value for this node along the cross axis
*/
void WXCoreLayoutNode::determineCrossSize(const float width, const float height, const bool stretch) {
if (mFlexLines.size() == 1 && isCrossExactly()) {
bool horizontal = isMainAxisHorizontal(this);
float size = mFlexLines[0]->mCrossSize;
float paddingAlongCrossAxis = sumPaddingBorderAlongAxis(this, !horizontal);
if (horizontal) {
if (heightMeasureMode == kExactly) {
size = height - paddingAlongCrossAxis;
}
} else {
if (widthMeasureMode == kExactly) {
size = width - paddingAlongCrossAxis;
}
}
mFlexLines[0]->mCrossSize = size;
}
if (stretch) {
stretchViewCrossSize();
}
}
void WXCoreLayoutNode::measureInternalNode(const float width, const float height, const bool needMeasure,
const bool hypotheticalMeasurment) {
for (WXCoreFlexLine *flexLine : mFlexLines) {
if(flexLine!= nullptr) {
delete flexLine;
}
flexLine = nullptr;
}
mFlexLines.clear();
Index childCount = getChildCount(kNonBFC);
WXCoreFlexLine *flexLine = new WXCoreFlexLine();
for (Index i = 0; i < childCount; i++) {
WXCoreLayoutNode *child = getChildAt(kNonBFC, i);
if (child->mCssStyle->mAlignSelf == kAlignSelfStretch) {
flexLine->mIndicesAlignSelfStretch.push_back(i);
}
measureChild(child, flexLine->mMainSize, width, height, needMeasure, hypotheticalMeasurment);
checkSizeConstraints(child, hypotheticalMeasurment);
if (isWrapRequired(width, height, flexLine->mMainSize,
calcItemSizeAlongAxis(child, isMainAxisHorizontal(this)))) {
if (flexLine->mItemCount > 0) {
mFlexLines.push_back(flexLine);
}
flexLine = new WXCoreFlexLine();
flexLine->mItemCount = 1;
} else {
flexLine->mItemCount++;
}
updateCurrentFlexline(childCount, flexLine, i, child, hypotheticalMeasurment || (!hypotheticalMeasurment && !needMeasure));
}
setMeasuredDimensionForFlex(width, widthMeasureMode, height, heightMeasureMode);
}
void WXCoreLayoutNode::updateCurrentFlexline(const Index childCount, WXCoreFlexLine* const flexLine, const Index i,
const WXCoreLayoutNode* const child, const bool useHypotheticalSize){
flexLine->mMainSize += calcItemSizeAlongAxis(child, isMainAxisHorizontal(this), useHypotheticalSize);
sumFlexGrow(child, flexLine, i);
flexLine->mCrossSize =
std::max(flexLine->mCrossSize, calcItemSizeAlongAxis(child, !isMainAxisHorizontal(this), useHypotheticalSize));
if (i == childCount - 1 && flexLine->mItemCount != 0) {
mFlexLines.push_back(flexLine);
}
}
void WXCoreLayoutNode::measureChild(WXCoreLayoutNode* const child, const float currentMainSize,
const float parentWidth, const float parentHeight,
const bool needMeasure, const bool hypotheticalMeasurment) {
if (needMeasure && child->isDirty()) {
if (hypotheticalMeasurment) {
float childWidth = child->mCssStyle->mStyleWidth;
float childHeight = child->mCssStyle->mStyleHeight;
bool stretch = !isMainAxisHorizontal(this) &&
child->measureFunc != nullptr &&
widthMeasureMode == kExactly &&
isSingleFlexLine(parentHeight) &&
((child->mCssStyle->mAlignSelf == kAlignSelfStretch) ||
(mCssStyle->mAlignItems == kAlignItemsStretch
&& child->mCssStyle->mAlignSelf == kAlignSelfAuto));
adjustChildSize(child, currentMainSize, parentWidth,
parentHeight, childWidth, childHeight);
child->hypotheticalMeasure(childWidth, childHeight, stretch);
} else {
if(isSingleFlexLine(isMainAxisHorizontal(this) ? parentWidth : parentHeight)
&& !isMainAxisHorizontal(this) && child->widthMeasureMode == kUnspecified){
child->setLayoutWidth(parentWidth - sumPaddingBorderAlongAxis(this, true)
-child->mCssStyle->sumMarginOfDirection(true));
if(child->heightMeasureMode == kUnspecified && child->widthDirty) {
child->mLayoutResult->mLayoutSize.height = NAN;
}
}
child->measure(child->mLayoutResult->mLayoutSize.width,
child->mLayoutResult->mLayoutSize.height, hypotheticalMeasurment);
}
}
}
void WXCoreLayoutNode::adjustChildSize(const WXCoreLayoutNode *child,
const float currentMainSize,
const float parentWidth,
const float parentHeight,
float &childWidth,
float &childHeight) const {
if(child->measureFunc == nullptr) {
if(!isnan(childWidth)){
childWidth = std::max(childWidth, child->sumPaddingBorderAlongAxis(child, true));
}
if(!isnan(childHeight)){
childHeight = std::max(childHeight, child->sumPaddingBorderAlongAxis(child, false));
}
}
if (isSingleFlexLine(isMainAxisHorizontal(this) ? parentWidth : parentHeight)) {
if (isMainAxisHorizontal(this)) {
if (!isnan(parentHeight) && isnan(child->mCssStyle->mStyleHeight)
&& child->mCssStyle->mAlignSelf == kAlignSelfAuto
&& mCssStyle->mAlignItems == kAlignItemsStretch) {
childHeight = parentHeight - sumPaddingBorderAlongAxis(this, false) -
child->mCssStyle->sumMarginOfDirection(false);
}
} else {
if (!isnan(parentWidth) && isnan(child->mCssStyle->mStyleWidth)) {
childWidth = parentWidth - sumPaddingBorderAlongAxis(this, true) -
child->mCssStyle->sumMarginOfDirection(true);
}
}
}
}
void WXCoreLayoutNode::checkSizeConstraints(WXCoreLayoutNode* const node, const bool hypotheticalMeasurment) {
bool widthRemeasure = false, heightRemeasure = false;
float nodeWidth,nodeHeight;
nodeWidth = node->mLayoutResult->mLayoutSize.width;
nodeHeight = node->mLayoutResult->mLayoutSize.height;
if (!isnan(node->mCssStyle->mMinWidth) &&
nodeWidth < node->mCssStyle->mMinWidth) {
widthRemeasure = true;
nodeWidth = node->mCssStyle->mMinWidth;
} else if (!isnan(node->mCssStyle->mMaxWidth)
&& nodeWidth > node->mCssStyle->mMaxWidth) {
widthRemeasure = true;
nodeWidth = node->mCssStyle->mMaxWidth;
}
if (!isnan(node->mCssStyle->mMinHeight) &&
nodeHeight < node->mCssStyle->mMinHeight) {
heightRemeasure = true;
nodeHeight = node->mCssStyle->mMinHeight;
} else if (!isnan(node->mCssStyle->mMaxHeight) &&
nodeHeight > node->mCssStyle->mMaxHeight) {
heightRemeasure = true;
nodeHeight = node->mCssStyle->mMaxHeight;
}
node->setWidthMeasureMode(widthRemeasure ? kExactly : node->widthMeasureMode);
node->setHeightMeasureMode(heightRemeasure ? kExactly : node->heightMeasureMode);
if (hypotheticalMeasurment) {
if (widthRemeasure) {
node->setLayoutWidth(nodeWidth);
node->mLayoutResult->mLayoutSize.hypotheticalWidth = nodeWidth;
}
if (heightRemeasure) {
node->setLayoutHeight(nodeHeight);
node->mLayoutResult->mLayoutSize.hypotheticalHeight = nodeHeight;
}
} else {
if (widthRemeasure || heightRemeasure) {
node->measure(nodeWidth, nodeHeight, hypotheticalMeasurment);
}
}
}
Index WXCoreLayoutNode::expandItemsInFlexLine(WXCoreFlexLine* const flexLine,
const float maxMainSize,
const Index startIndex) {
Index childIndex = startIndex;
if (flexLine->mTotalFlexGrow <= 0) {
childIndex += flexLine->mItemCount;
} else {
bool needsReexpand = false;
const float unitSpace = (maxMainSize - flexLine->mMainSize + flexLine->mTotalFlexibleSize) /
(flexLine->mTotalFlexGrow > 1 ? flexLine->mTotalFlexGrow : 1);
float sizeBeforeExpand = flexLine->mMainSize;
flexLine->mMainSize = 0;
for (Index i = 0; i < flexLine->mItemCount; i++) {
WXCoreLayoutNode *child = getChildAt(kNonBFC, childIndex);
if (!mChildrenFrozen[childIndex]) {
float childSizeAlongMainAxis = unitSpace * child->mCssStyle->mFlexGrow;
std::pair<bool, float> limitSize = limitChildMainSize(flexLine, child,
childSizeAlongMainAxis, childIndex);
needsReexpand = limitSize.first;
adjustChildSize(child, limitSize.second);
}
flexLine->mMainSize += calcItemSizeAlongAxis(child, isMainAxisHorizontal(this));
childIndex++;
}
if (needsReexpand && sizeBeforeExpand != flexLine->mMainSize) {
// Re-invoke the method with the same startIndex to distribute the positive free space
// that wasn't fully distributed (because of maximum/minimum length constraint)
expandItemsInFlexLine(flexLine, maxMainSize, startIndex);
}
}
return childIndex;
}
void WXCoreLayoutNode::adjustChildSize(WXCoreLayoutNode* const child, const float childMainSize) {
if (isMainAxisHorizontal(this)) {
child->setWidthMeasureMode(kExactly);
child->setLayoutWidth(childMainSize);
} else {
child->setHeightMeasureMode(kExactly);
child->setLayoutHeight(childMainSize);
}
}
void WXCoreLayoutNode::stretchViewCrossSize(){
if (mCssStyle->mAlignItems == kAlignItemsStretch) {
Index viewIndex = 0;
for (Index i = 0; i< mFlexLines.size(); i++ ) {
WXCoreFlexLine *flexLine = mFlexLines.at(i);
for (Index j = 0; j < flexLine->mItemCount; j++, viewIndex++) {
WXCoreLayoutNode* child = getChildAt(kNonBFC, viewIndex);
if (child->mCssStyle->mAlignSelf == kAlignSelfAuto ||
child->mCssStyle->mAlignSelf == kAlignSelfStretch) {
stretchViewCrossSize(child, flexLine->mCrossSize);
}
}
}
} else {
for (WXCoreFlexLine *flexLine : mFlexLines) {
for (auto index : flexLine->mIndicesAlignSelfStretch) {
stretchViewCrossSize(getChildAt(kNonBFC, index), flexLine->mCrossSize);
}
}
}
}
void WXCoreLayoutNode::stretchViewCrossSize(WXCoreLayoutNode* const child, float crossSize){
if (isMainAxisHorizontal(this)) {
if (child->heightMeasureMode != kExactly) {
crossSize -=
child->mCssStyle->mMargin.getMargin(kMarginTop) +
child->mCssStyle->mMargin.getMargin(kMarginBottom);
child->setHeightMeasureMode(kExactly);
child->setLayoutHeight(std::max(0.f, crossSize));
}
} else {
if (child->widthMeasureMode != kExactly) {
crossSize -=
child->mCssStyle->mMargin.getMargin(kMarginLeft) +
child->mCssStyle->mMargin.getMargin(kMarginRight);
child->setWidthMeasureMode(kExactly);
child->setLayoutWidth(std::max(0.f, crossSize));
}
}
}
void WXCoreLayoutNode::setFrame(const float l, const float t, const float r, const float b) {
if (mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeLeft) != l
|| mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeTop) != t
|| mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeRight) != r
|| mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeBottom) != b) {
setHasNewLayout(true);
setFrame(&mLayoutResult->mLayoutPosition, l, t, r, b);
}
}
void WXCoreLayoutNode::setFrame(WXCorePosition* position,const float l, const float t, const float r, const float b){
position->setPosition(kPositionEdgeLeft, l);
position->setPosition(kPositionEdgeTop, t);
position->setPosition(kPositionEdgeRight, r);
position->setPosition(kPositionEdgeBottom, b);
}
void WXCoreLayoutNode::layout(float left, float top, float right, float bottom, const bool absoluteFlexItem, const std::pair<float,float>* const renderPageSize) {
if(absoluteFlexItem) {
absoultePositon = new WXCorePosition();
setFrame(absoultePositon, left, top, right, bottom);
}
else{
switch (mCssStyle->mPositionType) {
case kFixed:
case kAbsolute:
calcAbsoluteOffset(left, top, right, bottom, renderPageSize);
break;
default:
case kRelative:
calcRelativeOffset(left, top, right, bottom);
break;
}
setFrame(left, top, right, bottom);
onLayout(left, top, right, bottom);
}
}
void WXCoreLayoutNode::calcRelativeOffset(float &left, float &top, float &right, float &bottom) const {
if (!isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft))) {
left += mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft);
right += mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft);
} else if (!isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeRight))) {
left -= mCssStyle->mStylePosition.getPosition(kPositionEdgeRight);
right -= mCssStyle->mStylePosition.getPosition(kPositionEdgeRight);
}
if (!isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeTop))) {
top += mCssStyle->mStylePosition.getPosition(kPositionEdgeTop);
bottom += mCssStyle->mStylePosition.getPosition(kPositionEdgeTop);
} else if (!isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom))) {
top -= mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom);
bottom -= mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom);
}
}
void WXCoreLayoutNode::calcAbsoluteOffset(float &left, float &top, float &right, float &bottom, const std::pair<float,float>* const renderPageSize){
WXCorePadding parentPadding;
WXCoreBorderWidth parentBorder;
WXCoreSize parentSize;
if (mCssStyle->mPositionType == kAbsolute && mParent != nullptr) {
parentPadding = mParent->mCssStyle->mPadding;
parentBorder = mParent->mCssStyle->mBorderWidth;
parentSize = mParent->mLayoutResult->mLayoutSize;
positionAbsoluteFlexItem(left, top, right, bottom);
} else if(mCssStyle->mPositionType == kFixed && renderPageSize!= nullptr){
parentSize.width = renderPageSize->first;
parentSize.height = renderPageSize->second;
}
updateLeftRightForAbsolute(left, right, parentPadding, parentBorder, parentSize);
updateTopBottomForAbsolute(top, bottom, parentPadding, parentBorder, parentSize);
}
void WXCoreLayoutNode::positionAbsoluteFlexItem(float &left, float &top, float &right, float &bottom){
if ((isnan(getStylePositionLeft()) && isnan(getStylePositionRight())) ||
(isnan(getStylePositionTop()) && isnan(getStylePositionBottom()))) {
WXCoreFlexLine tempLine;
mParent->updateFlexLineForAbsoluteItem(this, &tempLine);
mParent->onLayout(mParent->getLayoutPositionLeft(),
mParent->getLayoutPositionTop(),
mParent->getLayoutPositionRight(),
mParent->getLayoutPositionBottom(),
this, &tempLine);
if(absoultePositon != nullptr) {
if (isnan(getStylePositionLeft()) && isnan(getStylePositionRight())) {
left = absoultePositon->getPosition(kPositionEdgeLeft);
right = absoultePositon->getPosition(kPositionEdgeRight);
}
if (isnan(getStylePositionTop()) && isnan(getStylePositionBottom())) {
top = absoultePositon->getPosition(kPositionEdgeTop);
bottom = absoultePositon->getPosition(kPositionEdgeBottom);
}
delete absoultePositon;
absoultePositon = nullptr;
}
}
}
void WXCoreLayoutNode::updateFlexLineForAbsoluteItem(WXCoreLayoutNode *const absoluteFlexItem, WXCoreFlexLine *const flexLine){
flexLine->mMainSize = isMainAxisHorizontal(this) ?
absoluteFlexItem->getLayoutWidth() + absoluteFlexItem->getMarginLeft()
+ absoluteFlexItem->getMarginRight() :
absoluteFlexItem->getLayoutHeight() + absoluteFlexItem->getMarginTop()
+ absoluteFlexItem->getMarginBottom();
flexLine->mCrossSize = isMainAxisHorizontal(this) ?
absoluteFlexItem->getLayoutHeight() + absoluteFlexItem->getMarginTop()
+ absoluteFlexItem->getMarginBottom() :
absoluteFlexItem->getLayoutWidth() + absoluteFlexItem->getMarginLeft()
+ absoluteFlexItem->getMarginRight();
flexLine->mItemCount = 1;
}
void WXCoreLayoutNode::onLayout(const float left, const float top, const float right, const float bottom,
WXCoreLayoutNode *const absoulteItem, WXCoreFlexLine *const flexLine) {
switch (mCssStyle->mFlexDirection) {
case kFlexDirectionRow:
layoutHorizontal(false, left, top, right, bottom, absoulteItem, flexLine);
break;
case kFlexDirectionRowReverse:
layoutHorizontal(true, left, top, right, bottom, absoulteItem, flexLine);
break;
case kFlexDirectionColumnReverse:
layoutVertical(mCssStyle->mFlexWrap == kWrapReverse, true, left, top, right, bottom, absoulteItem, flexLine);
break;
case kFlexDirectionColumn:
default:
layoutVertical(mCssStyle->mFlexWrap == kWrapReverse, false, left, top, right, bottom, absoulteItem, flexLine);
break;
}
}
/**
* Sub method for {@link WXCoreLayoutNode #onLayout(int, int, int, int)} when the
* {@link #mFlexDirection} is either {@link WXCoreFlexDirection #WXCore_Flex_Direction_Row} or
* {@link WXCoreFlexDirection #WXCore_Flex_Direction_Row_REVERSE}.
*
* @param isRtl {@code true} if the horizontal layout direction is mStyleRight to mStyleLeft, {@code
* false} otherwise.
* @param left the mStyleLeft position of this View
* @param top the mStyleTop position of this View
* @param right the mStyleRight position of this View
* @param bottom the mStyleBottom position of this View
*/
void WXCoreLayoutNode::layoutHorizontal(const bool isRtl,
const float left, const float top,
const float right, const float bottom,
WXCoreLayoutNode *const absoulteItem,
WXCoreFlexLine *const flexLine) {
Index currentViewIndex = 0;
float height = bottom - top;
float width = right - left;
// childBottom is used if the mFlexWrap is FLEX_WRAP_WRAP_REVERSE otherwise
// childTop is used to align the vertical position of the children views.
float childBottom = height - getPaddingBottom() - getBorderWidthBottom();
float childTop = getPaddingTop() + getBorderWidthTop();
// Used only for RTL layout
// Use float to reduce the round error that may happen in when justifyContent ==
// SPACE_BETWEEN or SPACE_AROUND
float childLeft, childRight;
const std::vector<WXCoreFlexLine*> &lines = (flexLine == nullptr? mFlexLines: std::vector<WXCoreFlexLine*>{flexLine});
for (WXCoreFlexLine *flexLine: lines) {
float spaceBetweenItem = 0.f;
layoutFlexlineHorizontal(width, flexLine, childLeft, childRight, spaceBetweenItem);
spaceBetweenItem = std::max(spaceBetweenItem, 0.f);
if(absoulteItem == nullptr) {
for (Index j = 0; j < flexLine->mItemCount; j++) {
WXCoreLayoutNode *child = getChildAt(kNonBFC, currentViewIndex);
if (child == nullptr) {
continue;
}
layoutSingleChildHorizontal(isRtl, false, childBottom, childTop,
flexLine, child, childLeft, childRight);
childLeft += child->mLayoutResult->mLayoutSize.width + spaceBetweenItem + child->getMarginRight();
childRight -= child->mLayoutResult->mLayoutSize.width + spaceBetweenItem + child->getMarginLeft();
currentViewIndex++;
}
childTop += flexLine->mCrossSize;
childBottom -= flexLine->mCrossSize;
}
else{
layoutSingleChildHorizontal(isRtl, true, childBottom, childTop,
flexLine, absoulteItem, childLeft, childRight);
}
}
}
void WXCoreLayoutNode::layoutFlexlineHorizontal(const float width,
const WXCoreFlexLine *const flexLine,
float &childLeft,
float &childRight,
float &spaceBetweenItem) const {
Index visibleCount, visibleItem;
float denominator;
switch (mCssStyle->mJustifyContent) {
case kJustifyFlexEnd:
childLeft = width - flexLine->mMainSize - getPaddingRight() - getBorderWidthRight();
childRight = width - getPaddingLeft() - getBorderWidthLeft();
break;
case kJustifyCenter:
childLeft = (width - flexLine->mMainSize - mCssStyle->sumPaddingBorderOfEdge(kRight)
+ mCssStyle->sumPaddingBorderOfEdge(kLeft)) / 2;
childRight = childLeft + flexLine->mMainSize;
break;
case kJustifySpaceAround:
visibleCount = flexLine->mItemCount;
if (visibleCount != 0) {
spaceBetweenItem =
(width - flexLine->mMainSize - sumPaddingBorderAlongAxis(this, true)) / visibleCount;
}
childLeft = getPaddingLeft() + getBorderWidthLeft() + spaceBetweenItem / 2.f;
childRight = width - getPaddingRight() - getBorderWidthRight() - spaceBetweenItem / 2.f;
break;
case kJustifySpaceBetween:
childLeft = getPaddingLeft() + getBorderWidthLeft();
visibleItem = flexLine->mItemCount;
denominator = visibleItem != 1 ? visibleItem - 1 : 1.f;
spaceBetweenItem =
(width - flexLine->mMainSize - sumPaddingBorderAlongAxis(this, true)) / denominator;
childRight = width - getPaddingRight() - getBorderWidthRight();
break;
case kJustifyFlexStart:
default:
childLeft = getPaddingLeft() + getBorderWidthLeft();
childRight = width - getPaddingRight() - getBorderWidthRight();
break;
}
}
void WXCoreLayoutNode::layoutSingleChildHorizontal(const bool isRtl, const bool absoulteItem,
float childBottom, float childTop,
WXCoreFlexLine *const flexLine,
WXCoreLayoutNode *const child,
float &childLeft, float &childRight) {
childLeft += child->getMarginLeft();
childRight -= child->getMarginRight();
if (mCssStyle->mFlexWrap == kWrapReverse) {
if (isRtl) {
layoutSingleChildHorizontal(child,
flexLine,
mCssStyle->mFlexWrap,
mCssStyle->mAlignItems,
childRight - child->mLayoutResult->mLayoutSize.width,
childBottom - child->mLayoutResult->mLayoutSize.height,
childRight,
childBottom,
absoulteItem);
} else {
layoutSingleChildHorizontal(child,
flexLine,
mCssStyle->mFlexWrap,
mCssStyle->mAlignItems,
childLeft,
childBottom - child->mLayoutResult->mLayoutSize.height,
childLeft + child->mLayoutResult->mLayoutSize.width,
childBottom,
absoulteItem);
}
} else {
if (isRtl) {
layoutSingleChildHorizontal(child, flexLine, mCssStyle->mFlexWrap, mCssStyle->mAlignItems,
childRight - child->mLayoutResult->mLayoutSize.width, childTop,
childRight, childTop + child->mLayoutResult->mLayoutSize.height,
absoulteItem);
} else {
layoutSingleChildHorizontal(child, flexLine, mCssStyle->mFlexWrap, mCssStyle->mAlignItems,
childLeft, childTop,
childLeft + child->mLayoutResult->mLayoutSize.width,
childTop + child->mLayoutResult->mLayoutSize.height,
absoulteItem);
}
}
}
/**
* Place a single View when the layout direction is horizontal ({@link #mFlexDirection} is
* either {@link WXCoreFlexDirection #WXCore_Flex_Direction_Row} or
* {@link WXCoreFlexDirection #WXCore_Flex_Direction_Row_REVERSE}).
*
* @param node the View to be placed
* @param flexLine the {@link WXCoreFlexLine} where the View belongs to
* @param flexWrap the flex wrap attribute of this BasicLayoutNode
* @param alignItems the align items attribute of this BasicLayoutNode
* @param left the mStyleLeft position of the View, which the View's margin is already taken
* into account
* @param top the mStyleTop position of the flex line where the View belongs to. The actual
* View's mStyleTop position is shifted depending on the flexWrap and alignItems
* attributes
* @param right the mStyleRight position of the View, which the View's margin is already taken
* into account
* @param bottom the mStyleBottom position of the flex line where the View belongs to. The actual
* View's mStyleBottom position is shifted depending on the flexWrap and alignItems
* attributes
*/
void WXCoreLayoutNode::layoutSingleChildHorizontal(WXCoreLayoutNode *const node, WXCoreFlexLine* const flexLine,
const WXCoreFlexWrap flexWrap, WXCoreAlignItems alignItems,
const float left, const float top, const float right, const float bottom, const bool absoluteFlexItem) {
if (node->mCssStyle->mAlignSelf != kAlignSelfAuto) {
// Expecting the values for alignItems and alignSelf match except for ALIGN_SELF_AUTO.
// Assigning the alignSelf value as alignItems should work.
alignItems = static_cast<WXCoreAlignItems>(node->mCssStyle->mAlignSelf);
}
float crossSize = flexLine->mCrossSize;
switch (alignItems) {
case kAlignItemsFlexStart:
case kAlignItemsStretch:
if (flexWrap != kWrapReverse) {
node->layout(left, top + node->getMarginTop(), right, bottom + node->getMarginTop(), absoluteFlexItem);
} else {
node->layout(left, top - node->getMarginBottom(), right, bottom - node->getMarginBottom(), absoluteFlexItem);
}
break;
case kAlignItemsFlexEnd:
if (flexWrap != kWrapReverse) {
node->layout(left,
top + crossSize - node->mLayoutResult->mLayoutSize.height - node->getMarginBottom(),
right, top + crossSize - node->getMarginBottom(), absoluteFlexItem);
} else {
// If the flexWrap == FLEX_WRAP_WRAP_REVERSE, the direction of the
// flexEnd is flipped (from mStyleTop to mStyleBottom).
node->layout(left, top - crossSize + node->mLayoutResult->mLayoutSize.height + node->getMarginTop(),
right, bottom - crossSize + node->mLayoutResult->mLayoutSize.height + node->getMarginTop(), absoluteFlexItem);
}
break;
case kAlignItemsCenter:
float topFromCrossAxis = (crossSize - node->mLayoutResult->mLayoutSize.height
+ node->getMarginTop() - node->getMarginBottom()) / 2;
if (flexWrap != kWrapReverse) {
node->layout(left, top + topFromCrossAxis,
right, top + topFromCrossAxis + node->mLayoutResult->mLayoutSize.height, absoluteFlexItem);
} else {
node->layout(left, top - topFromCrossAxis,
right, top - topFromCrossAxis + node->mLayoutResult->mLayoutSize.height, absoluteFlexItem);
}
break;
}
}
/**
* Sub method for {@link WXCoreLayoutNode #onLayout(int, int, int, int)} when the
* {@link #mFlexDirection} is either {@link WXCoreFlexDirection #WXCore_Flex_Direction_Column} or
* {@link WXCoreFlexDirection #WXCore_Flex_Direction_Column_Reverse}.
*
* @param isRtl {@code true} if the horizontal layout direction is mStyleRight to mStyleLeft,
* {@code false}
* otherwise
* @param fromBottomToTop {@code true} if the layout direction is mStyleBottom to mStyleTop, {@code false}
* otherwise
* @param left the mStyleLeft position of this View
* @param top the mStyleTop position of this View
* @param right the mStyleRight position of this View
* @param bottom the mStyleBottom position of this View
*/
void
WXCoreLayoutNode::layoutVertical(const bool isRtl,
const bool fromBottomToTop,
const float left, const float top,
const float right, const float bottom,
WXCoreLayoutNode *const absoulteItem,
WXCoreFlexLine *const flexLine) {
float childLeft = getPaddingLeft() + getBorderWidthLeft();
Index currentViewIndex = 0;
float width = right - left;
float height = bottom - top;
// childRight is used if the mFlexWrap is FLEX_WRAP_WRAP_REVERSE otherwise
// childLeft is used to align the horizontal position of the children views.
float childRight = width - getPaddingRight() - getBorderWidthRight();
// Use float to reduce the round error that may happen in when justifyContent ==
// SPACE_BETWEEN or SPACE_AROUND
float childTop, childBottom;
const std::vector<WXCoreFlexLine*> &lines = (flexLine == nullptr? mFlexLines: std::vector<WXCoreFlexLine*>{flexLine});
for (WXCoreFlexLine *flexLine : lines) {
float spaceBetweenItem = 0.f;
layoutFlexlineVertical(height, flexLine, childTop, childBottom, spaceBetweenItem);
spaceBetweenItem = std::max(spaceBetweenItem, 0.f);
if(absoulteItem == nullptr) {
for (Index j = 0; j < flexLine->mItemCount; j++) {
WXCoreLayoutNode *child = getChildAt(kNonBFC, currentViewIndex);
if (child == nullptr) {
continue;
}
layoutSingleChildVertical(isRtl, fromBottomToTop, false,
childLeft, childRight, flexLine,
child, childTop, childBottom);
childTop += child->mLayoutResult->mLayoutSize.height + spaceBetweenItem + child->getMarginBottom();
childBottom -= child->mLayoutResult->mLayoutSize.height + spaceBetweenItem + child->getMarginTop();
currentViewIndex++;
}
childLeft += flexLine->mCrossSize;
childRight -= flexLine->mCrossSize;
}
else{
layoutSingleChildVertical(isRtl, fromBottomToTop, true,
childLeft, childRight, flexLine,
absoulteItem, childTop, childBottom);
}
}
}
void WXCoreLayoutNode::layoutFlexlineVertical(const float height,
const WXCoreFlexLine *const flexLine,
float &childTop,
float &childBottom,
float &spaceBetweenItem) const {
Index visibleCount, visibleItem;
float denominator;
switch (mCssStyle->mJustifyContent) {
case kJustifyFlexEnd:
childTop = height - flexLine->mMainSize - getPaddingBottom() - getBorderWidthBottom();
childBottom = height - getPaddingTop() - getBorderWidthTop();
break;
case kJustifyCenter:
childTop = (height - flexLine->mMainSize - mCssStyle->sumPaddingBorderOfEdge(kBottom)
+ mCssStyle->sumPaddingBorderOfEdge(kTop)) / 2;
childBottom = childTop + flexLine->mMainSize;
break;
case kJustifySpaceAround:
visibleCount = flexLine->mItemCount;
if (visibleCount != 0) {
spaceBetweenItem = (height - flexLine->mMainSize - sumPaddingBorderAlongAxis(this, false))
/ visibleCount;
}
childTop = getPaddingTop() + getBorderWidthTop() + spaceBetweenItem / 2;
childBottom = height - getPaddingBottom() - getBorderWidthBottom() - spaceBetweenItem / 2;
break;
case kJustifySpaceBetween:
childTop = getPaddingTop() + getBorderWidthTop();
visibleItem = flexLine->mItemCount;
denominator = visibleItem != 1 ? visibleItem - 1 : 1.f;
spaceBetweenItem =
(height - flexLine->mMainSize - sumPaddingBorderAlongAxis(this, false)) / denominator;
childBottom = height - getPaddingBottom() - getBorderWidthBottom();
break;
case kJustifyFlexStart:
default:
childTop = getPaddingTop() + getBorderWidthTop();
childBottom = height - getPaddingBottom() - getBorderWidthBottom();
break;
}
}
void WXCoreLayoutNode::layoutSingleChildVertical(const bool isRtl, const bool fromBottomToTop,
const bool absoluteFlexItem,
const float childLeft, const float childRight,
WXCoreFlexLine *const flexLine,
WXCoreLayoutNode *const child,
float &childTop, float &childBottom) {
childTop += child->getMarginTop();
childBottom -= child->getMarginBottom();
if (isRtl) {
if (fromBottomToTop) {
layoutSingleChildVertical(child, flexLine, true,
mCssStyle->mAlignItems,
childRight - child->mLayoutResult->mLayoutSize.width,
childBottom - child->mLayoutResult->mLayoutSize.height,
childRight, childBottom, absoluteFlexItem);
} else {
layoutSingleChildVertical(child, flexLine, true, mCssStyle->mAlignItems,
childRight - child->mLayoutResult->mLayoutSize.width, childTop,
childRight, childTop + child->mLayoutResult->mLayoutSize.height,
absoluteFlexItem);
}
} else {
if (fromBottomToTop) {
layoutSingleChildVertical(child, flexLine, false, mCssStyle->mAlignItems,
childLeft, childBottom - child->mLayoutResult->mLayoutSize.height,
childLeft + child->mLayoutResult->mLayoutSize.width, childBottom,
absoluteFlexItem);
} else {
layoutSingleChildVertical(child, flexLine, false, mCssStyle->mAlignItems,
childLeft, childTop,
childLeft + child->mLayoutResult->mLayoutSize.width,
childTop + child->mLayoutResult->mLayoutSize.height,
absoluteFlexItem);
}
}
}
/**
* Place a single View when the layout direction is vertical ({@link #mFlexDirection} is
* either {@link WXCoreFlexDirection #WXCore_Flex_Direction_Column} or
* {@link WXCoreFlexDirection #WXCore_Flex_Direction_Column_Reverse}).
*
* @param node the View to be placed
* @param flexLine the {@link FlexLine} where the View belongs to
* @param isRtl {@code true} if the layout direction is mStyleRight to mStyleLeft, {@code false}
* otherwise
* @param alignItems the align items attribute of this BasicLayoutNode
* @param left the mStyleLeft position of the flex line where the View belongs to. The actual
* View's mStyleLeft position is shifted depending on the isRtl and alignItems
* attributes
* @param top the mStyleTop position of the View, which the View's margin is already taken
* into account
* @param right the mStyleRight position of the flex line where the View belongs to. The actual
* View's mStyleRight position is shifted depending on the isRtl and alignItems
* attributes
* @param bottom the mStyleBottom position of the View, which the View's margin is already taken
* into account
*/
void WXCoreLayoutNode::layoutSingleChildVertical(WXCoreLayoutNode* const node, WXCoreFlexLine* const flexLine, const bool isRtl,
WXCoreAlignItems alignItems, const float left, const float top, const float right,
const float bottom, const bool absoluteFlexItem) {
if (node->mCssStyle->mAlignSelf != kAlignSelfAuto) {
// Expecting the values for alignItems and alignSelf match except for ALIGN_SELF_AUTO.
// Assigning the alignSelf value as alignItems should work.
alignItems = static_cast<WXCoreAlignItems>(node->mCssStyle->mAlignSelf);
}
float crossSize = flexLine->mCrossSize;
switch (alignItems) {
case kAlignItemsFlexStart:
case kAlignItemsStretch:
if (!isRtl) {
node->layout(left + node->getMarginLeft(), top, right + node->getMarginLeft(), bottom, absoluteFlexItem);
} else {
node->layout(left - node->getMarginRight(), top, right - node->getMarginRight(), bottom, absoluteFlexItem);
}
break;
case kAlignItemsFlexEnd:
if (!isRtl) {
node->layout(left + crossSize - node->mLayoutResult->mLayoutSize.width - node->getMarginRight(),
top, right + crossSize - node->mLayoutResult->mLayoutSize.width - node->getMarginRight(),
bottom, absoluteFlexItem);
} else {
// If the flexWrap == FLEX_WRAP_WRAP_REVERSE, the direction of the
// flexEnd is flipped (from mStyleLeft to mStyleRight).
node->layout(left - crossSize + node->mLayoutResult->mLayoutSize.width + node->getMarginLeft(), top,
right - crossSize + node->mLayoutResult->mLayoutSize.width + node->getMarginLeft(),
bottom, absoluteFlexItem);
}
break;
case kAlignItemsCenter:
float leftFromCrossAxis = (crossSize - node->mLayoutResult->mLayoutSize.width
+ node->getMarginLeft()
- node->getMarginRight()) / 2.f;
if (!isRtl) {
node->layout(left + leftFromCrossAxis, top, right + leftFromCrossAxis, bottom, absoluteFlexItem);
} else {
node->layout(left - leftFromCrossAxis, top, right - leftFromCrossAxis, bottom, absoluteFlexItem);
}
break;
}
}
}