void ofxDOMFlexBoxLayout::align(FlexDirection direction){ bool horizontal = direction == FlexDirection::ROW; float paddingHorizontal = DOMLH::getPaddingHorizontal(_parent); float paddingVertical = DOMLH::getPaddingVertical(_parent); float wParent = ofGetWidth(); float hParent = ofGetHeight(); if(_parent->parent()){ wParent = _parent->getSizeByParent().x; hParent = _parent->getSizeByParent().y; } float totalWidth = DOMLH::getDesiredWidthStretched(_parent, wParent) - paddingHorizontal; float totalHeight = DOMLH::getDesiredHeightStretched(_parent, hParent) - paddingVertical; // if(ofxGuiElement* el = dynamic_cast<ofxGuiElement*>(_parent)){ // cout << el->getName() << " total size: " << totalWidth << " " << totalHeight << endl; // } vector<vector<DOM::Element*>> lines; vector<vector<DOM::Element*>> noFlexItems; vector<vector<DOM::Element*>> flexItems; vector<vector<float>> flexItemsBasis; vector<float> totalSpaceMainAxis; float mainAxisSize = horizontal ? totalWidth : totalHeight; float crossAxisSize = horizontal ? totalHeight : totalWidth; int linecount = 0; if(children().size() > 0){ //newline lines.push_back(vector<DOM::Element*>()); noFlexItems.push_back(vector<DOM::Element*>()); flexItems.push_back(vector<DOM::Element*>()); flexItemsBasis.push_back(vector<float>()); totalSpaceMainAxis.push_back(mainAxisSize); } //sort children according to flex attribute and main size of children for(unsigned int i = 0; i < children().size(); i++){ DOM::Element* element = children().at(i); float w, h; if(horizontal){ w = DOMLH::getDesiredWidth(element, totalWidth); h = DOMLH::getDesiredHeight(element, totalHeight); }else{ w = DOMLH::getDesiredWidth(element, totalWidth); h = DOMLH::getDesiredHeight(element, totalHeight); } float elementMainSize = horizontal ? w : h; float elementCrossSize = horizontal ? h : w; if(element){ if(elementFlexing(element)){ // element->setSizeByParent(totalWidth, totalHeight); // set to minimal size on main axis if(horizontal){ element->setSizeByParent(elementMainSize + DOMLH::getMarginHorizontal(element), elementCrossSize + DOMLH::getMarginVertical(element)); element->setLayoutSize(elementMainSize, elementCrossSize, true); elementMainSize = element->getWidth() + DOMLH::getMarginHorizontal(element); }else { element->setSizeByParent(elementCrossSize + DOMLH::getMarginHorizontal(element), elementMainSize + DOMLH::getMarginVertical(element)); element->setLayoutSize(elementCrossSize, elementMainSize, true); elementMainSize = element->getHeight() + DOMLH::getMarginVertical(element); } //if element is flexible, add it to the current line and save the items flex basis if(element->hasAttribute("_flex")){ std::string flexval = element->getAttribute<std::string>("_flex"); if(flexval == "auto"){ lines.at(linecount).push_back(element); flexItems.at(linecount).push_back(element); flexItemsBasis.at(linecount).push_back(1); continue; } if(isFloat(ofTrim(flexval))){ float intflexval = ofToFloat(flexval); if(intflexval > 0){ lines.at(linecount).push_back(element); flexItems.at(linecount).push_back(element); flexItemsBasis.at(linecount).push_back(intflexval); continue; } } } // not flexible or no valid flex attribute, not flexing on main axis // add to new line if it does not fit and flex-wrap is on if((int)totalSpaceMainAxis.at(linecount) - (int)elementMainSize < 0){ FlexWrap _wrap = getFlexWrap(_parent); if(_wrap == FlexWrap::NOWRAP || i == 0){ //no new line }else{ //new line linecount++; lines.push_back(vector<DOM::Element*>()); flexItems.push_back(vector<DOM::Element*>()); flexItemsBasis.push_back(vector<float>()); totalSpaceMainAxis.push_back(mainAxisSize); } } lines.at(linecount).push_back(element); totalSpaceMainAxis.at(linecount) -= elementMainSize; }else { //set an absolute positioned element to its desired independent size if(DOMLH::elementAbsolutePositioned(element)){ element->setLayoutSize(w, h); } } } } //set main size of flex items if they are flexible for(unsigned int i = 0; i < flexItems.size(); i++){ int partscount = 0; for(int parts : flexItemsBasis.at(i)){ partscount += parts; } if(partscount > 0){ float partsize = totalSpaceMainAxis.at(i)/partscount; totalSpaceMainAxis.at(i) = 0; for(unsigned int j = 0; j < flexItems.at(i).size(); j++){ DOM::Element* element = flexItems.at(i).at(j); if(horizontal){ element->setSizeByParent(flexItemsBasis.at(i).at(j)*partsize, element->getSizeByParent().y); setLayoutWidthMinusMargin(element, flexItemsBasis.at(i).at(j)*partsize); }else{ element->setSizeByParent(element->getSizeByParent().x, flexItemsBasis.at(i).at(j)*partsize); setLayoutHeightMinusMargin(element, flexItemsBasis.at(i).at(j)*partsize); } } } } //set cross size of items if they stretch AlignItems alignItems = getAlignItems(_parent); vector<float> lineSizes; float totalSpaceCrossAxis = crossAxisSize; for(unsigned int i = 0; i < lines.size(); i++){ float lineSize = 0; for(auto e : lines.at(i)){ float elementCrossSize = horizontal ? e->getHeight()+DOMLH::getMarginVertical(e) : e->getWidth()+DOMLH::getMarginHorizontal(e); AlignSelf alignSelf = getAlignSelf(e); if(((alignSelf != AlignSelf::AUTO) && (alignSelf != AlignSelf::STRETCH)) || ((alignSelf == AlignSelf::AUTO) && (alignItems != AlignItems::STRETCH))){ if(elementCrossSize > lineSize){ lineSize = elementCrossSize; } } } totalSpaceCrossAxis -= lineSize; lineSizes.push_back(lineSize); } // count how many lines do not have a fixed size int zerolines = 0; for(int lineSize : lineSizes){ if(lineSize == 0){ zerolines++; } } // if there are lines without fixed height, take the remaining height of the parent // and share it between the lines without fixed height if(zerolines > 0){ for(unsigned int i = 0; i < lineSizes.size(); i++){ if(lineSizes[i] == 0){ lineSizes[i] = totalSpaceCrossAxis / zerolines; } } totalSpaceCrossAxis = 0; } // check if lines are not big enough to fit in all elements minimal size for(unsigned int i = 0; i < lines.size(); i++){ float lineSize = lineSizes.at(i); for(auto e : lines.at(i)){ float elementCrossSize = horizontal ? e->getHeight()+DOMLH::getMarginVertical(e) : e->getWidth()+DOMLH::getMarginHorizontal(e); if(elementCrossSize > lineSize){ lineSize = elementCrossSize; } } lineSizes.at(i) = lineSize; } float newCrossAxisSize = 0; for(int size : lineSizes){ newCrossAxisSize += size; } if(newCrossAxisSize > crossAxisSize){ totalSpaceCrossAxis = 0; // if(horizontal){ // setHeightInLayoutAddPadding_parent, newCrossAxisSize); // }else { // setWidthInLayoutAddPadding(_parent, newCrossAxisSize); // } } //take care of empty space on cross axis int spacingCrossAxisStart = 0; int spacingCrossAxisBetween = 0; if(lines.size() > 1){ if(totalSpaceCrossAxis > 0){ switch(getAlignContent(_parent)){ case AlignContent::CENTER: spacingCrossAxisStart = totalSpaceCrossAxis/2; break; case AlignContent::FLEX_END: spacingCrossAxisStart = totalSpaceCrossAxis; break; case AlignContent::SPACE_AROUND: spacingCrossAxisStart = totalSpaceCrossAxis/(lines.size()+1); spacingCrossAxisBetween = spacingCrossAxisStart; break; case AlignContent::SPACE_BETWEEN: spacingCrossAxisBetween = totalSpaceCrossAxis/(lines.size()-1); break; case AlignContent::STRETCH: spacingCrossAxisBetween = totalSpaceCrossAxis/lines.size(); break; default:break; } } }else{ if(lines.size()>0){ lineSizes.at(0) = max(lineSizes.at(0),crossAxisSize); } } totalWidth += paddingHorizontal; totalHeight += paddingVertical; float parentPaddingLeft = DOMLH::getPaddingLeft(_parent); float parentPaddingTop = DOMLH::getPaddingTop(_parent); float currentMainPos = 0; float currentCrossPos = spacingCrossAxisStart; currentCrossPos += horizontal ? parentPaddingTop : parentPaddingLeft; for(unsigned int i = 0; i < lines.size(); i++){ //take care of empty space on main axis int spacingMainAxisStart = horizontal ? parentPaddingLeft : parentPaddingTop; int spacingMainAxisBetween = 0; if(totalSpaceMainAxis.at(i) > 0){ switch(getJustifyContent(_parent)){ case JustifyContent::CENTER: spacingMainAxisStart += totalSpaceMainAxis.at(i)/2; break; case JustifyContent::FLEX_END: spacingMainAxisStart += totalSpaceMainAxis.at(i); break; case JustifyContent::SPACE_AROUND: spacingMainAxisStart += totalSpaceMainAxis.at(i)/(lines.at(i).size()+1); spacingMainAxisBetween = spacingMainAxisStart; break; case JustifyContent::SPACE_BETWEEN: spacingMainAxisBetween = totalSpaceMainAxis.at(i)/(lines.at(i).size()-1); break; default:break; } } currentMainPos = spacingMainAxisStart; for(unsigned int j = 0; j < lines.at(i).size(); j++){ // set cross size of item DOM::Element* element = lines.at(i).at(j); AlignSelf alignSelf = getAlignSelf(element); if(alignSelf == AlignSelf::STRETCH || ((alignSelf == AlignSelf::AUTO) && (alignItems == AlignItems::STRETCH))){ if(horizontal){ element->setSizeByParent(element->getSizeByParent().x, lineSizes.at(i)); setLayoutHeightMinusMargin(element, lineSizes.at(i)); }else{ element->setSizeByParent(lineSizes.at(i), element->getSizeByParent().y); setLayoutWidthMinusMargin(element, lineSizes.at(i)); } } //align item float elementMainPos = currentMainPos; float elementCrossPos = currentCrossPos; float elementMainSize = horizontal ? getWidthPlusMargin(element) : getHeightPlusMargin(element); float elementCrossSize = horizontal ? getHeightPlusMargin(element) : getWidthPlusMargin(element); //align item on cross axis AlignItems alignItem = alignItems; if(alignSelf != AlignSelf::AUTO){ switch(alignSelf){ case AlignSelf::CENTER: alignItem = AlignItems::CENTER; break; case AlignSelf::STRETCH: case AlignSelf::FLEX_START: alignItem = AlignItems::FLEX_START; break; case AlignSelf::FLEX_END: alignItem = AlignItems::FLEX_END; break; default: break; } } switch(alignItem){ case AlignItems::FLEX_END: elementCrossPos += lineSizes.at(i)-elementCrossSize; break; case AlignItems::CENTER: elementCrossPos += (lineSizes.at(i)-elementCrossSize)/2.; break; default: break; } //set final element position if(horizontal){ DOMLH::setPosition(element, ofPoint(elementMainPos, elementCrossPos)); }else{ DOMLH::setPosition(element, ofPoint(elementCrossPos, elementMainPos)); } totalWidth = max(totalWidth, element->getShape().getRight()+DOMLH::getMarginRight(element)+DOMLH::getPaddingRight(_parent)); totalHeight = max(totalHeight, element->getShape().getBottom()+DOMLH::getMarginBottom(element)+DOMLH::getPaddingBottom(_parent)); currentMainPos += elementMainSize + spacingMainAxisBetween; } currentCrossPos += lineSizes.at(i) + spacingCrossAxisBetween; } //make sure parent element contains all child elements on the main axis // maxX += DOMLH::getPaddingRight(_parent); // maxY += DOMLH::getPaddingBottom(_parent); // if(horizontal){ // _parent->setLayoutSize(max(maxX,_parent->getWidth()), max(maxY,_parent->getHeight())); // }else{ // _parent->setLayoutSize(max(maxX,_parent->getWidth()), max(maxY,_parent->getHeight())); // } // if(ofxGuiElement* el = dynamic_cast<ofxGuiElement*>(_parent)){ // cout << el->getName() << " total size end: " << totalWidth << " " << totalHeight << endl; // } _parent->setLayoutSize(totalWidth, totalHeight, false); _parent->setNeedsRedraw(); }