/* Tencent is pleased to support the open source community by making Hippy available. * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "HPNode.h" #include #include #include //the layout progress refers https://www.w3.org/TR/css-flexbox-1/#layout-algorithm std::string getIndentString(int indent) { std::string str; for (int i = 0; i < indent; i++) { str += " "; } return str; } std::string toString(float value) { if (isUndefined(value)) { return "NAN"; } else { char str[10] = { 0 }; snprintf(str, 9, "%0.f", PixelRoundInt(value)); return str; } } void HPNode::printNode(uint32_t indent) { std::string indentStr = getIndentString(indent); std::string startStr; startStr = indentStr + "
\n"; HPLogd(startStr.c_str(), toString(result.dim[0]).c_str(), toString(result.dim[1]).c_str(), toString(result.position[0]).c_str(), toString(result.position[1]).c_str(), style.toString().c_str()); std::vector& items = children; for (size_t i = 0; i < items.size(); i++) { HPNodeRef item = items[i]; item->printNode(indent + 4); } std::string endStr = indentStr + "
\n"; HPLogd(endStr.c_str()); } HPNode::HPNode() { context = nullptr; parent = nullptr; measure = nullptr; dirtiedFunc = nullptr; initLayoutResult(); inInitailState = true; } HPNode::~HPNode() { //remove from parent if (parent != nullptr) { parent->removeChild(this); parent = nullptr; } //set child's parent as null for (size_t i = 0; i < children.size(); i++) { HPNodeRef item = children[i]; if (item != nullptr) { item->setParent(nullptr); } } children.clear(); } void HPNode::initLayoutResult() { #ifdef LAYOUT_TIME_ANALYZE fetchCount = 0; #endif isFrozen = false; isDirty = true; _hasNewLayout = false; result.dim[DimWidth] = 0; result.dim[DimHeight] = 0; memset((void *) result.position, 0, sizeof(float) * 4); memset((void *) result.cachedPosition, 0, sizeof(float) * 4); memset((void *) result.margin, 0, sizeof(float) * 4); memset((void *) result.padding, 0, sizeof(float) * 4); memset((void *) result.border, 0, sizeof(float) * 4); result.hadOverflow = false; result.direction = DirectionInherit; } bool HPNode::reset() { if (childCount() != 0 || getParent() != nullptr) return false; children.clear(); children.shrink_to_fit(); initLayoutResult(); inInitailState = true; return true; } void HPNode::resetLayoutRecursive(bool isDisplayNone) { if (inInitailState && isDisplayNone) { return; } initLayoutResult(); if (!isDisplayNone) { //see HPNode::removeChild //set result as undefined.see HPNodeChildTest.cpp //in tests/folder result.dim[DimWidth] = VALUE_UNDEFINED; result.dim[DimHeight] = VALUE_UNDEFINED; } else { inInitailState = true; //prevent resetLayoutRecursive run many times in recursive //in DisplayNone state, set hasNewLayout as true; //set dirty false; setHasNewLayout(true); setDirty(false); } //if just because parent's display type change, //not to clear child layout cache, can be reused. layoutCache.clearCache(); for (size_t i = 0; i < children.size(); i++) { HPNodeRef item = children[i]; item->resetLayoutRecursive(isDisplayNone); } } HPStyle HPNode::getStyle() { return style; } void HPNode::setStyle(const HPStyle& st) { style = st; //TODO:: layout if needed??? } bool HPNode::setMeasureFunc(HPMeasureFunc _measure) { if (measure == _measure) { return true; } //not leaf node , not set measure if (childCount() > 0) { return false; } measure = _measure; style.nodeType = _measure ? NodeTypeText : NodeTypeDefault; markAsDirty(); return true; } void HPNode::setParent(HPNodeRef _parent) { parent = _parent; } HPNodeRef HPNode::getParent() { return parent; } void HPNode::addChild(HPNodeRef item) { if (item == nullptr) { return; } item->setParent(this); children.push_back(item); markAsDirty(); } bool HPNode::insertChild(HPNodeRef item, uint32_t index) { //measure node cannot have child. if (item == nullptr || measure != nullptr) { return false; } item->setParent(this); children.insert(children.begin() + index, item); markAsDirty(); return true; } HPNodeRef HPNode::getChild(uint32_t index) { if (index > children.size() - 1) { return nullptr; } return children[index]; } bool HPNode::removeChild(HPNodeRef child) { std::vector::iterator p = std::find(children.begin(), children.end(), child); if (p != children.end()) { children.erase(p); child->setParent(nullptr); child->resetLayoutRecursive(false); markAsDirty(); return true; } return false; } bool HPNode::removeChild(uint32_t index) { if (index > children.size() - 1) { return false; } HPNodeRef child = getChild(index); if (child != nullptr) { child->setParent(nullptr); child->resetLayoutRecursive(false); } children.erase(children.begin() + index); markAsDirty(); return true; } uint32_t HPNode::childCount() { return children.size(); } void HPNode::setDisplayType(DisplayType displayType) { if (style.displayType == displayType) return; style.displayType = displayType; isDirty = false; //force following markAsDirty did effect to its parent markAsDirty(); } void HPNode::markAsDirty() { if (!isDirty) { setDirty(true); if (parent) { parent->markAsDirty(); } } } void HPNode::setHasNewLayout(bool hasNewLayoutOrNot) { _hasNewLayout = hasNewLayoutOrNot; } bool HPNode::hasNewLayout() { return _hasNewLayout; } void HPNode::setDirty(bool dirtyOrNot) { if (isDirty == dirtyOrNot) { return; } isDirty = dirtyOrNot; if (isDirty) { //reset layout direction to initial state //need to calculated again setLayoutDirection(DirectionInherit); //if is dirty, reset frozen. isFrozen = false; //if is dirty, layout cache muse be in clear state. layoutCache.clearCache(); if (dirtiedFunc != nullptr) { dirtiedFunc(this); } } } void HPNode::setDirtiedFunc(HPDirtiedFunc _dirtiedFunc) { dirtiedFunc = _dirtiedFunc; } void HPNode::setContext(void * _context) { context = _context; } void * HPNode::getContext() { return context; } bool HPNode::isLayoutDimDefined(FlexDirection axis) { return isDefined(result.dim[axisDim[axis]]); } void HPNode::setLayoutDim(FlexDirection axis, float value) { result.dim[axisDim[axis]] = value; } inline void HPNode::setLayoutDirection(HPDirection direction) { result.direction = direction; } inline HPDirection HPNode::getLayoutDirection() { return result.direction; } float HPNode::getLayoutDim(FlexDirection axis) { if (!isLayoutDimDefined(axis)) { return VALUE_UNDEFINED; } return result.dim[axisDim[axis]]; } float HPNode::getMainAxisDim() { FlexDirection mainAxis = style.flexDirection; if (!isLayoutDimDefined(mainAxis)) { return VALUE_UNDEFINED; } return result.dim[axisDim[mainAxis]]; } float HPNode::getStartBorder(FlexDirection axis) { return style.getStartBorder(axis); } float HPNode::getEndBorder(FlexDirection axis) { return style.getEndBorder(axis); } float HPNode::getStartPaddingAndBorder(FlexDirection axis) { return style.getStartPadding(axis) + style.getStartBorder(axis); } float HPNode::getEndPaddingAndBorder(FlexDirection axis) { return style.getEndPadding(axis) + style.getEndBorder(axis); } float HPNode::getPaddingAndBorder(FlexDirection axis) { return getStartPaddingAndBorder(axis) + getEndPaddingAndBorder(axis); } float HPNode::getStartMargin(FlexDirection axis) { return style.getStartMargin(axis); } float HPNode::getEndMargin(FlexDirection axis) { return style.getEndMargin(axis); } float HPNode::getMargin(FlexDirection axis) { return style.getStartMargin(axis) + style.getEndMargin(axis); } bool HPNode::isAutoStartMargin(FlexDirection axis) { return style.isAutoStartMargin(axis); } bool HPNode::isAutoEndMargin(FlexDirection axis) { return style.isAutoEndMargin(axis); } void HPNode::setLayoutStartMargin(FlexDirection axis, float value) { result.margin[axisStart[axis]] = value; } void HPNode::setLayoutEndMargin(FlexDirection axis, float value) { result.margin[axisEnd[axis]] = value; } inline float HPNode::getLayoutMargin(FlexDirection axis) { return getLayoutStartMargin(axis) + getLayoutEndMargin(axis); } float HPNode::getLayoutStartMargin(FlexDirection axis) { return isDefined(result.margin[axisStart[axis]]) ? result.margin[axisStart[axis]] : 0; } float HPNode::getLayoutEndMargin(FlexDirection axis) { return isDefined(result.margin[axisEnd[axis]]) ? result.margin[axisEnd[axis]] : 0; } /* If both axisStart and axisEnd are defined, * then use axisStart. Otherwise use which is defined. * @param axis flex direction * @param forAxisStart * true get relative value for axis start * false get relative value for axis end */ float HPNode::resolveRelativePosition(FlexDirection axis, bool forAxisStart) { if (style.positionType != PositionTypeRelative) { return 0.0f; } if (isDefined(style.getStartPosition(axis))) { float value = style.getStartPosition(axis); return forAxisStart ? value : -value; } else if (isDefined(style.getEndPosition(axis))) { float value = style.getEndPosition(axis); return forAxisStart ? -value : value; } return 0.0f; } void HPNode::setLayoutStartPosition(FlexDirection axis, float value, bool addRelativePosition) { if (addRelativePosition && style.positionType == PositionTypeRelative) { value += resolveRelativePosition(axis, true); } if (!FloatIsEqual(result.cachedPosition[axisStart[axis]], value)) { result.cachedPosition[axisStart[axis]] = value; setHasNewLayout(true); } result.position[axisStart[axis]] = value; } void HPNode::setLayoutEndPosition(FlexDirection axis, float value, bool addRelativePosition) { if (addRelativePosition && style.positionType == PositionTypeRelative) { value += resolveRelativePosition(axis, false); } if (!FloatIsEqual(result.cachedPosition[axisEnd[axis]], value)) { result.cachedPosition[axisEnd[axis]] = value; setHasNewLayout(true); } result.position[axisEnd[axis]] = value; } float HPNode::getLayoutStartPosition(FlexDirection axis) { return result.position[axisStart[axis]]; } float HPNode::getLayoutEndPosition(FlexDirection axis) { return result.position[axisEnd[axis]]; } //calculate main axis by refer this node's flex direction //and layout direction which resolved in resolveDirection. FlexDirection HPNode::resolveMainAxis() { FlexDirection mainAxis = style.flexDirection; HPDirection direction = getLayoutDirection(); if (direction == DirectionRTL) { if (mainAxis == FLexDirectionRow) { return FLexDirectionRowReverse; } else if (mainAxis == FLexDirectionRowReverse) { return FLexDirectionRow; } } return mainAxis; } /* calculate cross axis by refer this node's flex properties * and layout direction which resolved in resolveDirection. * must use this method when cross axis alignment * to determine cross start & cross end */ FlexDirection HPNode::resolveCrossAxis() { FlexDirection mainAxis = style.flexDirection; FlexDirection crossAxis; //cross axis's direction rely on flex wrap mode. if (isRowDirection(mainAxis)) { if (style.flexWrap == FlexWrapReverse) { crossAxis = FLexDirectionColumnReverse; } else { crossAxis = FLexDirectionColumn; } } else { if (style.flexWrap == FlexWrapReverse) { crossAxis = FLexDirectionRowReverse; } else { crossAxis = FLexDirectionRow; } //in this situation crossAxis is Row Direction //should think about node's layout direction(HPDirection) HPDirection direction = getLayoutDirection(); if (direction == DirectionRTL) { if (crossAxis == FLexDirectionRow) { crossAxis = FLexDirectionRowReverse; } else if (crossAxis == FLexDirectionRowReverse) { crossAxis = FLexDirectionRow; } } } return crossAxis; } FlexAlign HPNode::getNodeAlign(HPNodeRef item) { ASSERT(item != nullptr); if (item->style.alignSelf == FlexAlignAuto) { return style.alignItems; } return item->style.alignSelf; } float HPNode::boundAxis(FlexDirection axis, float value) { float min = style.minDim[axisDim[axis]]; float max = style.maxDim[axisDim[axis]]; float boundValue = value; if (!isUndefined(max) && max >= 0.0 && boundValue > max) { boundValue = max; } if (!isUndefined(min) && min >= 0.0 && boundValue < min) { boundValue = min; } return boundValue; } inline HPDirection HPNode::resolveDirection(HPDirection parentDirection) { return style.direction == DirectionInherit ? (parentDirection > DirectionInherit ? parentDirection : DirectionLTR) : style.direction; } //called after resolveDirection void HPNode::resolveStyleValues() { // if (!isDirty) { // return; // } FlexDirection mainAxis = resolveMainAxis(); FlexDirection crossAxis = resolveCrossAxis(); //set layout margin value //auto margins are treated as zero. may be modified during layout process setLayoutStartMargin(mainAxis, getStartMargin(mainAxis)); setLayoutEndMargin(mainAxis, getEndMargin(mainAxis)); setLayoutStartMargin(crossAxis, getStartMargin(crossAxis)); setLayoutEndMargin(crossAxis, getEndMargin(crossAxis)); //set layout padding value result.padding[axisStart[mainAxis]] = style.getStartPadding(mainAxis); result.padding[axisEnd[mainAxis]] = style.getEndPadding(mainAxis); result.padding[axisStart[crossAxis]] = style.getStartPadding(crossAxis); result.padding[axisEnd[crossAxis]] = style.getEndPadding(crossAxis); //set layout border value; result.border[axisStart[mainAxis]] = style.getStartBorder(mainAxis); result.border[axisEnd[mainAxis]] = style.getEndBorder(mainAxis); result.border[axisStart[crossAxis]] = style.getStartBorder(crossAxis); result.border[axisEnd[crossAxis]] = style.getEndBorder(crossAxis); } #ifdef LAYOUT_TIME_ANALYZE static int layoutCount = 0; static int layoutCacheCount = 0; static int measureCount = 0; static int measureCacheCount = 0; #endif void HPNode::layout(float parentWidth, float parentHeight, HPDirection parentDirection, void * layoutContext) { #ifdef LAYOUT_TIME_ANALYZE layoutCount = 0; layoutCacheCount = 0; measureCount = 0; measureCacheCount = 0; #endif if (isUndefined( style.flexBasis) && !isUndefined(style.dim[axisDim[style.flexDirection]])) { style.flexBasis = style.dim[axisDim[style.flexDirection]]; } //if container not set itself width and parent width is set, //set container width as parentWidth subtract margin bool styleWidthReset = false; if (isUndefined(style.dim[DimWidth]) && isDefined(parentWidth)) { float containerWidth = parentWidth - getMargin(FLexDirectionRow); style.setDim(DimWidth, containerWidth > 0.0f ? containerWidth : 0.0f); styleWidthReset = true; } bool styleHeightReset = false; if (isUndefined(style.dim[DimHeight]) && isDefined(parentHeight)) { float containerHeight = parentHeight - getMargin(FLexDirectionColumn); style.setDim(DimHeight, containerHeight > 0.0f ? containerHeight : 0.0f); styleHeightReset = true; } layoutImpl(parentWidth, parentHeight, parentDirection, LayoutActionLayout, layoutContext); if (styleWidthReset) { style.setDim(DimWidth, VALUE_UNDEFINED); } if (styleHeightReset) { style.setDim(DimHeight, VALUE_UNDEFINED); } //calculate container's position FlexDirection mainAxis = resolveMainAxis(); FlexDirection crossAxis = resolveCrossAxis(); setLayoutStartPosition(mainAxis, getStartMargin(mainAxis), true); setLayoutEndPosition(mainAxis, getEndMargin(mainAxis), true); setLayoutStartPosition(crossAxis, getStartMargin(crossAxis), true); setLayoutEndPosition(crossAxis, getEndMargin(crossAxis), true); //node 's layout is complete //convert its and its descendants position and size to a integer value. #ifndef ANDROID convertLayoutResult(0.0f, 0.0f); //layout result convert has been taken in java . 3.8.2018. ianwang.. #endif #ifdef LAYOUT_TIME_ANALYZE HPLog(LogLevelDebug,"HippyLayoutTime layout: count %d cache %d, measure: count %d cache %d", layoutCount,layoutCacheCount,measureCount,measureCacheCount); #endif } //3.Determine the flex base size and hypothetical main size of each item void HPNode::calculateItemsFlexBasis(HPSize availableSize, void * layoutContext) { FlexDirection mainAxis = style.flexDirection; std::vector& items = children; for (size_t i = 0; i < items.size(); i++) { HPNodeRef item = items[i]; //for display none item, reset its and its descendants layout result. if (item->style.displayType == DisplayTypeNone) { item->resetLayoutRecursive(); continue; } //https://stackoverflow.com/questions/34352140/what-are-the-differences-between-flex-basis-and-width //flex-basis has no effect on absolutely-positioned flex items. width and height properties //would be necessary. //Absolutely-positioned flex items do not participate in flex layout. if (item->style.positionType == PositionTypeAbsolute) { continue; } //3.Determine the flex base size and hypothetical main size of each item: //3.1 If the item has a definite used flex basis, that's the flex base size. if (isDefined( item->style.getFlexBasis()) && isDefined(style.dim[axisDim[mainAxis]])) { item->result.flexBaseSize = item->style.getFlexBasis(); } else if (isDefined(item->style.dim[axisDim[mainAxis]])) { //flex-basis:auto: //When specified on a flex item, the auto keyword retrieves the value //of the main size property as the used flex-basis. //If that value is itself auto, then the used value is content. item->result.flexBaseSize = item->style.dim[axisDim[mainAxis]]; } else { //3.2 Otherwise, size the item into the available space using its used flex basis //in place of its main size, float oldMainDim = item->style.getDim(mainAxis); //item->style.flexBasis is auto value item->style.setDim(mainAxis, item->style.flexBasis); item->layoutImpl( availableSize.width, availableSize.height, getLayoutDirection(), isRowDirection(mainAxis) ? LayoutActionMeasureWidth : LayoutActionMeasureHeight, layoutContext); item->style.setDim(mainAxis, oldMainDim); item->result.flexBaseSize = isDefined(item->result.dim[axisDim[mainAxis]]) ? item->result.dim[axisDim[mainAxis]] : 0; } //item->result.dim[axisDim[mainAxis]] = item->boundAxis(mainAxis, item->result.flexBasis); //The hypothetical main size is the item's flex base size clamped //according to its min and max main size properties (and flooring the content box size at zero). item->result.hypotheticalMainAxisSize = item->boundAxis( mainAxis, item->result.flexBaseSize); item->result.hypotheticalMainAxisMarginBoxSize = item->result .hypotheticalMainAxisSize + item->getMargin(mainAxis); } } bool HPNode::collectFlexLines(std::vector& flexLines, HPSize availableSize) { std::vector& items = children; bool sumHypotheticalMainSizeOverflow = false; float availableWidth = axisDim[style.flexDirection] == DimWidth ? availableSize.width : availableSize.height; if (isUndefined(availableWidth)) { availableWidth = INFINITY; } FlexLine * line = nullptr; int itemsSize = items.size(); int i = 0; while (i < itemsSize) { HPNodeRef item = items[i]; if (item->style.positionType == PositionTypeAbsolute || item->style.displayType == DisplayTypeNone) { //see HippyTest.dirty_mark_all_children_as_dirty_when_display_changes //when display changes. if (i == itemsSize - 1 && line != nullptr) { flexLines.push_back(line); break; } // i++; continue; } if (line == nullptr) { line = new FlexLine(this); } float leftSpace = availableWidth - (line->sumHypotheticalMainSize + item->result.hypotheticalMainAxisMarginBoxSize); if (leftSpace < 0) { //may be line wrap happened sumHypotheticalMainSizeOverflow = true; } if (style.flexWrap == FlexNoWrap) { line->addItem(item); if (i == itemsSize - 1) { flexLines.push_back(line); break; } i++; } else { if (leftSpace >= 0 || line->isEmpty()) { line->addItem(item); if (i == itemsSize - 1) { flexLines.push_back(line); line = nullptr; } i++; } else { flexLines.push_back(line); line = nullptr; } } } return sumHypotheticalMainSizeOverflow; } void HPNode::cacheLayoutOrMeasureResult(HPSize availableSize, HPSizeMode measureMode, FlexLayoutAction layoutAction) { HPSize resultSize = { result.dim[DimWidth], result.dim[DimHeight] }; layoutCache.cacheResult(availableSize, resultSize, measureMode, layoutAction); if (layoutAction == LayoutActionLayout) { setDirty(false); setHasNewLayout(true); inInitailState = false; } } /* * availableWidth/availableHeight has subtract its margin and padding. */ void HPNode::layoutSingleNode(float availableWidth, MeasureMode widthMeasureMode, float availableHeight, MeasureMode heightMeasureMode, FlexLayoutAction layoutAction, void * layoutContext) { if (widthMeasureMode == MeasureModeExactly && heightMeasureMode == MeasureModeExactly) { result.dim[DimWidth] = availableWidth + getPaddingAndBorder(FLexDirectionRow); result.dim[DimHeight] = availableHeight + getPaddingAndBorder(FLexDirectionColumn); } else { //measure text, image etc. content node; HPSize dim = { 0, 0 }; bool needMeasure = true; if (style.flexGrow > 0 && style.flexShrink > 0 && parent && parent->childCount() == 1 && !parent->style.isDimensionAuto(FLexDirectionRow) && !parent->style.isDimensionAuto(FLexDirectionColumn)) { //don't measure single grow shrink child //see HPMeasureTest.cpp dont_measure_single_grow_shrink_child needMeasure = false; } if (!needMeasure) { dim.width = availableWidth; dim.height = availableHeight; } else if (measure != nullptr && needMeasure) { dim = measure(this, availableWidth, widthMeasureMode, availableHeight, heightMeasureMode, layoutContext); } result.dim[DimWidth] = boundAxis( FLexDirectionRow, widthMeasureMode == MeasureModeExactly ? (availableWidth + getPaddingAndBorder(FLexDirectionRow)) : (dim.width + getPaddingAndBorder(FLexDirectionRow))); result.dim[DimHeight] = boundAxis( FLexDirectionColumn, heightMeasureMode == MeasureModeExactly ? (availableHeight + getPaddingAndBorder(FLexDirectionColumn)) : (dim.height + getPaddingAndBorder(FLexDirectionColumn))); } HPSize availableSize = { availableWidth, availableHeight }; HPSizeMode measureMode = { widthMeasureMode, heightMeasureMode }; cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction); } //reference: https://www.w3.org/TR/css-flexbox-1/#layout-algorithm void HPNode::layoutImpl(float parentWidth, float parentHeight, HPDirection parentDirection, FlexLayoutAction layoutAction, void * layoutContext) { #ifdef LAYOUT_TIME_ANALYZE if(layoutAction == LayoutActionLayout) { layoutCount++; } else { measureCount++; } #endif HPDirection direction = resolveDirection(parentDirection); if (getLayoutDirection() != direction) { setLayoutDirection(direction); layoutCache.clearCache(); resolveStyleValues(); } FlexDirection mainAxis = style.flexDirection; bool performLayout = layoutAction == LayoutActionLayout; if (isDefined(parentWidth)) { parentWidth -= getMargin(FLexDirectionRow); parentWidth = parentWidth >= 0.0f ? parentWidth : 0.0f; } if (isDefined(parentHeight)) { parentHeight -= getMargin(FLexDirectionColumn); parentHeight = parentHeight >= 0.0f ? parentHeight : 0.0f; } //get node dim from style float nodeWidth = isDefined(style.dim[DimWidth]) ? boundAxis(FLexDirectionRow, style.dim[DimWidth]) : VALUE_UNDEFINED; float nodeHeight = isDefined(style.dim[DimHeight]) ? boundAxis(FLexDirectionColumn, style.dim[DimHeight]) : VALUE_UNDEFINED; //layoutMeasuredWidth layoutMeasuredHeight used in //"Determine the flex base size and hypothetical main size of each item" if (layoutAction == LayoutActionMeasureWidth && isDefined(nodeWidth)) { #ifdef LAYOUT_TIME_ANALYZE measureCacheCount++; #endif result.dim[DimWidth] = nodeWidth; return; } else if (layoutAction == LayoutActionMeasureHeight && isDefined(nodeHeight)) { #ifdef LAYOUT_TIME_ANALYZE measureCacheCount++; #endif result.dim[DimHeight] = nodeHeight; return; } //9.2.Line Length Determination //Determine the available main and cross space for the flex items. //For each dimension, if that dimension of the flex container's content box is a definite size, //use that; if that dimension of the flex container is being sized under a min or max-content constraint, //the available space in that dimension is that constraint; //otherwise, subtract the flex container's margin, border, and padding //from the space available to the flex container in that dimension and use that value. //This might result in an infinite value. //get available size for layout or measure float availableWidth = VALUE_UNDEFINED; if (isDefined(nodeWidth)) { availableWidth = nodeWidth - getPaddingAndBorder(FLexDirectionRow); } else if (isDefined(parentWidth)) { availableWidth = parentWidth - getPaddingAndBorder(FLexDirectionRow); } float availableHeight = VALUE_UNDEFINED; if (isDefined(nodeHeight)) { availableHeight = nodeHeight - getPaddingAndBorder(FLexDirectionColumn); } else if (isDefined(parentHeight)) { availableHeight = parentHeight - getPaddingAndBorder(FLexDirectionColumn); } if (isDefined(style.maxDim[DimWidth])) { if (FloatIsEqual(style.maxDim[DimWidth], style.minDim[DimWidth])) { style.dim[DimWidth] = style.minDim[DimWidth]; } float maxDimWidth = style.maxDim[DimWidth] - getPaddingAndBorder(FLexDirectionRow); if (maxDimWidth >= 0.0f && maxDimWidth < NanAsINF(availableWidth)) { availableWidth = maxDimWidth; } } if (isDefined(style.maxDim[DimHeight])) { if (FloatIsEqual(style.maxDim[DimHeight], style.minDim[DimHeight])) { style.dim[DimHeight] = style.minDim[DimHeight]; } float maxDimHeight = style.maxDim[DimHeight] - getPaddingAndBorder(FLexDirectionColumn); if (maxDimHeight >= 0.0f && maxDimHeight < NanAsINF(availableHeight)) { availableHeight = maxDimHeight; } } //available size to layout... availableWidth = availableWidth < 0.0f ? 0.0f : availableWidth; availableHeight = availableHeight < 0.0f ? 0.0f : availableHeight; MeasureMode widthMeasureMode = MeasureModeUndefined; if (isDefined(style.dim[DimWidth])) { widthMeasureMode = MeasureModeExactly; } else if (isDefined(availableWidth)) { if (parent && parent->style.isOverflowScroll() && isRowDirection(parent->style.flexDirection)) { widthMeasureMode = MeasureModeUndefined; availableWidth = VALUE_AUTO; } else { widthMeasureMode = MeasureModeAtMost; } } MeasureMode heightMeasureMode = MeasureModeUndefined; if (isDefined(style.dim[DimHeight])) { heightMeasureMode = MeasureModeExactly; } else if (isDefined(availableHeight)) { if (parent && parent->style.isOverflowScroll() && isColumnDirection(parent->style.flexDirection)) { heightMeasureMode = MeasureModeUndefined; availableHeight = VALUE_AUTO; } else { heightMeasureMode = MeasureModeAtMost; } } HPSize availableSize = { availableWidth, availableHeight }; HPSizeMode measureMode = { widthMeasureMode, heightMeasureMode }; MeasureResult* cacheResult = layoutCache.getCachedMeasureResult( availableSize, measureMode, layoutAction, measure != nullptr); if (cacheResult != nullptr) { //set Result.... switch (layoutAction) { case LayoutActionMeasureWidth: #ifdef LAYOUT_TIME_ANALYZE measureCacheCount++; #endif ASSERT(isDefined(cacheResult->resultSize.width)); result.dim[DimWidth] = cacheResult->resultSize.width; break; case LayoutActionMeasureHeight: #ifdef LAYOUT_TIME_ANALYZE measureCacheCount++; #endif ASSERT(isDefined(cacheResult->resultSize.height)); result.dim[DimHeight] = cacheResult->resultSize.height; break; case LayoutActionLayout: //if it's a measure node and cache result cached by LayoutActionMeasureWidth or LayoutActionMeasureHeight //so this is first layout for current Measure Node, set hasNewLayout as true, //if not, this node's layout result value has been fetched to java and set hasNewLayout false in FlexNode.cc //so not set hasNewLayout as true to avoid JNI call again in FLexNode.cc if (cacheResult->layoutAction != LayoutActionLayout&& measure != nullptr) { //need assign result size if layoutAction is different 3.14.2018 result.dim[DimWidth] = cacheResult->resultSize.width; result.dim[DimHeight] = cacheResult->resultSize.height; cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction); } else { #ifdef LAYOUT_TIME_ANALYZE layoutCacheCount++; #endif //do nothing.. //layoutCache.cachedLayout object is last layout result. //used to determine need layout or not. } //if it's a measure node , layout could be cache by LayoutActionMeasureWidth //or LayoutActionMeasureHeight,so in this case, we need set dirty as false; setDirty(false); break; default: break; } return; } //before layout set result's hadOverflow as false. if (layoutAction == LayoutActionLayout) { result.hadOverflow = false; } //single element measure width and height if ((children.size() == 0)) { layoutSingleNode(availableWidth, widthMeasureMode, availableHeight, heightMeasureMode, layoutAction, layoutContext); return; } //3.Determine the flex base size and hypothetical main size of each item calculateItemsFlexBasis(availableSize, layoutContext); //9.3. Main Size Determination // 5. Collect flex items into flex lines: std::vector flexLines; bool sumHypotheticalMainSizeOverflow = collectFlexLines(flexLines, availableSize); //get max line's main size float maxSumItemsMainSize = 0; for (size_t i = 0; i < flexLines.size(); i++) { if (flexLines[i]->sumHypotheticalMainSize > maxSumItemsMainSize) { maxSumItemsMainSize = flexLines[i]->sumHypotheticalMainSize; } } // 4. Determine the main size of the flex container using the rules of the formatting context //in which it participates. For this computation, auto margins on flex items are treated as 0. //TODO:: if has set , what to do for next run in determineCrossAxisSize's layoutImpl float containerInnerMainSize = 0.0f; if (isDefined(style.dim[axisDim[mainAxis]])) { //MeasureModeExactly containerInnerMainSize = style.dim[axisDim[mainAxis]] - getPaddingAndBorder(mainAxis); } else { if (sumHypotheticalMainSizeOverflow) { //MeasureModeAtMost //if sum of hypothetical MainSize > available size; float mainInnerSize = axisDim[mainAxis] == DimWidth ? availableSize.width : availableSize.height; if (maxSumItemsMainSize > mainInnerSize && !style.isOverflowScroll()) { if (parent && parent->getNodeAlign(this) == FlexAlignStretch && axisDim[mainAxis] == axisDim[parent->resolveCrossAxis()] && style.positionType != PositionTypeAbsolute) { //it this node has text child and node main axis(width) is stretch //,cross axis length(height) is undefined //text can has multi-line, text's height can affect parent's height //in this situation, use availableSize if possible containerInnerMainSize = mainInnerSize; } else { containerInnerMainSize = maxSumItemsMainSize; } } else { containerInnerMainSize = mainInnerSize; } } else { containerInnerMainSize = maxSumItemsMainSize; } } result.dim[axisDim[mainAxis]] = boundAxis( mainAxis, containerInnerMainSize + getPaddingAndBorder(mainAxis)); //return if its just in measure if ((layoutAction == LayoutActionMeasureWidth && isRowDirection(mainAxis)) || (layoutAction == LayoutActionMeasureHeight && isColumnDirection(mainAxis))) { //cache layout result & state... cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction); //free flexLines, allocate in collectFlexLines. //TODO:: opt. for (size_t i = 0; i < flexLines.size(); i++) { delete flexLines[i]; } return; } //6. Resolving Flexible Lengths //To resolve the flexible lengths of the items within a flex line: //TODO:://this's the only place that confirm child items main axis size, see item->setLayoutDim determineItemsMainAxisSize(flexLines, layoutAction); //9.4. Cross Size Determination //calculate line's cross size in flexLines //TODO:: The real place that Determine //the flex container's used cross size is at step 15. float sumLinesCrossSize = determineCrossAxisSize(flexLines, availableSize, layoutAction, layoutContext); if (!performLayout) { //TODO::for measure, I put the calculate of flex container's cross size in here.. //TODO::why must in step 15 in W3 flex layout algorithm //noted by ianwang 12.30.2017. //15.Determine the flex container's used cross size: //If the cross size property is a definite size, use that, //clamped by the min and max cross size properties of the flex container. //Otherwise, use the sum of the flex lines' cross sizes, //clamped by the min and max cross size properties of the flex container. FlexDirection crossAxis = resolveCrossAxis(); float crossDimSize; if (isDefined(style.dim[axisDim[crossAxis]])) { crossDimSize = style.dim[axisDim[crossAxis]]; } else { crossDimSize = (sumLinesCrossSize + getPaddingAndBorder(crossAxis)); } result.dim[axisDim[crossAxis]] = boundAxis(crossAxis, crossDimSize); //cache layout result & state... cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction); //free flexLines, allocate in collectFlexLines. for (size_t i = 0; i < flexLines.size(); i++) { delete flexLines[i]; } return; } //9.5. Main-Axis Alignment mainAxisAlignment(flexLines); //9.6. Cross-Axis Alignment //if contianer's innerCross size not defined, //then it will be determined in step 15 of crossAxisAlignment crossAxisAlignment(flexLines); //free flexLines, allocate in collectFlexLines. for (size_t i = 0; i < flexLines.size(); i++) { delete flexLines[i]; } //cache layout result & state... cacheLayoutOrMeasureResult(availableSize, measureMode, layoutAction); //layout fixed elements... layoutFixedItems(measureMode, layoutContext); return; } //9.4. Cross Size Determination float HPNode::determineCrossAxisSize(std::vector& flexLines, HPSize availableSize, FlexLayoutAction layoutAction, void * layoutContext) { FlexDirection mainAxis = style.flexDirection; FlexDirection crossAxis = resolveCrossAxis(); float sumLinesCrossSize = 0; for (size_t i = 0; i < flexLines.size(); i++) { FlexLine * line = flexLines[i]; float maxItemCrossSize = 0; for (size_t j = 0; j < line->items.size(); j++) { HPNodeRef item = line->items[j]; //item's main axis size has been determined. //try to calculate the hypothetical cross size of each item //that would be stored in result.dim[crossAxis] //align stretch may be modify this value in the later step. //WARNING TODO::this is the only place that the Recursive flex layout happen. //7.Determine the hypothetical cross size of each item by performing layout with //the used main size and the available space, treating auto as fit-content. FlexLayoutAction oldLayoutAction = layoutAction; if (getNodeAlign(item) == FlexAlignStretch && item->style.isDimensionAuto(crossAxis) && !item->style.hasAutoMargin(crossAxis) && layoutAction == LayoutActionLayout) { //Delay layout for stretch item, do layout later in step 11. layoutAction = axisDim[crossAxis] == DimWidth ? LayoutActionMeasureWidth : LayoutActionMeasureHeight; } float oldMainDim = item->style.getDim(mainAxis); item->style.setDim(mainAxis, item->getLayoutDim(mainAxis)); item->layoutImpl(availableSize.width, availableSize.height, getLayoutDirection(), layoutAction, layoutContext); item->style.setDim(mainAxis, oldMainDim); layoutAction = oldLayoutAction; //if child item had overflow , then transfer this state to its parent. //see HippyTest_HadOverflowTests.spacing_overflow_in_nested_nodes in ./tests/HPHadOverflowTest.cpp result.hadOverflow = result.hadOverflow | item->result.hadOverflow; //TODO:: if need support baseline add here //8.Calculate the cross size of each flex line. //1)Collect all the flex items whose inline-axis is parallel to the main-axis, //whose align-self is baseline, and whose cross-axis margins are both non-auto. //Find the largest of the distances between each item's baseline and its hypothetical //outer cross-start edge, and the largest of the distances between each item's baseline //and its hypothetical outer cross-end edge, and sum these two values. //2)Among all the items not collected by the previous step, find the largest outer //hypothetical cross size. //3)The used cross-size of the flex line is the largest of the numbers found //in the previous two steps and zero. //Max item cross size float itemOutCrossSize = item->getLayoutDim(crossAxis) + item->getMargin(crossAxis); if (itemOutCrossSize > maxItemCrossSize) { maxItemCrossSize = itemOutCrossSize; } } //8.Calculate the cross size of each flex line. //clip current container cross axis size.. maxItemCrossSize = boundAxis(crossAxis, maxItemCrossSize); line->lineCrossSize = maxItemCrossSize; sumLinesCrossSize += maxItemCrossSize; //single line , set line height as container inner height if (flexLines.size() == 1 && isDefined(style.dim[axisDim[crossAxis]])) { //if following assert is true, means front-end's style is in unsuitable state .. //such as main axis is undefined but set flex-wrap as FlexWrap. //ASSERT(style.flexWrap == FlexNoWrap); float innerCrossSize = boundAxis(crossAxis, style.dim[axisDim[crossAxis]]) - getPaddingAndBorder(crossAxis); line->lineCrossSize = innerCrossSize; sumLinesCrossSize = innerCrossSize; } } //9.Handle 'align-content: stretch' for lines if (isDefined(style.dim[axisDim[crossAxis]]) && style.alignContent == FlexAlignStretch) { float innerCrossSize = boundAxis(crossAxis, style.dim[axisDim[crossAxis]]) - getPaddingAndBorder(crossAxis); if (sumLinesCrossSize < innerCrossSize) { for (size_t i = 0; i < flexLines.size(); i++) { FlexLine * line = flexLines[i]; line->lineCrossSize += (innerCrossSize - sumLinesCrossSize) / flexLines.size(); } } } //11.Determine the used cross size of each flex item //Think about item align-self: stretch for (size_t i = 0; i < flexLines.size(); i++) { FlexLine * line = flexLines[i]; for (size_t j = 0; j < line->items.size(); j++) { HPNodeRef item = line->items[j]; //1): If a flex item has align-self: stretch, its computed cross size property is auto, // and neither of its cross-axis margins are auto, the used outer cross size is the used // cross size of its flex line, clamped according to the item's // min and max cross size properties. //2):Otherwise,the used cross size is the item's hypothetical cross size. if (getNodeAlign(item) == FlexAlignStretch && item->style.isDimensionAuto(crossAxis) && !item->style.hasAutoMargin(crossAxis)) { item->result.dim[axisDim[crossAxis]] = item->boundAxis( crossAxis, line->lineCrossSize - item->getMargin(crossAxis)); //If the flex item has align-self: stretch, redo layout for its contents, //treating this used size as its definite cross size //so that percentage-sized children can be resolved. float oldMainDim = item->style.getDim(mainAxis); float oldCrossDim = item->style.getDim(crossAxis); item->style.setDim(mainAxis, item->getLayoutDim(mainAxis)); item->style.setDim(crossAxis, item->getLayoutDim(crossAxis)); item->layoutImpl(availableSize.width, availableSize.height, getLayoutDirection(), layoutAction, layoutContext); item->style.setDim(mainAxis, oldMainDim); item->style.setDim(crossAxis, oldCrossDim); } else { //Otherwise, the used cross size is the item's hypothetical cross size. //see the step7. //item's hypothetical cross size. has been set in result.dim[axisDim[crossAxis]]. } } } //TODO::Why Determine the flex container's used cross size in step 15. return sumLinesCrossSize; } //See 9.7 Resolving Flexible Lengths. void HPNode::determineItemsMainAxisSize(std::vector & flexLines, FlexLayoutAction layoutAction) { FlexDirection mainAxis = style.flexDirection; float mainAxisContentSize = result.dim[axisDim[mainAxis]] - getPaddingAndBorder(mainAxis); // 6. Resolve the flexible lengths of all the flex items to find their used main size (see section 9.7.) for (size_t i = 0; i < flexLines.size(); i++) { FlexLine * line = flexLines[i]; line->SetContainerMainInnerSize(mainAxisContentSize); line->FreezeInflexibleItems(layoutAction); while (!line->ResolveFlexibleLengths()) { ASSERT(line->totalFlexGrow >= 0); ASSERT(line->totalFlexGrow >= 0); } if (layoutAction == LayoutActionLayout && line->remainingFreeSpace < 0) { result.hadOverflow = true; } } } //9.5 Main-Axis Alignment void HPNode::mainAxisAlignment(std::vector & flexLines) { //TODO:: RTL:: // 12. Distribute any remaining free space. For each flex line: FlexDirection mainAxis = style.flexDirection; float mainAxisContentSize = getLayoutDim(mainAxis) - getPaddingAndBorder(mainAxis); for (size_t i = 0; i < flexLines.size(); i++) { FlexLine * line = flexLines[i]; line->SetContainerMainInnerSize(mainAxisContentSize); line->alignItems(); } } //9.6 Cross-Axis Alignment void HPNode::crossAxisAlignment(std::vector & flexLines) { FlexDirection crossAxis = resolveCrossAxis(); float sumLinesCrossSize = 0; int linesCount = flexLines.size(); for (int i = 0; i < linesCount; i++) { FlexLine * line = flexLines[i]; sumLinesCrossSize += line->lineCrossSize; for (size_t j = 0; j < line->items.size(); j++) { HPNodeRef item = line->items[j]; //13.Resolve cross-axis auto margins. If a flex item has auto cross-axis margins: float remainingFreeSpace = line->lineCrossSize - item->result.dim[axisDim[crossAxis]] - item->getMargin(crossAxis); if (remainingFreeSpace > 0) { //If its outer cross size (treating those auto margins as zero) is less than //the cross size of its flex line, distribute the difference //in those sizes equally to the auto margins. if (item->isAutoStartMargin(crossAxis) && item->isAutoEndMargin(crossAxis)) { item->setLayoutStartMargin(crossAxis, remainingFreeSpace / 2); item->setLayoutEndMargin(crossAxis, remainingFreeSpace / 2); } else if (item->isAutoStartMargin(crossAxis)) { item->setLayoutStartMargin(crossAxis, remainingFreeSpace); } else if (item->isAutoEndMargin(crossAxis)) { item->setLayoutEndMargin(crossAxis, remainingFreeSpace); } else { //For margin:: assign style value to result value at this place.. item->setLayoutStartMargin(crossAxis, item->getStartMargin(crossAxis)); item->setLayoutEndMargin(crossAxis, item->getEndMargin(crossAxis)); } } else { //Otherwise, if the block-start or inline-start margin //(whichever is in the cross axis) is auto, set it to zero. //Set the opposite margin so that the outer cross size of the //item equals the cross size of its flex line. item->setLayoutStartMargin(crossAxis, item->getStartMargin(crossAxis)); item->setLayoutEndMargin(crossAxis, item->getEndMargin(crossAxis)); } //14.Align all flex items along the cross-axis per align-self, //if neither of the item's cross-axis margins are auto. //calculate item's offset in its line by style align-self remainingFreeSpace = line->lineCrossSize - item->result.dim[axisDim[crossAxis]] - item->getLayoutMargin(crossAxis); float offset = item->getLayoutStartMargin(crossAxis); switch (getNodeAlign(item)) { //when align self is auto , it overwrite by align items case FlexAlignStart: break; case FlexAlignCenter: offset += remainingFreeSpace / 2; break; case FlexAlignEnd: offset += remainingFreeSpace; break; //TODO:: case baseline alignment default: break; } //include (axisStart[crossAxis] == CSSTop) and (axisStart[crossAxis] == CSSBottom) //For temporary store. use false parameter item->setLayoutStartPosition(crossAxis, offset, false); } } //15.Determine the flex container's used cross size: //If the cross size property is a definite size, use that, //clamped by the min and max cross size properties of the flex container. //Otherwise, use the sum of the flex lines' cross sizes, //clamped by the min and max cross size properties of the flex container. float crossDimSize; if (isDefined(style.dim[axisDim[crossAxis]])) { crossDimSize = style.dim[axisDim[crossAxis]]; } else { crossDimSize = (sumLinesCrossSize + getPaddingAndBorder(crossAxis)); } result.dim[axisDim[crossAxis]] = boundAxis(crossAxis, crossDimSize); //when container's cross size determined align all flex lines by align-content //16.Align all flex lines per align-content float innerCrossSize = result.dim[axisDim[crossAxis]] - getPaddingAndBorder(crossAxis); float remainingFreeSpace = innerCrossSize - sumLinesCrossSize; float offset = getStartPaddingAndBorder(crossAxis); float space = 0; switch (style.alignContent) { case FlexAlignStart: break; case FlexAlignCenter: offset += remainingFreeSpace / 2; break; case FlexAlignEnd: offset += remainingFreeSpace; break; case FlexAlignSpaceBetween: space = remainingFreeSpace / (linesCount - 1); break; case FlexAlignSpaceAround: space = remainingFreeSpace / linesCount; offset += space / 2; break; default: break; } //flex-end:: //The cross-end margin edge of the flex item is placed flush with the cross-end edge of the line. //crossAxisPostionStart calculated along the cross axis direction. float crossAxisPostionStart = offset; for (int i = 0; i < linesCount; i++) { FlexLine * line = flexLines[i]; for (size_t j = 0; j < line->items.size(); j++) { HPNodeRef item = line->items[j]; //include (axisStart[crossAxis] == CSSTop) and (axisStart[crossAxis] == CSSBottom) //getLayoutStartPosition set in step 14. item->setLayoutStartPosition( crossAxis, crossAxisPostionStart + item->getLayoutStartPosition(crossAxis)); //layout start position has use relative ,so end position not use it ,use false parameter. item->setLayoutEndPosition( crossAxis, (getLayoutDim(crossAxis) - item->getLayoutStartPosition(crossAxis) - item->getLayoutDim(crossAxis)), false); } crossAxisPostionStart += line->lineCrossSize + space; } } //4.1. Absolutely-Positioned Flex Children //As it is out-of-flow, an absolutely-positioned child of a flex container //does not participate in flex layout. //The static position of an absolutely-positioned child of a flex container //is determined such that the child is positioned as if it were the sole flex item in the flex container, //assuming both the child and the flex container were fixed-size boxes of their used size. //For this purpose, auto margins are treated as zero. void HPNode::layoutFixedItems(HPSizeMode measureMode, void * layoutContext) { FlexDirection mainAxis = resolveMainAxis(); FlexDirection crossAxis = resolveCrossAxis(); std::vector& items = children; for (size_t i = 0; i < items.size(); i++) { HPNodeRef item = items[i]; //for display none item, reset its layout result. if (item->style.displayType == DisplayTypeNone) { item->resetLayoutRecursive(); continue; } if (item->style.positionType != PositionTypeAbsolute) { continue; } float parentWidth = getLayoutDim(FLexDirectionRow) - getPaddingAndBorder(FLexDirectionRow); float parentHeight = getLayoutDim(FLexDirectionColumn) - getPaddingAndBorder(FLexDirectionColumn); float itemOldStyleDimMainAxis = item->style.getDim(mainAxis); float itemOldStyleDimCrossAxis = item->style.getDim(crossAxis); if (isUndefined(itemOldStyleDimMainAxis) && isDefined(item->style.getStartPosition(mainAxis)) && isDefined(item->style.getEndPosition(mainAxis))) { item->style.setDim( mainAxis, (getLayoutDim(mainAxis) - style.getStartBorder(mainAxis) - style.getEndBorder(mainAxis) - item->style.getStartPosition(mainAxis) - item->style.getEndPosition(mainAxis) - item->getMargin(mainAxis))); } if (isUndefined(itemOldStyleDimCrossAxis) && isDefined(item->style.getStartPosition(crossAxis)) && isDefined(item->style.getEndPosition(crossAxis))) { item->style.setDim( crossAxis, (getLayoutDim(crossAxis) - style.getStartBorder(crossAxis) - style.getEndBorder(crossAxis) - item->style.getStartPosition(crossAxis) - item->style.getEndPosition(crossAxis) - item->getMargin(crossAxis))); } item->layoutImpl(parentWidth, parentHeight, getLayoutDirection(), LayoutActionLayout, layoutContext); //recover item's previous style value item->style.setDim(mainAxis, itemOldStyleDimMainAxis); item->style.setDim(crossAxis, itemOldStyleDimCrossAxis); //after layout, calculate fix item 's postion //1) for main axis calculateFixedItemPosition(item, mainAxis); //2)for cross axis calculateFixedItemPosition(item, crossAxis); } } //when item's layout complete, update fixed item's position on Specified axis //called in layoutFixedItems //should be called twice, one for main axis ,one for cross axis void HPNode::calculateFixedItemPosition(HPNodeRef item, FlexDirection axis) { if (isDefined(item->style.getStartPosition(axis))) { item->setLayoutStartPosition( axis, getStartBorder(axis) + item->getLayoutStartMargin(axis) + item->style.getStartPosition(axis)); item->setLayoutEndPosition( axis, getLayoutDim(axis) - item->getLayoutStartPosition(axis) - item->getLayoutDim(axis)); } else if (isDefined(item->style.getEndPosition(axis))) { item->setLayoutEndPosition( axis, getEndBorder(axis) + item->getLayoutEndMargin(axis) + item->style.getEndPosition(axis)); item->setLayoutStartPosition( axis, getLayoutDim(axis) - item->getLayoutEndPosition(axis) - item->getLayoutDim(axis)); } else { float remainingFreeSpace = getLayoutDim(axis) - getPaddingAndBorder(axis) - item->getLayoutDim(axis); float offset = getStartPaddingAndBorder(axis); FlexAlign alignMode = (axis == resolveMainAxis() ? style.justifyContent : getNodeAlign(item)); switch (alignMode) { case FlexAlignStart: break; case FlexAlignCenter: offset += remainingFreeSpace / 2; break; case FlexAlignEnd: offset += remainingFreeSpace; break; default: break; } item->setLayoutStartPosition( axis, getStartPaddingAndBorder(axis) + item->getLayoutStartMargin(axis) + offset); item->setLayoutEndPosition( axis, getLayoutDim(axis) - item->getLayoutStartPosition(axis) - item->getLayoutDim(axis)); } } //convert position and dimension values to integer value.. //absLeft, absTop is mainly think about the influence of parent's Fraction offset //for example: if parent's Fraction offset is 0.3 //and current child offset is 0.4 //then the child's absolute offset is 0.7. //if use roundf , roundf(0.7) == 1 //so we need absLeft, absTop parameter void HPNode::convertLayoutResult(float absLeft, float absTop) { if (!hasNewLayout()) { return; } const float left = result.position[CSSLeft]; const float top = result.position[CSSTop]; const float width = result.dim[DimWidth]; const float height = result.dim[DimHeight]; absLeft += left; absTop += top; bool isTextNode = style.nodeType == NodeTypeText; result.position[CSSLeft] = HPRoundValueToPixelGrid(left, false, isTextNode); result.position[CSSTop] = HPRoundValueToPixelGrid(top, false, isTextNode); const bool hasFractionalWidth = !FloatIsEqual(fmodf(width, 1.0), 0) && !FloatIsEqual(fmodf(width, 1.0), 1.0); const bool hasFractionalHeight = !FloatIsEqual(fmodf(height, 1.0), 0) && !FloatIsEqual(fmodf(height, 1.0), 1.0); const float absRight = absLeft + width; const float absBottom = absTop + height; result.dim[DimWidth] = HPRoundValueToPixelGrid( absRight, (isTextNode && hasFractionalWidth), (isTextNode && !hasFractionalWidth)) - HPRoundValueToPixelGrid(absLeft, false, isTextNode); result.dim[DimHeight] = HPRoundValueToPixelGrid( absBottom, (isTextNode && hasFractionalHeight), (isTextNode && !hasFractionalHeight)) - HPRoundValueToPixelGrid(absTop, false, isTextNode); std::vector& items = children; for (size_t i = 0; i < items.size(); i++) { HPNodeRef item = items[i]; item->convertLayoutResult(absLeft, absTop); } }