You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1212 lines
38 KiB
1212 lines
38 KiB
/** |
|
* 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. |
|
*/ |
|
#ifdef __cplusplus |
|
|
|
#ifndef WEEXCORE_FLEXLAYOUT_WXCORELAYOUTNODE_H |
|
#define WEEXCORE_FLEXLAYOUT_WXCORELAYOUTNODE_H |
|
|
|
#include <string.h> |
|
#include <math.h> |
|
#include <vector> |
|
#include <iostream> |
|
#include <string> |
|
#include <algorithm> |
|
#include "style.h" |
|
#include "flex_enum.h" |
|
|
|
namespace WeexCore { |
|
|
|
class WXCoreLayoutNode; |
|
|
|
class WXCoreFlexLine; |
|
|
|
enum FormattingContext { |
|
kBFC, |
|
kNonBFC, |
|
} ; |
|
|
|
enum MeasureMode { |
|
kUnspecified = 0, |
|
kExactly, |
|
} ; |
|
|
|
struct WXCoreSize { |
|
private: |
|
float hypotheticalWidth; |
|
float hypotheticalHeight; |
|
public: |
|
friend class WXCoreLayoutNode; |
|
float width; |
|
float height; |
|
|
|
WXCoreSize() : hypotheticalWidth(NAN), |
|
hypotheticalHeight(NAN), |
|
width(0), height(0) {} |
|
|
|
inline void reset() { |
|
hypotheticalWidth = NAN ; |
|
hypotheticalHeight = NAN; |
|
width = 0; |
|
height = 0; |
|
} |
|
|
|
inline bool isNAN() { |
|
return isnan(width) || isnan(height); |
|
} |
|
}; |
|
|
|
/** |
|
* layout-result:layout-height、layout-width、position(left、right、top、bottom)、direction |
|
*/ |
|
struct WXCorelayoutResult { |
|
WXCoreDirection mLayoutDirection; |
|
WXCoreSize mLayoutSize; |
|
WXCorePosition mLayoutPosition; |
|
|
|
inline bool isNAN() { |
|
return mLayoutSize.isNAN() || mLayoutPosition.isNAN(); |
|
} |
|
|
|
inline void reset() { |
|
mLayoutSize.reset(); |
|
mLayoutPosition.reset(); |
|
mLayoutDirection = kDirectionInherit; |
|
} |
|
}; |
|
|
|
typedef WXCoreSize(*WXCoreMeasureFunc)(WXCoreLayoutNode *node, float width, |
|
MeasureMode widthMeasureMode, |
|
float height, MeasureMode heightMeasureMode); |
|
|
|
using Index = std::vector<WXCoreLayoutNode *>::size_type; |
|
|
|
/** |
|
* flie line |
|
*/ |
|
class WXCoreFlexLine { |
|
public: |
|
float mMainSize; |
|
|
|
float mCrossSize; |
|
|
|
Index mItemCount; |
|
|
|
float mTotalFlexGrow; |
|
|
|
float mTotalFlexibleSize; |
|
|
|
/** |
|
* Store the indices of the children views whose mAlignSelf property is stretch. |
|
* The stored indices are the absolute indices including all children in the Flexbox, |
|
* not the relative indices in this flex line. |
|
*/ |
|
std::vector<Index> mIndicesAlignSelfStretch; |
|
|
|
WXCoreFlexLine() : mMainSize(0), |
|
mCrossSize(0), |
|
mItemCount(0), |
|
mTotalFlexGrow(0), |
|
mTotalFlexibleSize(0) { |
|
} |
|
|
|
~WXCoreFlexLine() { |
|
mMainSize = 0; |
|
mCrossSize = 0; |
|
mItemCount = 0; |
|
mTotalFlexGrow = 0; |
|
mTotalFlexibleSize = 0; |
|
mIndicesAlignSelfStretch.clear(); |
|
} |
|
}; |
|
|
|
/** |
|
* Layout node |
|
*/ |
|
class WXCoreLayoutNode { |
|
|
|
public: |
|
WXCoreLayoutNode() : |
|
mParent(nullptr), |
|
dirty(true), |
|
widthDirty{false}, |
|
heightDirty{false}, |
|
mHasNewLayout(true), |
|
mIsDestroy(false), |
|
measureFunc(nullptr) { |
|
mCssStyle = new WXCoreCSSStyle(); |
|
mLayoutResult = new WXCorelayoutResult(); |
|
} |
|
|
|
virtual ~WXCoreLayoutNode() { |
|
mIsDestroy = true; |
|
mHasNewLayout = true; |
|
dirty = true; |
|
measureFunc = nullptr; |
|
mParent = nullptr; |
|
mChildList.clear(); |
|
BFCs.clear(); |
|
NonBFCs.clear(); |
|
mChildrenFrozen.clear(); |
|
|
|
for (WXCoreFlexLine *flexLine : mFlexLines) { |
|
if (flexLine != nullptr) { |
|
delete flexLine; |
|
flexLine = nullptr; |
|
} |
|
} |
|
mFlexLines.clear(); |
|
|
|
if (mCssStyle != nullptr) { |
|
delete mCssStyle; |
|
mCssStyle = nullptr; |
|
} |
|
|
|
if (mLayoutResult != nullptr) { |
|
delete mLayoutResult; |
|
mLayoutResult = nullptr; |
|
} |
|
} |
|
|
|
private: |
|
|
|
/** |
|
* Holds the 'frozen' state of children during measure. If a view is frozen it will no longer |
|
* expand regardless of mFlexGrow. Items are indexed by the child's |
|
* reordered index. |
|
*/ |
|
std::vector<bool> mChildrenFrozen; |
|
|
|
std::vector<WXCoreFlexLine *> mFlexLines; |
|
|
|
std::vector<WXCoreLayoutNode *> mChildList; |
|
|
|
std::vector<WXCoreLayoutNode *> BFCs; |
|
|
|
std::vector<WXCoreLayoutNode *> NonBFCs; |
|
|
|
WXCoreLayoutNode *mParent = nullptr; |
|
|
|
WXCoreCSSStyle *mCssStyle = nullptr; |
|
|
|
MeasureMode widthMeasureMode = kUnspecified; |
|
|
|
MeasureMode heightMeasureMode = kUnspecified; |
|
|
|
WXCorelayoutResult *mLayoutResult = nullptr; |
|
|
|
WXCorePosition *absoultePositon = nullptr; |
|
|
|
bool mHasNewLayout; |
|
|
|
bool dirty, widthDirty, heightDirty; |
|
|
|
bool mIsDestroy = true; |
|
|
|
bool mNeedsPlatformDependentLayout = false; |
|
|
|
WXCoreMeasureFunc measureFunc = nullptr; |
|
|
|
void *context = nullptr; |
|
|
|
/** ================================ Cache:Last calculate result =================================== **/ |
|
|
|
public: |
|
|
|
|
|
/** ================================ Engine Entry Function =================================== **/ |
|
|
|
void calculateLayout(const std::pair<float,float>&); |
|
|
|
/** ================================ measureFunc =================================== **/ |
|
|
|
inline void setMeasureFunc(WXCoreMeasureFunc measure) { |
|
measureFunc = measure; |
|
markDirty(); |
|
} |
|
|
|
inline bool haveMeasureFunc() const { |
|
return nullptr != measureFunc; |
|
} |
|
|
|
inline WXCoreMeasureFunc getMeasureFunc() const { |
|
return measureFunc; |
|
} |
|
|
|
/** ================================ context =================================== **/ |
|
|
|
|
|
inline void *getContext() const { |
|
return context; |
|
} |
|
|
|
inline void setContext(void * const context) { |
|
this->context = context; |
|
} |
|
|
|
inline void copyStyle(WXCoreLayoutNode *srcNode) { |
|
if (srcNode != nullptr && memcmp(mCssStyle, srcNode->mCssStyle, sizeof(WXCoreCSSStyle)) != 0) { |
|
memcpy(mCssStyle, srcNode->mCssStyle, sizeof(WXCoreCSSStyle)); |
|
markDirty(); |
|
} |
|
} |
|
|
|
void copyFrom(WXCoreLayoutNode* srcNode){ |
|
if (srcNode == nullptr) return; |
|
|
|
memcpy(mCssStyle, srcNode->mCssStyle, sizeof(WXCoreCSSStyle)); |
|
} |
|
|
|
inline void copyMeasureFunc(WXCoreLayoutNode *srcNode) { |
|
if (srcNode != nullptr && memcmp(&measureFunc, &srcNode->measureFunc, sizeof(WXCoreMeasureFunc)) != 0) { |
|
memcpy(&measureFunc, &srcNode->measureFunc, sizeof(WXCoreMeasureFunc)); |
|
markDirty(); |
|
} |
|
} |
|
|
|
/** ================================ custom =================================== **/ |
|
inline bool getNeedsPlatformDependentLayout() const { |
|
return mNeedsPlatformDependentLayout; |
|
} |
|
|
|
inline void setNeedsPlatformDependentLayout(bool v) { |
|
this->mNeedsPlatformDependentLayout = v; |
|
} |
|
|
|
private: |
|
|
|
/** ================================ measure =================================== **/ |
|
|
|
inline void reset() { |
|
if (isDirty()) { |
|
mLayoutResult->reset(); |
|
for (WXCoreFlexLine *flexLine : mFlexLines) { |
|
if (flexLine != nullptr) { |
|
delete flexLine; |
|
flexLine = nullptr; |
|
} |
|
} |
|
mFlexLines.clear(); |
|
|
|
mChildrenFrozen.assign(getChildCount(kNonBFC), false); |
|
} |
|
widthMeasureMode = isnan(mCssStyle->mStyleWidth) ? kUnspecified : kExactly; |
|
heightMeasureMode = isnan(mCssStyle->mStyleHeight) ? kUnspecified : kExactly; |
|
} |
|
|
|
inline void setLayoutWidth(const float width) { |
|
if (mLayoutResult->mLayoutSize.width != width && |
|
(!isnan(width) || !isnan(mLayoutResult->mLayoutSize.width))) { |
|
mLayoutResult->mLayoutSize.width = width; |
|
widthDirty = true; |
|
markDirty(false); |
|
} |
|
} |
|
|
|
inline void setLayoutHeight(const float height) { |
|
if (mLayoutResult->mLayoutSize.height != height && |
|
(!isnan(height) || !isnan(mLayoutResult->mLayoutSize.height))) { |
|
mLayoutResult->mLayoutSize.height = height; |
|
heightDirty = true; |
|
markDirty(false); |
|
} |
|
} |
|
|
|
inline void setWidthMeasureMode(const MeasureMode measureMode) { |
|
if (widthMeasureMode != measureMode) { |
|
widthMeasureMode = measureMode; |
|
if (getChildCount(kNonBFC) > 0) { |
|
widthDirty = true; |
|
} |
|
} |
|
} |
|
|
|
inline void setHeightMeasureMode(const MeasureMode measureMode) { |
|
if (heightMeasureMode != measureMode) { |
|
heightMeasureMode = measureMode; |
|
if (getChildCount(kNonBFC) > 0) { |
|
heightDirty = true; |
|
} |
|
} |
|
} |
|
|
|
inline float firstLineCrossSize() const { |
|
float sum = sumPaddingBorderAlongAxis(this, !isMainAxisHorizontal(this)); |
|
if (!mFlexLines.empty()) { |
|
sum += mFlexLines[0]->mCrossSize; |
|
} |
|
return sum; |
|
} |
|
|
|
inline float getSumOfCrossSize() const { |
|
float sum = sumPaddingBorderAlongAxis(this, !isMainAxisHorizontal(this)); |
|
for (WXCoreFlexLine *flexLine: mFlexLines) { |
|
sum += flexLine->mCrossSize; |
|
} |
|
return sum; |
|
} |
|
|
|
inline bool isMainAxisHorizontal(const WXCoreLayoutNode* const node) const { |
|
return node->mCssStyle->mFlexDirection == kFlexDirectionRow || |
|
node->mCssStyle->mFlexDirection == kFlexDirectionRowReverse; |
|
} |
|
|
|
inline bool isCrossExactly() const { |
|
return isMainAxisHorizontal(this) ? heightMeasureMode == kExactly |
|
: widthMeasureMode == kExactly; |
|
} |
|
|
|
inline float sumPaddingBorderAlongAxis(const WXCoreLayoutNode* const node, bool horizontal) const { |
|
float paddingBorderAlongAxis; |
|
if (horizontal) { |
|
paddingBorderAlongAxis = |
|
node->mCssStyle->mPadding.getPadding(kPaddingLeft) + |
|
node->mCssStyle->mPadding.getPadding(kPaddingRight) + |
|
node->mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthLeft) + |
|
node->mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthRight); |
|
} else { |
|
paddingBorderAlongAxis = |
|
node->mCssStyle->mPadding.getPadding(kPaddingTop) + |
|
node->mCssStyle->mPadding.getPadding(kPaddingBottom) + |
|
node->mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthTop) + |
|
node->mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthBottom); |
|
} |
|
return paddingBorderAlongAxis; |
|
} |
|
|
|
inline bool isWrapRequired(const float &width, const float &height, |
|
const float ¤tLength, const float &childLength) const { |
|
float freeMainSize = CalculateFreeSpaceAlongMainAxis(width, height, currentLength); |
|
return !isSingleFlexLine(freeMainSize) && freeMainSize < childLength; |
|
} |
|
|
|
//If width/height is NAN, ret is NAN, which property we use on purpose. |
|
virtual float CalculateFreeSpaceAlongMainAxis(const float &width, const float &height, |
|
const float ¤tLength) const{ |
|
float ret; |
|
if(isMainAxisHorizontal(this)){ |
|
ret = width - sumPaddingBorderAlongAxis(this, true) - currentLength; |
|
} |
|
else{ |
|
ret = height - sumPaddingBorderAlongAxis(this, false) - currentLength; |
|
} |
|
return ret; |
|
} |
|
|
|
inline bool isSingleFlexLine(const float &mainSize) const { |
|
return mCssStyle->mFlexWrap == kNoWrap || isnan(mainSize); |
|
} |
|
|
|
inline void sumFlexGrow(const WXCoreLayoutNode* const child, WXCoreFlexLine* const flexLine, Index i){ |
|
if (child->mCssStyle->mFlexGrow > 0) { |
|
flexLine->mTotalFlexGrow += child->mCssStyle->mFlexGrow; |
|
mChildrenFrozen[i] = false; |
|
if (isMainAxisHorizontal(this)) { |
|
if (!isnan(child->mLayoutResult->mLayoutSize.hypotheticalWidth)) { |
|
flexLine->mTotalFlexibleSize += child->mLayoutResult->mLayoutSize.hypotheticalWidth; |
|
} |
|
} else { |
|
if (!isnan(child->mLayoutResult->mLayoutSize.hypotheticalHeight)) { |
|
flexLine->mTotalFlexibleSize += child->mLayoutResult->mLayoutSize.hypotheticalHeight; |
|
} |
|
} |
|
} else { |
|
mChildrenFrozen[i] = true; |
|
} |
|
} |
|
|
|
inline void setMeasuredDimensionForFlex( |
|
const float width, const MeasureMode widthMeasureMode, |
|
const float height, const MeasureMode heightMeasureMode){ |
|
float actualWidth, actualHeight; |
|
if (isMainAxisHorizontal(this)) { |
|
actualWidth = widthMeasureMode == kExactly ? width : getLargestMainSize(); |
|
actualHeight = heightMeasureMode == kExactly ? height : getSumOfCrossSize(); |
|
} else { |
|
actualHeight = heightMeasureMode == kExactly ? height : getLargestMainSize(); |
|
actualWidth = widthMeasureMode == kExactly ? width : firstLineCrossSize(); |
|
} |
|
setMeasuredDimension(actualWidth, actualHeight); |
|
} |
|
|
|
inline float calcItemSizeAlongAxis(const WXCoreLayoutNode* const node, const bool horizontal, const bool useHypotheticalSize = false) const { |
|
float ret; |
|
if (horizontal) { |
|
ret = node->mCssStyle->mMargin.getMargin(kMarginLeft) + |
|
node->mCssStyle->mMargin.getMargin(kMarginRight); |
|
ret += useHypotheticalSize ? node->mLayoutResult->mLayoutSize.hypotheticalWidth |
|
: node->mLayoutResult->mLayoutSize.width; |
|
} else { |
|
ret = node->mCssStyle->mMargin.getMargin(kMarginTop) + |
|
node->mCssStyle->mMargin.getMargin(kMarginBottom); |
|
ret += useHypotheticalSize ? node->mLayoutResult->mLayoutSize.hypotheticalHeight |
|
: node->mLayoutResult->mLayoutSize.height; |
|
} |
|
return ret; |
|
} |
|
|
|
inline void limitMainSizeForFlexGrow(WXCoreFlexLine* const flexLine, const Index childIndex, |
|
const float flexGrow) { |
|
mChildrenFrozen[childIndex] = true; |
|
flexLine->mTotalFlexGrow -= flexGrow; |
|
} |
|
|
|
inline void setMeasuredDimension(const float width, const float height) { |
|
mLayoutResult->mLayoutSize.width = width; |
|
mLayoutResult->mLayoutSize.height = height; |
|
} |
|
|
|
inline std::pair<bool, float> limitChildMainSize(WXCoreFlexLine* const flexLine, const WXCoreLayoutNode* const child, |
|
float childSizeAlongMainAxis, const Index childIndex){ |
|
bool needsReexpand = false; |
|
if (isMainAxisHorizontal(this)) { |
|
if (!isnan(child->mCssStyle->mMaxWidth) && |
|
childSizeAlongMainAxis > child->mCssStyle->mMaxWidth) { |
|
needsReexpand = true; |
|
childSizeAlongMainAxis = child->mCssStyle->mMaxWidth; |
|
} else if (!isnan(child->mCssStyle->mMinWidth) && |
|
childSizeAlongMainAxis < child->mCssStyle->mMinWidth) { |
|
needsReexpand = true; |
|
childSizeAlongMainAxis = child->mCssStyle->mMinWidth; |
|
} |
|
} else { |
|
if (!isnan(child->mCssStyle->mMaxHeight) && |
|
childSizeAlongMainAxis > child->mCssStyle->mMaxHeight) { |
|
needsReexpand = true; |
|
childSizeAlongMainAxis = child->mCssStyle->mMaxHeight; |
|
} else if (!isnan(child->mCssStyle->mMinHeight) && |
|
childSizeAlongMainAxis < child->mCssStyle->mMinHeight) { |
|
needsReexpand = true; |
|
childSizeAlongMainAxis = child->mCssStyle->mMinHeight; |
|
} |
|
} |
|
limitMainSizeForFlexGrow(flexLine, childIndex, child->mCssStyle->mFlexGrow); |
|
return std::make_pair(needsReexpand, childSizeAlongMainAxis); |
|
} |
|
|
|
void updateLeftRightForAbsolute(float &left, float &right, |
|
const WXCorePadding &parentPadding, |
|
const WXCoreBorderWidth &parentBorder, |
|
const WXCoreSize &parentSize) const { |
|
if (isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft))) { |
|
if (isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeRight))) { |
|
; |
|
} else { |
|
right += parentSize.width - |
|
(parentBorder.getBorderWidth(kBorderWidthRight) + |
|
mCssStyle->mStylePosition.getPosition(kPositionEdgeRight) |
|
+ mLayoutResult->mLayoutSize.width); |
|
left += parentSize.width - |
|
(parentBorder.getBorderWidth(kBorderWidthRight) + |
|
mCssStyle->mStylePosition.getPosition(kPositionEdgeRight) |
|
+ mLayoutResult->mLayoutSize.width); |
|
} |
|
} else { |
|
left += parentBorder.getBorderWidth(kBorderWidthLeft) + |
|
mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft); |
|
right += parentBorder.getBorderWidth(kBorderWidthLeft) + |
|
mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft); |
|
} |
|
} |
|
|
|
void updateTopBottomForAbsolute(float &top, float &bottom, |
|
const WXCorePadding &parentPadding, |
|
const WXCoreBorderWidth &parentBorder, |
|
const WXCoreSize &parentSize) const { |
|
if (isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeTop))) { |
|
if (isnan(mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom))) { |
|
; |
|
} else { |
|
top += parentSize.height - |
|
(parentBorder.getBorderWidth(kBorderWidthBottom) + |
|
mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom) |
|
+ mLayoutResult->mLayoutSize.height); |
|
bottom += parentSize.height - |
|
(parentBorder.getBorderWidth(kBorderWidthBottom) + |
|
mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom) |
|
+ mLayoutResult->mLayoutSize.height); |
|
} |
|
} else { |
|
top += parentBorder.getBorderWidth(kBorderWidthTop) + |
|
mCssStyle->mStylePosition.getPosition(kPositionEdgeTop); |
|
bottom += parentBorder.getBorderWidth(kBorderWidthTop) + |
|
mCssStyle->mStylePosition.getPosition(kPositionEdgeTop); |
|
} |
|
} |
|
|
|
/** ================================ other =================================== **/ |
|
|
|
inline void clearDirty() { |
|
dirty = false; |
|
widthDirty = false; |
|
heightDirty = false; |
|
} |
|
|
|
void |
|
measure(float, float, bool); |
|
|
|
void hypotheticalMeasure(float, float, bool = false); |
|
|
|
void measureLeafNode(float, float, bool, bool); |
|
|
|
void measureInternalNode(float, float, bool, bool); |
|
|
|
void updateCurrentFlexline(Index, WXCoreFlexLine *, Index, const WXCoreLayoutNode *, bool); |
|
|
|
void measureChild(WXCoreLayoutNode* , float, float, float, bool, bool); |
|
|
|
void adjustChildSize(WXCoreLayoutNode *, float); |
|
|
|
void adjustChildSize(const WXCoreLayoutNode *child, |
|
const float currentMainSize, |
|
const float parentWidth, |
|
const float parentHeight, |
|
float &childWidth, |
|
float &childHeight) const; |
|
|
|
void stretchViewCrossSize(); |
|
|
|
void stretchViewCrossSize(WXCoreLayoutNode *, float); |
|
|
|
Index expandItemsInFlexLine(WXCoreFlexLine *, float, Index); |
|
|
|
void checkSizeConstraints(WXCoreLayoutNode *, bool); |
|
|
|
void |
|
determineMainSize(float width, float height); |
|
|
|
void |
|
determineCrossSize(float, float, bool); |
|
|
|
void |
|
determineCrossSize(float, float, WXCoreFlexLine *); |
|
|
|
void setFrame(float, float, float, float); |
|
|
|
void setFrame(WXCorePosition*,float, float, float, float); |
|
|
|
/** ================================ layout =================================== **/ |
|
|
|
void layout(float left, float top, float right, float bottom, bool, const std::pair<float,float>* = nullptr); |
|
|
|
void calcRelativeOffset(float &left, float &top, float &right, float &bottom) const ; |
|
|
|
void calcAbsoluteOffset(float &left, float &top, float &right, float &bottom, const std::pair<float,float>* = nullptr); |
|
|
|
void positionAbsoluteFlexItem(float &left, float &top, float &right, float &bottom); |
|
|
|
void layoutHorizontal(bool isRtl, float left, float top, float right, float bottom, |
|
WXCoreLayoutNode*, WXCoreFlexLine *const flexLine); |
|
|
|
void layoutFlexlineHorizontal(const float width, |
|
const WXCoreFlexLine *const flexLine, |
|
float &childLeft, |
|
float &childRight, |
|
float &spaceBetweenItem) const; |
|
|
|
void layoutSingleChildHorizontal(WXCoreLayoutNode *node, WXCoreFlexLine *flexLine, |
|
WXCoreFlexWrap flexWrap, WXCoreAlignItems alignItems, |
|
float, float, float, float, bool); |
|
|
|
void layoutSingleChildHorizontal(const bool isRtl, |
|
const bool, |
|
float childBottom, float childTop, |
|
WXCoreFlexLine *const flexLine, |
|
WXCoreLayoutNode *const child, |
|
float&, float&); |
|
|
|
void layoutVertical(bool isRtl, bool fromBottomToTop, float left, float top, float right, float bottom, |
|
WXCoreLayoutNode*, WXCoreFlexLine *const flexLine); |
|
|
|
void layoutFlexlineVertical(const float height, |
|
const WXCoreFlexLine *const flexLine, |
|
float &childTop, |
|
float &childBottom, |
|
float &spaceBetweenItem) const; |
|
void layoutSingleChildVertical(WXCoreLayoutNode *node, WXCoreFlexLine *flexLine, |
|
bool isRtl, WXCoreAlignItems alignItems, |
|
float, float, float, float, bool); |
|
|
|
void layoutSingleChildVertical(const bool isRtl, const bool fromBottomToTop, |
|
const bool absoluteFlexItem, |
|
const float childLeft, const float childRight, |
|
WXCoreFlexLine *const flexLine, |
|
WXCoreLayoutNode *const child, |
|
float& ,float&); |
|
|
|
void updateFlexLineForAbsoluteItem(WXCoreLayoutNode *const absoluteFlexItem, WXCoreFlexLine *const flexLine); |
|
|
|
void initFormatingContext(std::vector<WXCoreLayoutNode *> &BFCs); |
|
|
|
std::pair<bool,float> calculateBFCWidth(float, float); |
|
|
|
std::pair<bool,float> calculateBFCHeight(float, float); |
|
|
|
std::tuple<bool, float, float> calculateBFCDimension(const std::pair<float,float>&); |
|
|
|
virtual void OnLayoutBefore() { |
|
|
|
} |
|
|
|
virtual void OnLayoutAfter(float width, float height) { |
|
|
|
} |
|
|
|
|
|
public: |
|
virtual void onLayout(float left, float top, float right, float bottom, WXCoreLayoutNode* = nullptr, WXCoreFlexLine *const flexLine = nullptr); |
|
/** ================================ tree =================================== **/ |
|
|
|
inline Index getChildCount(FormattingContext formattingContext) const { |
|
switch (formattingContext) { |
|
case kNonBFC: |
|
return NonBFCs.size(); |
|
case kBFC: |
|
return BFCs.size(); |
|
default: |
|
return mChildList.size(); |
|
} |
|
} |
|
|
|
inline Index getChildCount() const { |
|
return mChildList.size(); |
|
} |
|
|
|
inline std::vector<WXCoreLayoutNode *>::const_iterator ChildListIterBegin() { |
|
return mChildList.cbegin(); |
|
} |
|
|
|
inline std::vector<WXCoreLayoutNode *>::const_iterator ChildListIterEnd() { |
|
return mChildList.cend(); |
|
} |
|
|
|
inline bool hasChild(const WXCoreLayoutNode* const child){ |
|
if(std::find(mChildList.begin(), mChildList.end(), child) != mChildList.end()){ |
|
return true; |
|
}else{ |
|
return false; |
|
} |
|
} |
|
|
|
inline void removeChild(const WXCoreLayoutNode* const child) { |
|
for (int index = 0; index < mChildList.size(); index++) { |
|
if (child == mChildList[index]) { |
|
mChildList.erase(mChildList.begin() + index); |
|
break; |
|
} |
|
} |
|
markDirty(); |
|
} |
|
|
|
inline void addChildAt(WXCoreLayoutNode* const child, Index index) { |
|
mChildList.insert(mChildList.begin() + index, child); |
|
child->mParent = this; |
|
markDirty(); |
|
} |
|
|
|
inline WXCoreLayoutNode *getChildAt(const FormattingContext formattingContext, const Index index) const { |
|
switch (formattingContext) { |
|
case kNonBFC: |
|
return NonBFCs[index]; |
|
case kBFC: |
|
return BFCs[index]; |
|
default: |
|
return mChildList[index]; |
|
} |
|
} |
|
|
|
inline WXCoreLayoutNode *getChildAt(const Index index) const { |
|
return mChildList[index]; |
|
} |
|
|
|
inline WXCoreLayoutNode *getParent() const { |
|
return mParent; |
|
} |
|
|
|
inline void setParent(WXCoreLayoutNode * const parent, WXCoreLayoutNode * const child) const { |
|
child->mParent = parent; |
|
} |
|
|
|
inline bool isBFC(WXCoreLayoutNode* const node) const { |
|
return node->mCssStyle->mPositionType == kAbsolute || node->mCssStyle->mPositionType == kFixed; |
|
} |
|
|
|
/** ================================ margin =================================== **/ |
|
|
|
inline float getMarginTop() const { |
|
return mCssStyle->mMargin.getMargin(kMarginTop); |
|
} |
|
|
|
inline float getMarginBottom() const { |
|
return mCssStyle->mMargin.getMargin(kMarginBottom); |
|
} |
|
|
|
inline float getMarginLeft() const { |
|
return mCssStyle->mMargin.getMargin(kMarginLeft); |
|
} |
|
|
|
inline float getMarginRight() const { |
|
return mCssStyle->mMargin.getMargin(kMarginRight); |
|
} |
|
|
|
inline void setMargin(const WXCoreMarginEdge &edge, const float margin) { |
|
if (mCssStyle->mMargin.setMargin(edge, margin)) { |
|
markDirty(); |
|
} |
|
} |
|
|
|
inline const WXCoreMargin &GetMargins() const { |
|
return mCssStyle->mMargin; |
|
} |
|
|
|
/** ================================ padding =================================== **/ |
|
|
|
inline float getPaddingLeft() const { |
|
return mCssStyle->mPadding.getPadding(kPaddingLeft); |
|
} |
|
|
|
inline float getPaddingRight() const { |
|
return mCssStyle->mPadding.getPadding(kPaddingRight); |
|
} |
|
|
|
inline float getPaddingTop() const { |
|
return mCssStyle->mPadding.getPadding(kPaddingTop); |
|
} |
|
|
|
inline float getPaddingBottom() const { |
|
return mCssStyle->mPadding.getPadding(kPaddingBottom); |
|
} |
|
|
|
inline void setPadding(const WXCorePaddingEdge edge, const float padding) { |
|
if (mCssStyle->mPadding.setPadding(edge, padding)) { |
|
markDirty(); |
|
} |
|
} |
|
|
|
inline const WXCorePadding &GetPaddings() const { |
|
return mCssStyle->mPadding; |
|
} |
|
|
|
/** ================================ border-width =================================== **/ |
|
|
|
inline float getBorderWidthLeft() const { |
|
return mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthLeft); |
|
} |
|
|
|
inline float getBorderWidthRight() const { |
|
return mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthRight); |
|
} |
|
|
|
inline float getBorderWidthTop() const { |
|
return mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthTop); |
|
} |
|
|
|
inline float getBorderWidthBottom() const { |
|
return mCssStyle->mBorderWidth.getBorderWidth(kBorderWidthBottom); |
|
} |
|
|
|
inline void setBorderWidth(const WXCoreBorderWidthEdge edge, const float borderWidth) { |
|
if (mCssStyle->mBorderWidth.setBorderWidth(edge, borderWidth)) { |
|
markDirty(); |
|
} |
|
} |
|
|
|
inline const WXCoreBorderWidth &GetBorders() const { |
|
return mCssStyle->mBorderWidth; |
|
} |
|
|
|
/** ================================ position-type =================================== **/ |
|
|
|
inline void setStylePositionType(const WXCorePositionType positionType) { |
|
if (mCssStyle->mPositionType != positionType) { |
|
mCssStyle->mPositionType = positionType; |
|
markDirty(); |
|
} |
|
} |
|
|
|
inline WXCorePositionType getStylePositionType() const { |
|
return mCssStyle->mPositionType; |
|
} |
|
|
|
|
|
/** ================================ position =================================== **/ |
|
|
|
inline float getStylePositionTop() const { |
|
return mCssStyle->mStylePosition.getPosition(kPositionEdgeTop); |
|
} |
|
|
|
inline float getStylePositionBottom() const { |
|
return mCssStyle->mStylePosition.getPosition(kPositionEdgeBottom); |
|
} |
|
|
|
inline float getStylePositionLeft() const { |
|
return mCssStyle->mStylePosition.getPosition(kPositionEdgeLeft); |
|
} |
|
|
|
inline float getStylePositionRight() const { |
|
return mCssStyle->mStylePosition.getPosition(kPositionEdgeRight); |
|
} |
|
|
|
inline void setStylePosition(const WXCorePositionEdge edge, const float positionRight) { |
|
if (mCssStyle->mStylePosition.setPosition(edge, positionRight)) |
|
markDirty(); |
|
} |
|
|
|
|
|
/** ================================ dimension =================================== **/ |
|
|
|
inline void setStyleWidthLevel(const DimensionLevel level) const { |
|
if (mCssStyle->mStyleWidthLevel != level) { |
|
mCssStyle->mStyleWidthLevel = level; |
|
} |
|
} |
|
|
|
inline void setStyleHeightLevel(const DimensionLevel level) const { |
|
if (mCssStyle->mStyleHeightLevel != level) { |
|
mCssStyle->mStyleHeightLevel = level; |
|
} |
|
} |
|
|
|
inline DimensionLevel getStyleHeightLevel() const { |
|
return mCssStyle->mStyleHeightLevel; |
|
} |
|
|
|
inline DimensionLevel getStyleWidthLevel() const { |
|
return mCssStyle->mStyleWidthLevel; |
|
} |
|
|
|
inline void setStyleWidth(const float width, const bool updating) { |
|
if (mCssStyle->mStyleWidth != width) { |
|
mCssStyle->mStyleWidth = width; |
|
markDirty(); |
|
if(updating) { |
|
markChildrenDirty(true); |
|
} |
|
} |
|
} |
|
|
|
inline void setStyleWidthToNAN() { |
|
if (!isnan(mCssStyle->mStyleWidth)) { |
|
mCssStyle->mStyleWidth = NAN; |
|
markDirty(); |
|
markChildrenDirty(true); |
|
} |
|
} |
|
|
|
inline float getStyleWidth() const { |
|
return mCssStyle->mStyleWidth; |
|
} |
|
|
|
inline void setStyleHeight(const float height) { |
|
if (mCssStyle->mStyleHeight != height) { |
|
mCssStyle->mStyleHeight = height; |
|
markDirty(); |
|
} |
|
} |
|
|
|
inline float getStyleHeight() const { |
|
return mCssStyle->mStyleHeight; |
|
} |
|
|
|
inline void setMinWidth(const float minWidth, const bool updating) { |
|
if (mCssStyle->mMinWidth != minWidth) { |
|
mCssStyle->mMinWidth = minWidth; |
|
markDirty(); |
|
if(updating) { |
|
markChildrenDirty(true); |
|
} |
|
} |
|
} |
|
|
|
inline float getMinWidth() const { |
|
return mCssStyle->mMinWidth; |
|
} |
|
|
|
inline void setMaxWidth(const float maxWidth, const bool updating) { |
|
if (mCssStyle->mMaxWidth != maxWidth) { |
|
mCssStyle->mMaxWidth = maxWidth; |
|
markDirty(); |
|
if(updating) { |
|
markChildrenDirty(true); |
|
} |
|
} |
|
} |
|
|
|
inline float getMaxWidth() const { |
|
return mCssStyle->mMaxWidth; |
|
} |
|
|
|
inline void setMinHeight(const float minHeight) { |
|
if (mCssStyle->mMinHeight != minHeight) { |
|
mCssStyle->mMinHeight = minHeight; |
|
markDirty(); |
|
} |
|
} |
|
|
|
inline float getMinHeight() const { |
|
return mCssStyle->mMinHeight; |
|
} |
|
|
|
inline void setMaxHeight(const float maxHeight) { |
|
if (mCssStyle->mMaxHeight != maxHeight) { |
|
mCssStyle->mMaxHeight = maxHeight; |
|
markDirty(); |
|
} |
|
} |
|
|
|
inline float getMaxHeight() const { |
|
return mCssStyle->mMaxHeight; |
|
} |
|
|
|
inline void setDirection(const WXCoreDirection direction, const bool updating) { |
|
if (nullptr == mCssStyle) return; |
|
|
|
if (mCssStyle->mDirection != direction) { |
|
mCssStyle->mDirection = direction; |
|
markDirty(); |
|
if (updating) { |
|
for (auto it = ChildListIterBegin(); it != ChildListIterEnd(); it++) { |
|
(*it)->markInheritableDirty(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
inline WXCoreDirection getDirection() const { |
|
if (mCssStyle == nullptr) { |
|
return WEEXCORE_CSS_DEFAULT_DIRECTION; |
|
} |
|
return mCssStyle->mDirection; |
|
} |
|
|
|
/** ================================ CSS direction For RTL =================================== **/ |
|
|
|
void determineChildLayoutDirection(const WXCoreDirection direction); |
|
|
|
WXCoreDirection getLayoutDirectionFromPathNode(); |
|
|
|
/** ================================ flex-style =================================== **/ |
|
|
|
inline void setFlexDirection(const WXCoreFlexDirection flexDirection, const bool updating) { |
|
if (mCssStyle->mFlexDirection != flexDirection) { |
|
mCssStyle->mFlexDirection = flexDirection; |
|
markDirty(); |
|
if (updating) { |
|
for (auto it = ChildListIterBegin(); it != ChildListIterEnd(); it++) { |
|
(*it)->markDirty(false); |
|
} |
|
} |
|
} |
|
} |
|
|
|
inline WXCoreFlexDirection getFlexDirection() const { |
|
return mCssStyle->mFlexDirection; |
|
} |
|
|
|
inline void setFlexWrap(const WXCoreFlexWrap flexWrap) { |
|
if (mCssStyle->mFlexWrap != flexWrap) { |
|
mCssStyle->mFlexWrap = flexWrap; |
|
markDirty(); |
|
} |
|
} |
|
|
|
inline WXCoreFlexWrap getFlexWrap() const { |
|
return mCssStyle->mFlexWrap; |
|
} |
|
|
|
inline void setJustifyContent(const WXCoreJustifyContent justifyContent) { |
|
if (mCssStyle->mJustifyContent != justifyContent) { |
|
mCssStyle->mJustifyContent = justifyContent; |
|
} |
|
} |
|
|
|
inline WXCoreJustifyContent getJustifyContent() const { |
|
return mCssStyle->mJustifyContent; |
|
} |
|
|
|
inline void setAlignItems(const WXCoreAlignItems alignItems) { |
|
if (mCssStyle->mAlignItems != alignItems) { |
|
mCssStyle->mAlignItems = alignItems; |
|
markDirty(); |
|
} |
|
} |
|
|
|
inline WXCoreAlignItems getAlignItems() const { |
|
return mCssStyle->mAlignItems; |
|
} |
|
|
|
inline void setAlignSelf(const WXCoreAlignSelf alignSelf) { |
|
if (mCssStyle->mAlignSelf != alignSelf) { |
|
mCssStyle->mAlignSelf = alignSelf; |
|
markDirty(); |
|
} |
|
} |
|
|
|
inline WXCoreAlignSelf getAlignSelf() const { |
|
return mCssStyle->mAlignSelf; |
|
} |
|
|
|
virtual void set_flex(const float flex) { |
|
if (mCssStyle->mFlexGrow != flex) { |
|
mCssStyle->mFlexGrow = flex; |
|
markDirty(); |
|
} |
|
} |
|
|
|
inline float getFlex() const { |
|
return mCssStyle->mFlexGrow; |
|
} |
|
|
|
/** ================================ layout-result =================================== **/ |
|
|
|
inline float getLayoutWidth() const { |
|
return mLayoutResult->mLayoutSize.width; |
|
} |
|
|
|
inline float getLayoutHeight() const { |
|
return mLayoutResult->mLayoutSize.height; |
|
} |
|
|
|
inline float getLayoutPositionTop() const { |
|
return mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeTop); |
|
} |
|
|
|
inline float getLayoutPositionBottom() const { |
|
return mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeBottom); |
|
} |
|
|
|
inline float getLayoutPositionLeft() const { |
|
return mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeLeft); |
|
} |
|
|
|
inline float getLayoutPositionRight() const { |
|
return mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeRight); |
|
} |
|
|
|
virtual inline WXCoreDirection getLayoutDirection() const { |
|
if (nullptr == mLayoutResult) { |
|
return WEEXCORE_CSS_DEFAULT_DIRECTION; |
|
} |
|
return mLayoutResult->mLayoutDirection; |
|
} |
|
|
|
inline void setLayoutDirection(WXCoreDirection direction) { |
|
if (nullptr == mLayoutResult) return; |
|
mLayoutResult->mLayoutDirection = direction; |
|
} |
|
inline bool hasNewLayout() const { |
|
return mHasNewLayout; |
|
} |
|
|
|
inline bool isDirty() const { |
|
return dirty; |
|
} |
|
|
|
inline void markDirty(const bool recursion = true) { |
|
if (!isDirty()) { |
|
dirty = true; |
|
if (getParent() != nullptr && recursion) { |
|
getParent()->markDirty(); |
|
} |
|
} |
|
} |
|
|
|
void markAllDirty() { |
|
markDirty(false); |
|
for (WXCoreLayoutNode* c : mChildList) { |
|
c->markAllDirty(); |
|
} |
|
} |
|
|
|
bool markChildrenDirty(const bool updatedNode = false) { |
|
bool ret = false; |
|
if(getChildCount() == 0){ |
|
if(measureFunc!= nullptr){ |
|
ret = true; |
|
} |
|
} |
|
else { |
|
//isnan(mCssStyle->mStyleWidth) XOR updatedNode |
|
if(isnan(mCssStyle->mStyleWidth) != updatedNode){ |
|
for (auto it = ChildListIterBegin(); it != ChildListIterEnd(); it++) { |
|
ret = ((*it)->markChildrenDirty() || ret) ; |
|
} |
|
} |
|
} |
|
dirty = ret || dirty; |
|
return ret; |
|
} |
|
|
|
void markInheritableDirty() { |
|
if (resetInheritableSet()) { |
|
// if some style was inherited from parent, reset those styles |
|
// then mark self dirty |
|
markDirty(false); |
|
|
|
// traverse children to mark dirty |
|
if(getChildCount() == 0){ |
|
return; |
|
} |
|
else { |
|
for (auto it = ChildListIterBegin(); it != ChildListIterEnd(); it++) { |
|
(*it)->markInheritableDirty(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* if some style was inherited from parent, reset those styles, then return true, eles return false |
|
*/ |
|
bool resetInheritableSet() { |
|
if (mCssStyle == nullptr || mLayoutResult == nullptr) return false; |
|
|
|
bool hasInheritedStyle = false; |
|
if (mCssStyle->mDirection == kDirectionInherit) { |
|
mLayoutResult->mLayoutDirection = kDirectionInherit; |
|
hasInheritedStyle = true; |
|
} |
|
return hasInheritedStyle; |
|
} |
|
|
|
inline void setHasNewLayout(const bool hasNewLayout) { |
|
this->mHasNewLayout = hasNewLayout; |
|
} |
|
|
|
inline float getLargestMainSize() const { |
|
float largestSize = 0; |
|
for (WXCoreFlexLine *flexLine : mFlexLines) { |
|
largestSize = std::max(largestSize, flexLine->mMainSize); |
|
} |
|
return largestSize + sumPaddingBorderAlongAxis(this, isMainAxisHorizontal(this)); |
|
} |
|
|
|
inline void rewriteLayoutResult(float left, float top, float width, float height) { |
|
if (mLayoutResult != nullptr) { |
|
mLayoutResult->mLayoutPosition.setPosition(kPositionEdgeLeft, left); |
|
mLayoutResult->mLayoutPosition.setPosition(kPositionEdgeTop, top); |
|
mLayoutResult->mLayoutPosition.setPosition(kPositionEdgeRight, left + width); |
|
mLayoutResult->mLayoutPosition.setPosition(kPositionEdgeBottom, top + height); |
|
mLayoutResult->mLayoutSize.width = width; |
|
mLayoutResult->mLayoutSize.height = height; |
|
} |
|
} |
|
}; |
|
} |
|
#endif //WEEXCORE_FLEXLAYOUT_WXCORELAYOUTNODE_H |
|
#endif
|
|
|