http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/2f8caedb/weex_core/Source/core/bridge/bridge.h ---------------------------------------------------------------------- diff --git a/weex_core/Source/core/bridge/bridge.h b/weex_core/Source/core/bridge/bridge.h new file mode 100644 index 0000000..8076232 --- /dev/null +++ b/weex_core/Source/core/bridge/bridge.h @@ -0,0 +1,81 @@ +#ifndef Bridge_h +#define Bridge_h + +#include <jni.h> +#include <string> +#include <map> +#include <set> +#include <vector> + +namespace WeexCore { + + class WXCoreMargin; + + class WXCorePadding; + + class WXCoreBorderWidth; + + class Bridge { + public: + virtual void setJSVersion(const char* version) = 0; + + virtual void reportException(const char* pageId, const char *func, const char *exception_string) = 0; + + virtual int callNative(const char* pageId, const char *task, const char *callback) = 0; + + virtual jobject callNativeModule(const char* pageId, const char *module, const char *method, + const char *argString, const char *optString) = 0; + + virtual void callNativeComponent(const char* pageId, const char* ref, const char *method, + const char *argString, const char *optString) = 0; + + virtual void setTimeout(const char* callbackID, const char* time) = 0; + + virtual void callNativeLog(const char* str_array) = 0; + + virtual int callUpdateFinish(const char* pageId, const char *task, const char *callback) = 0; + + virtual int callRefreshFinish(const char* pageId, const char *task, const char *callback) = 0; + + virtual int callAddEvent(const char* pageId, const char* ref, const char *event) = 0; + + virtual int callRemoveEvent(const char* pageId, const char* ref, const char *event) = 0; + + virtual int callCreateBody(const char* pageId, const char *componentType, const char* ref, + std::map<std::string, std::string> *styles, + std::map<std::string, std::string> *attributes, + std::set<std::string> *events, + const WXCoreMargin &margins, + const WXCorePadding &paddings, + const WXCoreBorderWidth &borders) = 0; + + virtual int callAddElement(const char* pageId, const char *componentType, const char* ref, + int &index, const char* parentRef, + std::map<std::string, std::string> *styles, + std::map<std::string, std::string> *attributes, + std::set<std::string> *events, + const WXCoreMargin &margins, + const WXCorePadding &paddings, + const WXCoreBorderWidth &borders) = 0; + + virtual int callLayout(const char* pageId, const char* ref, + int top, int bottom, int left, int right, + int height, int width, int index) = 0; + + virtual int callUpdateStyle(const char* pageId, const char* ref, + std::vector<std::pair<std::string, std::string>> *style, + std::vector<std::pair<std::string, std::string>> *margin, + std::vector<std::pair<std::string, std::string>> *padding, + std::vector<std::pair<std::string, std::string>> *border) = 0; + + virtual int callUpdateAttr(const char* pageId, const char* ref, + std::vector<std::pair<std::string, std::string>> *attrs) = 0; + + virtual int callCreateFinish(const char* pageId) = 0; + + virtual int callRemoveElement(const char* pageId, const char* ref) = 0; + + virtual int callMoveElement(const char* pageId, const char* ref, const char* parentRef, int index) = 0; + }; +} //end WeexCore +#endif //Bridge_h
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/2f8caedb/weex_core/Source/core/config/core_environment.cpp ---------------------------------------------------------------------- diff --git a/weex_core/Source/core/config/core_environment.cpp b/weex_core/Source/core/config/core_environment.cpp new file mode 100644 index 0000000..5b7d928 --- /dev/null +++ b/weex_core/Source/core/config/core_environment.cpp @@ -0,0 +1,63 @@ +#include "core_environment.h" +#include "base/CoreConstants.h" +#include <stdlib.h> +#include <base/ViewUtils.h> + +namespace WeexCore { + + WXCoreEnvironment *WXCoreEnvironment::m_pInstance = nullptr; + + bool WXCoreEnvironment::SetPlatform(std::string platformName) { + if (platformName.empty()) + return false; + this->mPlatformName = platformName; + if (platformName == OS_Android || platformName == OS_iOS) { + return true; + } else { + return false; + } + } + + bool WXCoreEnvironment::IsAndroid() { + return mPlatformName == OS_Android; + } + + bool WXCoreEnvironment::IsIOS() { + return mPlatformName == OS_iOS; + } + + bool WXCoreEnvironment::SetDeviceWidth(const std::string &width) { + if (width.empty() || isnan(getFloat(width.c_str()))) + return false; + mDeviceWidth = getFloat(width.c_str()); + return true; + } + + bool WXCoreEnvironment::SetDeviceHeight(const std::string &height) { + if (height.empty() || isnan(getFloat(height.c_str()))) + return false; + mDeviceHeight = getFloat(height.c_str()); + return true; + } + + const float &WXCoreEnvironment::DeviceWidth() { + return mDeviceWidth; + } + + const float &WXCoreEnvironment::DeviceHeight() { + return mDeviceHeight; + } + + const std::string &WXCoreEnvironment::GetOption(const std::string &key) { + std::map<std::string, std::string>::iterator iter = mOptions.find(key); + if (iter != mOptions.end()) { + return iter->second; + } else { + return ""; + } + } + + void WXCoreEnvironment::AddOption(std::string key, std::string value) { + mOptions.insert(std::pair<std::string, std::string>(key, value)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/2f8caedb/weex_core/Source/core/config/core_environment.h ---------------------------------------------------------------------- diff --git a/weex_core/Source/core/config/core_environment.h b/weex_core/Source/core/config/core_environment.h new file mode 100644 index 0000000..ac89845 --- /dev/null +++ b/weex_core/Source/core/config/core_environment.h @@ -0,0 +1,68 @@ +#ifndef WEEX_PROJECT_WXCOREENVIRONMENT_H +#define WEEX_PROJECT_WXCOREENVIRONMENT_H + +#include <string> +#include <map> + +namespace WeexCore { + + class WXCoreEnvironment { + + private: + + WXCoreEnvironment() {} + + ~WXCoreEnvironment() {} + + static WXCoreEnvironment *m_pInstance; + + //just to release singleton object + class Garbo { + public: + ~Garbo() { + if (WXCoreEnvironment::m_pInstance) { + delete WXCoreEnvironment::m_pInstance; + } + } + }; + + static Garbo garbo; + + std::string mPlatformName; + + float mDeviceWidth; + + float mDeviceHeight; + + std::map<std::string, std::string> mOptions; + + public: + + bool SetPlatform(std::string platformName); + + bool IsAndroid(); + + bool IsIOS(); + + bool SetDeviceWidth(const std::string &width); + + bool SetDeviceHeight(const std::string &height); + + const float &DeviceWidth(); + + const float &DeviceHeight(); + + const std::string &GetOption(const std::string &key); + + void AddOption(std::string key, std::string value); + + static WXCoreEnvironment *getInstance() { + if (!m_pInstance) { + m_pInstance = new WXCoreEnvironment(); + } + return m_pInstance; + } + }; +} + +#endif //WEEX_PROJECT_WXCOREENVIRONMENT_H http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/2f8caedb/weex_core/Source/core/css/constants_name.h ---------------------------------------------------------------------- diff --git a/weex_core/Source/core/css/constants_name.h b/weex_core/Source/core/css/constants_name.h new file mode 100644 index 0000000..8e07e04 --- /dev/null +++ b/weex_core/Source/core/css/constants_name.h @@ -0,0 +1,63 @@ + +#ifndef WEEXV8_CONSTANTSNAME_H +#define WEEXV8_CONSTANTSNAME_H + +#include <string> + +namespace WeexCore { + + constexpr char FLEX[] = "flex"; + constexpr char HORIZONTAL[] = "horizontal"; + constexpr char ALIGN_ITEMS[] = "alignItems"; + constexpr char ALIGN_SELF[] = "alignSelf"; + constexpr char FLEX_DIRECTION[] = "flexDirection"; + constexpr char JUSTIFY_CONTENT[] = "justifyContent"; + constexpr char FLEX_WRAP[] = "flexWrap"; + + constexpr char MIN_WIDTH[] = "minWidth"; + constexpr char MIN_HEIGHT[] = "minHeight"; + constexpr char MAX_WIDTH[] = "maxWidth"; + constexpr char MAX_HEIGHT[] = "maxHeight"; + + constexpr char DEFAULT_HEIGHT[] = "defaultHeight"; + constexpr char HEIGHT[] = "height"; + + constexpr char DEFAULT_WIDTH[] = "defaultWidth"; + constexpr char WIDTH[] = "width"; + constexpr char SCROLL_DIRECTION[] = "scrollDirection"; + + constexpr char POSITION[] = "position"; + + constexpr char LEFT[] = "left"; + constexpr char TOP[] = "top"; + constexpr char RIGHT[] = "right"; + constexpr char BOTTOM[] = "bottom"; + + constexpr char MARGIN[] = "margin"; + constexpr char MARGIN_LEFT[] = "marginLeft"; + constexpr char MARGIN_TOP[] = "marginTop"; + constexpr char MARGIN_RIGHT[] = "marginRight"; + constexpr char MARGIN_BOTTOM[] = "marginBottom"; + + constexpr char BORDER_WIDTH[] = "borderWidth"; + constexpr char BORDER_TOP_WIDTH[] = "borderTopWidth"; + constexpr char BORDER_RIGHT_WIDTH[] = "borderRightWidth"; + constexpr char BORDER_BOTTOM_WIDTH[] = "borderBottomWidth"; + constexpr char BORDER_LEFT_WIDTH[] = "borderLeftWidth"; + + constexpr char PADDING[] = "padding"; + constexpr char PADDING_LEFT[] = "paddingLeft"; + constexpr char PADDING_TOP[] = "paddingTop"; + constexpr char PADDING_RIGHT[] = "paddingRight"; + constexpr char PADDING_BOTTOM[] = "paddingBottom"; + + constexpr char FONT_STYLE[] = "fontStyle"; + constexpr char COLUMN_WIDTH[] = "columnWidth"; + constexpr char COLUMN_COUNT[] = "columnCount"; + constexpr char COLUMN_GAP[] = "columnGap"; + + constexpr char COLOR[] = "color"; + constexpr char BACKGROUND_COLOR[] = "backgroundColor"; +} + +#endif //WEEXV8_CONSTANTSNAME_H http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/2f8caedb/weex_core/Source/core/css/constants_value.h ---------------------------------------------------------------------- diff --git a/weex_core/Source/core/css/constants_value.h b/weex_core/Source/core/css/constants_value.h new file mode 100644 index 0000000..71cd013 --- /dev/null +++ b/weex_core/Source/core/css/constants_value.h @@ -0,0 +1,45 @@ + +#ifndef WEEXV8_CONSTANTSVALUE_H +#define WEEXV8_CONSTANTSVALUE_H + +#include <string> + +namespace WeexCore { + + // flex-direction + constexpr char ROW[] = "row"; + constexpr char ROW_REVERSE[] = "row-reverse"; + constexpr char COLUMN[] = "column"; + constexpr char COLUMN_REVERSE[] = "column-reverse"; + + // flex-wrap + constexpr char NOWRAP[] = "nowrap"; + constexpr char WRAP[] = "wrap"; + constexpr char WRAP_REVERSE[] = "wrap-reverse"; + + // align + constexpr char FLEX_START[] = "flex-start"; + constexpr char FLEX_END[] = "flex-end"; + constexpr char CENTER[] = "center"; + constexpr char SPACE_BETWEEN[] = "space-between"; + constexpr char SPACE_AROUND[] = "space-around"; + constexpr char STRETCH[] = "stretch"; + constexpr char AUTO[] = "auto"; + + // position + constexpr char RELATIVE[] = "relative"; + constexpr char ABSOLUTE[] = "absolute"; + constexpr char FIXED[] = "fixed"; + constexpr char STICKY[] = "sticky"; + + constexpr char NORMAL[] = "normal"; + constexpr int COLUMN_COUNT_NORMAL = 1; + constexpr float AUTO_VALUE = -1; + constexpr float COLUMN_GAP_NORMAL = 32; + + constexpr int HORIZONTAL_VALUE = 0; + constexpr int VERTICAL_VALUE = 1; + + constexpr float kDefaultViewPortWidth = 750.0f; +} +#endif //WEEXV8_CONSTANTSVALUE_H http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/2f8caedb/weex_core/Source/core/css/css_value_getter.cpp ---------------------------------------------------------------------- diff --git a/weex_core/Source/core/css/css_value_getter.cpp b/weex_core/Source/core/css/css_value_getter.cpp new file mode 100644 index 0000000..5241cd6 --- /dev/null +++ b/weex_core/Source/core/css/css_value_getter.cpp @@ -0,0 +1,88 @@ +#include "css_value_getter.h" +#include "constants_value.h" +#include "constants_name.h" +#include "core/layout/style.h" + +namespace WeexCore { + + const WXCoreFlexDirection &GetWXCoreFlexDirection(const std::string &value) { + if (value == COLUMN) { + return WeexCore::kFlexDirectionColumn; + } else if (value == ROW) { + return WeexCore::kFlexDirectionRow; + } else if (value == COLUMN_REVERSE) { + return WeexCore::kFlexDirectionColumnReverse; + } else if (value == ROW_REVERSE) { + return WeexCore::kFlexDirectionRowReverse; + } + return WeexCore::kFlexDirectionColumn; + } + + const WXCoreJustifyContent &GetWXCoreJustifyContent(const std::string &value) { + if (value == FLEX_START) { + return WeexCore::kJustifyFlexStart; + } else if (value == FLEX_END) { + return WeexCore::kJustifyFlexEnd; + } else if (value == CENTER) { + return WeexCore::kJustifyCenter; + } else if (value == SPACE_BETWEEN) { + return WeexCore::kJustifySpaceBetween; + } else if (value == SPACE_AROUND) { + return WeexCore::kJustifySpaceAround; + } + return WeexCore::kJustifyFlexStart; + } + + const WXCoreAlignItems &GetWXCoreAlignItem(const std::string &value) { + if (value == STRETCH) { + return WeexCore::kAlignItemsStretch; + } else if (value == FLEX_START) { + return WeexCore::kAlignItemsFlexStart; + } else if (value == FLEX_END) { + return WeexCore::kAlignItemsFlexEnd; + } else if (value == CENTER) { + return WeexCore::kAlignItemsCenter; + } + return WeexCore::kAlignItemsStretch; + } + + const WXCoreFlexWrap &GetWXCoreFlexWrap(const std::string &value) { + if (value == NOWRAP) { + return WeexCore::kNoWrap; + } else if (value == WRAP) { + return WeexCore::kWrap; + } else if (value == WRAP_REVERSE) { + return WeexCore::kWrapReverse; + } + return WeexCore::kNoWrap; + } + + const WXCoreAlignSelf &GetWXCoreAlignSelf(const std::string &value) { + if (value == AUTO) { + return WeexCore::kAlignSelfAuto; + } else if (value == STRETCH) { + return WeexCore::kAlignSelfStretch; + } else if (value == FLEX_START) { + return WeexCore::kAlignSelfFlexStart; + } else if (value == FLEX_END) { + return WeexCore::kAlignSelfFlexEnd; + } else if (value == CENTER) { + return WeexCore::kAlignSelfCenter; + } + return WeexCore::kAlignSelfAuto; + } + + const WXCorePositionType &GetWXCorePositionType(const std::string &value) { + auto ret = kRelative; + if (value == RELATIVE) { + ret = kRelative; + } else if (value == ABSOLUTE) { + ret = kAbsolute; + } else if(value == STICKY){ + ret = kSticky; + }else if(value == FIXED){ + ret = kFixed; + } + return ret; + } +} http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/2f8caedb/weex_core/Source/core/css/css_value_getter.h ---------------------------------------------------------------------- diff --git a/weex_core/Source/core/css/css_value_getter.h b/weex_core/Source/core/css/css_value_getter.h new file mode 100644 index 0000000..1a045b3 --- /dev/null +++ b/weex_core/Source/core/css/css_value_getter.h @@ -0,0 +1,23 @@ +#ifndef WEEX_PROJECT_STYLEGETTER_H +#define WEEX_PROJECT_STYLEGETTER_H + +#include "core/layout/flex_enum.h" +#include <string> + +namespace WeexCore { + + const WXCoreFlexDirection &GetWXCoreFlexDirection(const std::string &value); + + const WXCoreJustifyContent &GetWXCoreJustifyContent(const std::string &value); + + const WXCoreAlignItems &GetWXCoreAlignItem(const std::string &value); + + const WXCoreFlexWrap &GetWXCoreFlexWrap(const std::string &value); + + const WXCoreAlignSelf &GetWXCoreAlignSelf(const std::string &value); + + const WXCorePositionType &GetWXCorePositionType(const std::string &value); +} + + +#endif //WEEX_PROJECT_STYLEGETTER_H http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/2f8caedb/weex_core/Source/core/layout/flex_enum.h ---------------------------------------------------------------------- diff --git a/weex_core/Source/core/layout/flex_enum.h b/weex_core/Source/core/layout/flex_enum.h new file mode 100644 index 0000000..26fe8a3 --- /dev/null +++ b/weex_core/Source/core/layout/flex_enum.h @@ -0,0 +1,107 @@ +#ifdef __cplusplus + +#ifndef WEEXCORE_FLEXLAYOUT_WXCOREFLEXENUM_H +#define WEEXCORE_FLEXLAYOUT_WXCOREFLEXENUM_H + +namespace WeexCore { + + /** + * MainAxis direction + */ + enum WXCoreFlexDirection { + kFlexDirectionColumn, + kFlexDirectionColumnReverse, + kFlexDirectionRow, + kFlexDirectionRowReverse, + }; + + /** + * Controls the position of the element on the MainAxis + */ + enum WXCoreJustifyContent { + kJustifyFlexStart, + kJustifyCenter, + kJustifyFlexEnd, + kJustifySpaceBetween, + kJustifySpaceAround, + }; + + /** + * Controls the position of the element on the CrossAxis and whether Stretch + */ + enum WXCoreAlignItems { + kAlignItemsFlexStart, + kAlignItemsCenter, + kAlignItemsFlexEnd, + kAlignItemsStretch, + }; + + /** + * Controls the count of flexlines + */ + enum WXCoreFlexWrap { + kNoWrap, + kWrap, + kWrapReverse, + }; + + /** + * The align-self will overrides the align-items specified by the Flex container. + * The two attributes have the same range of values. + */ + enum WXCoreAlignSelf { + kAlignSelfAuto = -1, + kAlignSelfFlexStart = kAlignItemsFlexStart, + kAlignSelfCenter = kAlignItemsCenter, + kAlignSelfFlexEnd = kAlignItemsFlexEnd, + kAlignSelfStretch = kAlignItemsStretch, + }; + + enum WXCorePositionType { + kRelative, + kAbsolute, + kFixed, + kSticky = kRelative + }; + + enum WXCorePositionEdge { + kPositionEdgeTop, + kPositionEdgeBottom, + kPositionEdgeLeft, + kPositionEdgeRight, + }; + + enum WXCoreMarginEdge { + kMarginALL, + kMarginTop, + kMarginBottom, + kMarginLeft, + kMarginRight, + }; + + enum WXCorePaddingEdge { + kPaddingALL, + kPaddingTop, + kPaddingBottom, + kPaddingLeft, + kPaddingRight, + }; + + enum WXCoreBorderWidthEdge { + kBorderWidthALL, + kBorderWidthTop, + kBorderWidthBottom, + kBorderWidthLeft, + kBorderWidthRight, + }; + + enum WXCoreEdge{ + kTop, + kRight, + kBottom, + kLeft, + }; + +} +#endif //WEEXCORE_FLEXLAYOUT_WXCOREFLEXENUM_H +#endif \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/2f8caedb/weex_core/Source/core/layout/layout.cpp ---------------------------------------------------------------------- diff --git a/weex_core/Source/core/layout/layout.cpp b/weex_core/Source/core/layout/layout.cpp new file mode 100644 index 0000000..d4655e3 --- /dev/null +++ b/weex_core/Source/core/layout/layout.cpp @@ -0,0 +1,1034 @@ +#include "layout.h" + +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); + 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); + 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(widthMeasureMode == kExactly && !isnan(width)){ + 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; + } + } +} + +
