| /** |
| * 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 <math.h> |
| #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; |
| default: 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; |
| default: 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) && !isnan(mCssStyle->mMaxWidth)){ |
| constrainsWidth = mCssStyle->mMaxWidth; |
| } |
| |
| if((!isnan(width)) || |
| (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()) { |
| determineCrossSize(width, height, mFlexLines[0]); |
| } |
| if (stretch) { |
| stretchViewCrossSize(); |
| } |
| } |
| |
| void WXCoreLayoutNode::determineCrossSize(const float width, const float height, WXCoreFlexLine* const flexLine){ |
| bool horizontal = isMainAxisHorizontal(this); |
| float size = flexLine->mCrossSize; |
| float paddingAlongCrossAxis = sumPaddingBorderAlongAxis(this, !horizontal); |
| if (horizontal) { |
| if (heightMeasureMode == kExactly) { |
| size = height - paddingAlongCrossAxis; |
| } |
| } else { |
| if (widthMeasureMode == kExactly) { |
| size = width - paddingAlongCrossAxis; |
| } |
| } |
| flexLine->mCrossSize = size; |
| } |
| |
| 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)){ |
| if(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); |
| //TODO Fix https://jsplayground.taobao.org/raxplayground/97efee70-775f-45a6-b07d-d84c8d1b4387 |
| //TODO This is just a temporary fix, we need to make things beauty and clean. |
| if(child->heightMeasureMode == kUnspecified && child->measureFunc != nullptr && child->getChildCount() == 0){ |
| child->setLayoutHeight(NAN); |
| } |
| } 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 && |
| !(child->measureFunc != nullptr && child->getChildCount() == 0)) { |
| 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 |
| || (l == 0.0f && t == 0.0f && r == 0.0f && b == 0.0f) ) { |
| 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; |
| determineCrossSize(getLayoutWidth(), |
| getLayoutHeight(), |
| flexLine); |
| } |
| |
| void WXCoreLayoutNode::onLayout(const float left, const float top, const float right, const float bottom, |
| WXCoreLayoutNode *const absoulteItem, WXCoreFlexLine *const flexLine) { |
| // determin direction |
| if (mLayoutResult->mLayoutDirection == kDirectionInherit) { |
| if(mCssStyle->mDirection == kDirectionInherit) { |
| // default direction in css is inherit, inherit direction from parent node |
| mLayoutResult->mLayoutDirection = NULL == mParent ? WEEXCORE_CSS_DEFAULT_DIRECTION : mParent->getLayoutDirection(); |
| } else { |
| // specific direction in current Node's style |
| mLayoutResult->mLayoutDirection = mCssStyle->mDirection; |
| } |
| } |
| |
| bool verticalRTL = false; |
| if (mLayoutResult->mLayoutDirection == kDirectionRTL) { |
| verticalRTL = mCssStyle->mFlexWrap != kWrapReverse; |
| } else { |
| verticalRTL = mCssStyle->mFlexWrap == kWrapReverse; |
| } |
| |
| switch (mCssStyle->mFlexDirection) { |
| case kFlexDirectionRow: |
| layoutHorizontal(mLayoutResult->mLayoutDirection == kDirectionRTL, left, top, right, bottom, absoulteItem, flexLine); |
| break; |
| case kFlexDirectionRowReverse: |
| layoutHorizontal(mLayoutResult->mLayoutDirection != kDirectionRTL, left, top, right, bottom, absoulteItem, flexLine); |
| break; |
| case kFlexDirectionColumnReverse: |
| layoutVertical(verticalRTL, true, left, top, right, bottom, absoulteItem, flexLine); |
| break; |
| case kFlexDirectionColumn: |
| default: |
| layoutVertical(verticalRTL, 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; |
| // Bugfix: when flex-direction is row-reverse, justify-content:flex-end is not available, it always performance as flex-start |
| // layoutFlexlineHorizontal(width, flexLine, childLeft, childRight, spaceBetweenItem); |
| layoutFlexlineHorizontal(isRtl, 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 bool isRTL, |
| const float width, |
| const WXCoreFlexLine *const flexLine, |
| float &childLeft, |
| float &childRight, |
| float &spaceBetweenItem) const { |
| Index visibleCount, visibleItem; |
| float denominator; |
| switch (mCssStyle->mJustifyContent) { |
| 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 kJustifyFlexEnd: |
| if (isRTL) { |
| childLeft = getPaddingLeft() + getBorderWidthLeft(); |
| childRight = flexLine->mMainSize + getPaddingLeft() + getBorderWidthLeft(); |
| } else { |
| childLeft = width - flexLine->mMainSize - getPaddingRight() - getBorderWidthRight(); |
| childRight = width - getPaddingRight() - getBorderWidthRight(); |
| } |
| break; |
| case kJustifyFlexStart: |
| default: |
| if (isRTL) { |
| childLeft = width - flexLine->mMainSize - getPaddingRight() - getBorderWidthRight(); |
| childRight = width - getPaddingRight() - getBorderWidthRight(); |
| } else { |
| childLeft = getPaddingLeft() + getBorderWidthLeft(); |
| childRight = flexLine->mMainSize + getPaddingLeft() + getBorderWidthLeft(); |
| } |
| break; |
| } |
| } |
| |
| void WXCoreLayoutNode::layoutFlexlineHorizontal(const float width, |
| const WXCoreFlexLine *const flexLine, |
| float &childLeft, |
| float &childRight, |
| float &spaceBetweenItem) const { |
| layoutFlexlineHorizontal(false, width, flexLine, childLeft, childRight, spaceBetweenItem); |
| } |
| |
| 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; |
| } |
| } |
| void WXCoreLayoutNode::determineChildLayoutDirection(const WXCoreDirection direction) { |
| for (Index i = 0; i < getChildCount(kBFC); ++i) { |
| WXCoreLayoutNode *child = getChildAt(kBFC, i); |
| if (child == nullptr) { |
| continue; |
| } |
| if (child->mLayoutResult == nullptr || child->mCssStyle == nullptr) { |
| continue; |
| } |
| // determin direction |
| if (child->mLayoutResult->mLayoutDirection == kDirectionInherit) { |
| if(child->mCssStyle->mDirection == kDirectionInherit) { |
| // default direction in css is inherit, inherit direction from parent node |
| child->mLayoutResult->mLayoutDirection = direction; |
| } else { |
| // specific direction in current Node's style |
| child->mLayoutResult->mLayoutDirection = child->mCssStyle->mDirection; |
| } |
| } |
| } |
| } |
| |
| WXCoreDirection WXCoreLayoutNode::getLayoutDirectionFromPathNode() { |
| if (getLayoutDirection() != kDirectionInherit) |
| return getLayoutDirection(); |
| if (getDirection() != kDirectionInherit) { |
| mLayoutResult->mLayoutDirection = getDirection(); |
| return getLayoutDirection(); |
| } else if (nullptr != mParent) { |
| mLayoutResult->mLayoutDirection = mParent->getLayoutDirectionFromPathNode(); |
| return getLayoutDirection(); |
| } |
| return WEEXCORE_CSS_DEFAULT_DIRECTION; |
| } |
| } |
| |
| |