• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/flex/wrap_layout_algorithm.h"
17 
18 #include <algorithm>
19 
20 #include "base/geometry/axis.h"
21 #include "base/geometry/ng/size_t.h"
22 #include "base/log/ace_trace.h"
23 #include "base/memory/referenced.h"
24 #include "base/utils/utils.h"
25 #include "core/common/container.h"
26 #include "core/components/common/layout/constants.h"
27 #include "core/components/common/properties/alignment.h"
28 #include "core/components_ng/base/frame_node.h"
29 #include "core/components_ng/base/geometry_node.h"
30 #include "core/components_ng/layout/layout_property.h"
31 #include "core/components_ng/layout/layout_wrapper.h"
32 #include "core/components_ng/pattern/flex/flex_layout_property.h"
33 #include "core/components_ng/property/layout_constraint.h"
34 #include "core/components_ng/property/measure_property.h"
35 #include "core/components_ng/property/measure_utils.h"
36 
37 namespace OHOS::Ace::NG {
38 
Measure(LayoutWrapper * layoutWrapper)39 void WrapLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
40 {
41     CHECK_NULL_VOID(layoutWrapper);
42     auto children = layoutWrapper->GetAllChildrenWithBuild();
43     if (children.empty()) {
44         layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF());
45         return;
46     }
47     outOfLayoutChildren_.clear();
48     auto flexProp = AceType::DynamicCast<FlexLayoutProperty>(layoutWrapper->GetLayoutProperty());
49     CHECK_NULL_VOID(flexProp);
50     direction_ = flexProp->GetWrapDirection().value_or(WrapDirection::HORIZONTAL);
51     // alignment for alignContent, alignment when cross axis has extra space
52     alignment_ = flexProp->GetAlignment().value_or(WrapAlignment::START);
53     // alignment for justifyContent, main axis alignment
54     mainAlignment_ = flexProp->GetMainAlignment().value_or(WrapAlignment::START);
55     // alignment for alignItems, crossAxisAlignment
56     crossAlignment_ = flexProp->GetCrossAlignment().value_or(WrapAlignment::START);
57     isHorizontal_ = direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE;
58     isReverse_ = direction_ == WrapDirection::HORIZONTAL_REVERSE || direction_ == WrapDirection::VERTICAL_REVERSE;
59     PerformLayoutInitialize(flexProp);
60     totalMainLength_ = 0.0f;
61     totalCrossLength_ = 0.0f;
62     auto realMaxSize = GetLeftSize(0.0f, mainLengthLimit_, crossLengthLimit_);
63     auto childLayoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
64     padding_ = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
65     MinusPaddingToSize(padding_, realMaxSize);
66     mainLengthLimit_ = GetMainAxisLengthOfSize(realMaxSize);
67     crossLengthLimit_ = GetCrossAxisLengthOfSize(realMaxSize);
68     childLayoutConstraint.UpdateMaxSizeWithCheck(realMaxSize);
69     childLayoutConstraint.UpdateMinSizeWithCheck(SizeF(0.0f, 0.0f));
70     if (isDialogStretch_) {
71         HandleDialogStretch();
72         return;
73     }
74     auto spacing = static_cast<float>(spacing_.ConvertToPx());
75     auto contentSpace = static_cast<float>(contentSpace_.ConvertToPx());
76     float currentMainLength = 0.0f;
77     float currentCrossLength = 0.0f;
78     int32_t currentItemCount = 0;
79     float baselineDistance = 0.0f;
80     contentList_.clear();
81     std::list<RefPtr<LayoutWrapper>> currentMainAxisItemsList;
82     for (auto& item : children) {
83         if (item->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::GONE) {
84             continue;
85         }
86         item->Measure(childLayoutConstraint);
87         if (item->IsOutOfLayout()) {
88             outOfLayoutChildren_.emplace_back(item);
89             continue;
90         }
91         // can place current child at current row
92         if (GreatOrEqual(mainLengthLimit_, currentMainLength + GetItemMainAxisLength(item->GetGeometryNode()))) {
93             currentMainLength += GetItemMainAxisLength(item->GetGeometryNode());
94             currentMainLength += spacing;
95             currentCrossLength = std::max(currentCrossLength, GetItemCrossAxisLength(item->GetGeometryNode()));
96             if (crossAlignment_ == WrapAlignment::BASELINE) {
97                 baselineDistance = std::max(baselineDistance, item->GetBaselineDistance());
98             }
99             currentMainAxisItemsList.emplace_back(item);
100             currentItemCount += 1;
101         } else {
102             // after finish processing previous row, reverse align order if developer meant to
103             currentMainLength -= spacing;
104             // save info of current main axis items into struct
105             auto contentInfo =
106                 ContentInfo(currentMainLength, currentCrossLength, currentItemCount, currentMainAxisItemsList);
107             contentInfo.maxBaselineDistance = baselineDistance;
108             // measure items again if cross axis alignment is stretch
109             // and a item has main axis size differ than content height
110             StretchItemsInContent(layoutWrapper, contentInfo);
111             contentList_.emplace_back(contentInfo);
112             currentMainAxisItemsList.clear();
113             // place current item on a new main axis
114             totalMainLength_ = std::max(currentMainLength, totalMainLength_);
115             totalCrossLength_ += currentCrossLength + contentSpace;
116             currentMainLength = GetItemMainAxisLength(item->GetGeometryNode()) + spacing;
117             currentCrossLength = GetItemCrossAxisLength(item->GetGeometryNode());
118             if (crossAlignment_ == WrapAlignment::BASELINE) {
119                 baselineDistance = item->GetBaselineDistance();
120             }
121             currentMainAxisItemsList.emplace_back(item);
122             currentItemCount = 1;
123         }
124     }
125     if (currentItemCount != 0) {
126         // Add last content into list
127         currentMainLength -= spacing;
128         auto contentInfo =
129             ContentInfo(currentMainLength, currentCrossLength, currentItemCount, currentMainAxisItemsList);
130         contentInfo.maxBaselineDistance = baselineDistance;
131         StretchItemsInContent(layoutWrapper, contentInfo);
132         contentList_.emplace_back(contentInfo);
133         totalMainLength_ = std::max(currentMainLength, totalMainLength_);
134         totalCrossLength_ += currentCrossLength;
135     }
136     if (isHorizontal_) {
137         frameSize_ = SizeF(mainLengthLimit_, hasIdealHeight_ ? crossLengthLimit_ : totalCrossLength_);
138     } else {
139         frameSize_ = SizeF(hasIdealWidth_ ? crossLengthLimit_ : totalCrossLength_, mainLengthLimit_);
140     }
141     auto& calcLayoutConstraint = layoutWrapper->GetLayoutProperty()->GetCalcLayoutConstraint();
142     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && calcLayoutConstraint) {
143         OptionalSizeF finalSize(frameSize_.Width(), frameSize_.Height());
144         finalSize = UpdateOptionSizeByCalcLayoutConstraint(finalSize, calcLayoutConstraint,
145             layoutWrapper->GetLayoutProperty()->GetLayoutConstraint()->percentReference);
146         frameSize_.SetHeight(finalSize.Height().value_or(frameSize_.Height()));
147         frameSize_.SetWidth(finalSize.Width().value_or(frameSize_.Width()));
148     }
149     AddPaddingToSize(padding_, frameSize_);
150     layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize_);
151     frameOffset_ = layoutWrapper->GetGeometryNode()->GetFrameOffset();
152 }
153 
GetMainAxisLengthOfSize(const SizeF & size) const154 float WrapLayoutAlgorithm::GetMainAxisLengthOfSize(const SizeF& size) const
155 {
156     if (!isHorizontal_) {
157         return size.Height();
158     }
159     return size.Width();
160 }
161 
GetCrossAxisLengthOfSize(const SizeF & size) const162 float WrapLayoutAlgorithm::GetCrossAxisLengthOfSize(const SizeF& size) const
163 {
164     if (!isHorizontal_) {
165         return size.Width();
166     }
167     return size.Height();
168 }
169 
StretchItemsInContent(LayoutWrapper * layoutWrapper,const ContentInfo & content)170 void WrapLayoutAlgorithm::StretchItemsInContent(LayoutWrapper* layoutWrapper, const ContentInfo& content)
171 {
172     if (crossAlignment_ != WrapAlignment::STRETCH) {
173         return;
174     }
175     auto childLayoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
176     for (const auto& item : content.itemList) {
177         auto itemCrossAxisLength = GetItemCrossAxisLength(item->GetGeometryNode());
178         // if content cross axis size is larger than item cross axis size,
179         // measure items again with content cross axis size as ideal size
180         if (GreatNotEqual(content.crossLength, itemCrossAxisLength)) {
181             if (isHorizontal_) {
182                 childLayoutConstraint.selfIdealSize.SetHeight(content.crossLength);
183             } else {
184                 childLayoutConstraint.selfIdealSize.SetWidth(content.crossLength);
185             }
186             item->Measure(childLayoutConstraint);
187         }
188     }
189 }
190 
Layout(LayoutWrapper * layoutWrapper)191 void WrapLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
192 {
193     auto children = layoutWrapper->GetAllChildrenWithBuild();
194     if (children.empty()) {
195         return;
196     }
197     OffsetF startPosition;
198     OffsetF spaceBetweenContentsOnCrossAxis;
199     LayoutWholeWrap(startPosition, spaceBetweenContentsOnCrossAxis, layoutWrapper);
200     TraverseContent(startPosition, spaceBetweenContentsOnCrossAxis);
201     for (const auto& child : children) {
202         child->Layout();
203     }
204     contentList_.clear();
205 }
206 
HandleDialogStretch()207 void WrapLayoutAlgorithm::HandleDialogStretch()
208 {
209     // TODO: Not completed.
210 }
211 
PerformLayoutInitialize(const RefPtr<LayoutProperty> & layoutProp)212 void WrapLayoutAlgorithm::PerformLayoutInitialize(const RefPtr<LayoutProperty>& layoutProp)
213 {
214     CHECK_NULL_VOID(layoutProp);
215     auto constraint = layoutProp->GetLayoutConstraint();
216     // if flex width and height is not set, wrap is as large as children, no need to set alignment_.
217     if (constraint->selfIdealSize.Height() || constraint->selfIdealSize.Width()) {
218         auto widthValue = constraint->selfIdealSize.Width();
219         auto heightValue = constraint->selfIdealSize.Height();
220         hasIdealWidth_ = widthValue.has_value();
221         hasIdealHeight_ = heightValue.has_value();
222         if (isHorizontal_) {
223             mainLengthLimit_ = hasIdealWidth_ ? widthValue.value() : constraint->maxSize.Width();
224             crossLengthLimit_ = hasIdealHeight_ ? heightValue.value() : constraint->maxSize.Height();
225         } else {
226             mainLengthLimit_ = hasIdealHeight_ ? heightValue.value() : constraint->maxSize.Height();
227             crossLengthLimit_ = hasIdealWidth_ ? widthValue.value() : constraint->maxSize.Width();
228         }
229         return;
230     }
231     if (isHorizontal_) {
232         mainLengthLimit_ = constraint->maxSize.Width();
233         crossLengthLimit_ = constraint->maxSize.Height();
234     } else {
235         mainLengthLimit_ = constraint->maxSize.Height();
236         crossLengthLimit_ = constraint->maxSize.Width();
237     }
238 }
239 
GetLeftSize(float crossLength,float mainLeftLength,float crossLeftLength)240 SizeF WrapLayoutAlgorithm::GetLeftSize(float crossLength, float mainLeftLength, float crossLeftLength)
241 {
242     if (isHorizontal_) {
243         return SizeF(mainLeftLength, crossLeftLength - crossLength);
244     }
245     return SizeF(crossLeftLength - crossLength, mainLeftLength);
246 }
247 
GetItemMainAxisLength(const RefPtr<GeometryNode> & item) const248 float WrapLayoutAlgorithm::GetItemMainAxisLength(const RefPtr<GeometryNode>& item) const
249 {
250     return isHorizontal_ ? item->GetMarginFrameSize().Width() : item->GetMarginFrameSize().Height();
251 }
252 
GetItemCrossAxisLength(const RefPtr<GeometryNode> & item) const253 float WrapLayoutAlgorithm::GetItemCrossAxisLength(const RefPtr<GeometryNode>& item) const
254 {
255     return !isHorizontal_ ? item->GetMarginFrameSize().Width() : item->GetMarginFrameSize().Height();
256 }
257 
AddPaddingToStartPosition(OffsetF & startPosition) const258 void WrapLayoutAlgorithm::AddPaddingToStartPosition(OffsetF& startPosition) const
259 {
260     switch (direction_) {
261         // horizontal or vertical will start from top left
262         case WrapDirection::HORIZONTAL:
263         case WrapDirection::VERTICAL:
264             startPosition.AddX(padding_.left.value_or(0.0f));
265             startPosition.AddY(padding_.top.value_or(0.0f));
266             break;
267         case WrapDirection::HORIZONTAL_REVERSE:
268             startPosition.AddX(-padding_.right.value_or(0.0f));
269             startPosition.AddY(padding_.top.value_or(0.0f));
270             break;
271         case WrapDirection::VERTICAL_REVERSE:
272             startPosition.AddX(padding_.left.value_or(0.0f));
273             startPosition.AddY(-padding_.bottom.value_or(0.0f));
274             break;
275         default:
276             LOGW("Unknown direction");
277     }
278 }
279 
AddExtraSpaceToStartPosition(OffsetF & startPosition,float extraSpace,bool onMainAxis) const280 void WrapLayoutAlgorithm::AddExtraSpaceToStartPosition(OffsetF& startPosition, float extraSpace, bool onMainAxis) const
281 {
282     if (isReverse_) {
283         extraSpace = -extraSpace;
284     }
285     if (onMainAxis) {
286         if (isHorizontal_) {
287             startPosition.AddX(extraSpace);
288         } else {
289             startPosition.AddY(extraSpace);
290         }
291         return;
292     }
293     if (isHorizontal_) {
294         startPosition.AddY(extraSpace);
295         return;
296     }
297     startPosition.AddX(extraSpace);
298 }
299 
LayoutWholeWrap(OffsetF & startPosition,OffsetF & spaceBetweenContentsOnCrossAxis,LayoutWrapper * layoutWrapper)300 void WrapLayoutAlgorithm::LayoutWholeWrap(
301     OffsetF& startPosition, OffsetF& spaceBetweenContentsOnCrossAxis, LayoutWrapper* layoutWrapper)
302 {
303     auto contentNum = static_cast<int32_t>(contentList_.size());
304     if (contentNum == 0) {
305         return;
306     }
307 
308     const auto& layoutProp = layoutWrapper->GetLayoutProperty();
309     CHECK_NULL_VOID(layoutProp);
310     AddPaddingToStartPosition(startPosition);
311     if (isReverse_) {
312         AddExtraSpaceToStartPosition(startPosition, isHorizontal_ ? -frameSize_.Width() : -frameSize_.Height(), true);
313     }
314     // if cross axis size is not set, cross axis size is as large as children cross axis size sum
315     // no need to set alignment_.
316     if ((!isHorizontal_ && hasIdealWidth_ && crossLengthLimit_ <= totalCrossLength_) ||
317         (!isHorizontal_ && !hasIdealWidth_)) {
318         return;
319     }
320     if ((isHorizontal_ && hasIdealHeight_ && crossLengthLimit_ <= totalCrossLength_) ||
321         (isHorizontal_ && !hasIdealHeight_)) {
322         return;
323     }
324 
325     auto crossAxisRemainSpace = crossLengthLimit_ - totalCrossLength_;
326 
327     if (isReverse_) {
328         crossAxisRemainSpace = -crossAxisRemainSpace;
329     }
330     // switch align content enum, alignment when extra space exists in container extra spaces
331 
332     switch (alignment_) {
333         case WrapAlignment::START:
334             break;
335         // for reverse cases, start position will not include "first" item's main axis size
336         case WrapAlignment::END: {
337             AddExtraSpaceToStartPosition(startPosition, crossAxisRemainSpace, false);
338             break;
339         }
340         case WrapAlignment::CENTER: {
341             // divided the space by two
342             crossAxisRemainSpace /= 2.0f;
343             AddExtraSpaceToStartPosition(startPosition, crossAxisRemainSpace, false);
344             break;
345         }
346         case WrapAlignment::SPACE_BETWEEN: {
347             // space between will not affect start position, update space between only
348             float crossSpace =
349                 contentNum > 1 ? (crossLengthLimit_ - totalCrossLength_) / static_cast<float>(contentNum - 1) : 0.0f;
350             spaceBetweenContentsOnCrossAxis = isHorizontal_ ? OffsetF(0.0f, crossSpace) : OffsetF(crossSpace, 0.0f);
351             break;
352         }
353         case WrapAlignment::SPACE_EVENLY: {
354             float crossSpace = crossAxisRemainSpace / static_cast<float>(contentNum + 1);
355             AddExtraSpaceToStartPosition(startPosition, crossSpace, false);
356             spaceBetweenContentsOnCrossAxis =
357                 isHorizontal_ ? OffsetF(0.0f, std::abs(crossSpace)) : OffsetF(std::abs(crossSpace), 0.0f);
358             break;
359         }
360         case WrapAlignment::SPACE_AROUND: {
361             float crossSpace = crossAxisRemainSpace / static_cast<float>(contentNum);
362             AddExtraSpaceToStartPosition(startPosition, crossSpace / 2.0f, false);
363             spaceBetweenContentsOnCrossAxis =
364                 isHorizontal_ ? OffsetF(0.0f, std::abs(crossSpace)) : OffsetF(std::abs(crossSpace), 0.0);
365             break;
366         }
367         default: {
368             break;
369         }
370     }
371 }
372 
GetMainAxisRemainSpace(float totalMainLength) const373 SizeF WrapLayoutAlgorithm::GetMainAxisRemainSpace(float totalMainLength) const
374 {
375     if (isHorizontal_) {
376         return SizeF(mainLengthLimit_ - totalMainLength, 0.0f);
377     }
378     return SizeF(0.0f, mainLengthLimit_ - totalMainLength);
379 }
380 
GetCrossAxisRemainSpace(float totalCrossLength) const381 SizeF WrapLayoutAlgorithm::GetCrossAxisRemainSpace(float totalCrossLength) const
382 {
383     if (isHorizontal_) {
384         return SizeF(0.0f, crossLengthLimit_ - totalCrossLength);
385     }
386     return SizeF(crossLengthLimit_ - totalCrossLength, 0.0f);
387 }
388 
GetMainAxisOffset(const OffsetF & offset) const389 float WrapLayoutAlgorithm::GetMainAxisOffset(const OffsetF& offset) const
390 {
391     if (isHorizontal_) {
392         return offset.GetX();
393     }
394     return offset.GetY();
395 }
396 
GetCrossAxisOffset(const OffsetF & offset) const397 float WrapLayoutAlgorithm::GetCrossAxisOffset(const OffsetF& offset) const
398 {
399     if (isHorizontal_) {
400         return offset.GetY();
401     }
402     return offset.GetX();
403 }
404 
TraverseContent(const OffsetF & startPosition,const OffsetF & spaceBetweenContentsOnCrossAxis)405 void WrapLayoutAlgorithm::TraverseContent(const OffsetF& startPosition, const OffsetF& spaceBetweenContentsOnCrossAxis)
406 {
407     // determine the content start position by main axis
408     OffsetF contentPosition(startPosition.GetX(), startPosition.GetY());
409     auto contentSpace = static_cast<float>(contentSpace_.ConvertToPx());
410     auto spaceBetween = isHorizontal_ ? spaceBetweenContentsOnCrossAxis.GetY() : spaceBetweenContentsOnCrossAxis.GetX();
411     for (const auto& content : contentList_) {
412         LayoutContent(content, contentPosition);
413         if (isHorizontal_) {
414             contentPosition.AddY(content.crossLength + contentSpace + spaceBetween);
415         } else {
416             contentPosition.AddX(content.crossLength + contentSpace + spaceBetween);
417         }
418     }
419 }
420 
GetItemMainOffset(float mainSpace) const421 OffsetF WrapLayoutAlgorithm::GetItemMainOffset(float mainSpace) const
422 {
423     // calculate the offset of each item in content
424     if (isHorizontal_) {
425         return OffsetF(mainSpace, 0.0);
426     }
427     return OffsetF(0.0, mainSpace);
428 }
429 
CalcItemCrossAxisOffset(const ContentInfo & content,const OffsetF & contentOffset,const RefPtr<GeometryNode> & node)430 float WrapLayoutAlgorithm::CalcItemCrossAxisOffset(
431     const ContentInfo& content, const OffsetF& contentOffset, const RefPtr<GeometryNode>& node)
432 {
433     switch (crossAlignment_) {
434         case WrapAlignment::START:
435         // stretch has been processed in measure, result is the same as start
436         case WrapAlignment::STRETCH: {
437             if (isHorizontal_) {
438                 return contentOffset.GetY();
439             }
440             return contentOffset.GetX();
441         }
442         case WrapAlignment::END: {
443             auto itemFrameSize = node->GetMarginFrameSize();
444             if (isHorizontal_) {
445                 return contentOffset.GetY() + content.crossLength - itemFrameSize.Height();
446             }
447             return contentOffset.GetX() + content.crossLength - itemFrameSize.Width();
448         }
449         case WrapAlignment::CENTER: {
450             // divide the space by two
451             auto itemFrameSize = node->GetMarginFrameSize();
452             if (isHorizontal_) {
453                 return contentOffset.GetY() + (content.crossLength - itemFrameSize.Height()) / 2.0f;
454             }
455             return contentOffset.GetX() + (content.crossLength - itemFrameSize.Width()) / 2.0f;
456         }
457         case WrapAlignment::BASELINE: {
458             // TODO: Complete baseline
459             break;
460         }
461         default: {
462             if (isHorizontal_) {
463                 return contentOffset.GetY();
464             }
465             return contentOffset.GetX();
466 
467             break;
468         }
469     }
470     if (isHorizontal_) {
471         return contentOffset.GetY();
472     }
473     return contentOffset.GetX();
474 }
475 
CalcItemMainAxisStartAndSpaceBetween(OffsetF & startPosition,OffsetF & spaceBetweenItemsOnMainAxis,const ContentInfo & content)476 void WrapLayoutAlgorithm::CalcItemMainAxisStartAndSpaceBetween(
477     OffsetF& startPosition, OffsetF& spaceBetweenItemsOnMainAxis, const ContentInfo& content)
478 {
479     // switch align content enum, alignment when extra space exists in container extra spaces
480     float spaceLeftOnMainAxis = mainLengthLimit_ - content.mainLength;
481     switch (mainAlignment_) {
482         case WrapAlignment::START:
483             break;
484         case WrapAlignment::END: {
485             AddExtraSpaceToStartPosition(startPosition, spaceLeftOnMainAxis, true);
486             break;
487         }
488         case WrapAlignment::CENTER: {
489             AddExtraSpaceToStartPosition(startPosition, spaceLeftOnMainAxis / 2.0f, true);
490             break;
491         }
492         case WrapAlignment::SPACE_BETWEEN: {
493             float mainSpace = content.count > 1 ? spaceLeftOnMainAxis / static_cast<float>(content.count - 1) : 0.0f;
494             spaceBetweenItemsOnMainAxis = isHorizontal_ ? OffsetF(mainSpace, 0.0f) : OffsetF(0.0f, mainSpace);
495             break;
496         }
497         case WrapAlignment::SPACE_EVENLY: {
498             float mainSpace = spaceLeftOnMainAxis / static_cast<float>(content.count + 1);
499             AddExtraSpaceToStartPosition(startPosition, mainSpace, true);
500             spaceBetweenItemsOnMainAxis = isHorizontal_ ? OffsetF(mainSpace, 0.0f) : OffsetF(0.0f, mainSpace);
501             break;
502         }
503         case WrapAlignment::SPACE_AROUND: {
504             float mainSpace = spaceLeftOnMainAxis / static_cast<float>(content.count);
505             AddExtraSpaceToStartPosition(startPosition, mainSpace / 2.0f, true);
506             spaceBetweenItemsOnMainAxis = isHorizontal_ ? OffsetF(mainSpace, 0.0f) : OffsetF(0.0f, mainSpace);
507             break;
508         }
509         default: {
510             break;
511         }
512     }
513 }
514 
LayoutContent(const ContentInfo & content,const OffsetF & position)515 void WrapLayoutAlgorithm::LayoutContent(const ContentInfo& content, const OffsetF& position)
516 {
517     int32_t itemNum = content.count;
518     if (itemNum == 0) {
519         return;
520     }
521     OffsetF contentStartPosition(position.GetX(), position.GetY());
522     OffsetF spaceBetweenItemsOnMainAxis;
523     CalcItemMainAxisStartAndSpaceBetween(contentStartPosition, spaceBetweenItemsOnMainAxis, content);
524 
525     FlexItemProperties flexItemProperties;
526     GetFlexItemProperties(content, flexItemProperties);
527     float remainSpace = mainLengthLimit_ - currentMainLength_;
528     for (const auto& itemWrapper : content.itemList) {
529         auto item = itemWrapper->GetGeometryNode();
530         if (GreatNotEqual(remainSpace, 0.0f)) {
531             CalcFlexGrowLayout(itemWrapper, flexItemProperties, remainSpace);
532         }
533         // calc start position and between space
534         auto itemMainAxisOffset = isHorizontal_ ? contentStartPosition.GetX() : contentStartPosition.GetY();
535         if (isReverse_) {
536             itemMainAxisOffset -= GetItemMainAxisLength(item);
537         }
538         auto itemCrossAxisOffset = CalcItemCrossAxisOffset(content, contentStartPosition, item);
539         OffsetF offset;
540         float contentMainAxisSpan = 0.0f;
541         if (isHorizontal_) {
542             offset = OffsetF(itemMainAxisOffset, itemCrossAxisOffset);
543             contentMainAxisSpan = item->GetMarginFrameSize().Width() + static_cast<float>(spacing_.ConvertToPx()) +
544                                   spaceBetweenItemsOnMainAxis.GetX();
545             contentStartPosition.AddX(isReverse_ ? -contentMainAxisSpan : contentMainAxisSpan);
546         } else {
547             offset = OffsetF(itemCrossAxisOffset, itemMainAxisOffset);
548             contentMainAxisSpan = item->GetMarginFrameSize().Height() + static_cast<float>(spacing_.ConvertToPx()) +
549                                   spaceBetweenItemsOnMainAxis.GetY();
550             contentStartPosition.AddY(isReverse_ ? -contentMainAxisSpan : contentMainAxisSpan);
551         }
552         itemWrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
553     }
554 }
555 
GetFlexItemProperties(const ContentInfo & content,FlexItemProperties & flexItemProperties)556 void WrapLayoutAlgorithm::GetFlexItemProperties(const ContentInfo& content, FlexItemProperties& flexItemProperties)
557 {
558     auto spacing = static_cast<float>(spacing_.ConvertToPx());
559     currentMainLength_ = 0.0f;
560     for (const auto& itemWrapper : content.itemList) {
561         if (!itemWrapper) {
562             continue;
563         }
564         currentMainLength_ += GetItemMainAxisLength(itemWrapper->GetGeometryNode()) + spacing;
565         auto layoutProperty = itemWrapper->GetLayoutProperty();
566         if (!layoutProperty) {
567             continue;
568         }
569         const auto& flexItemProperty = layoutProperty->GetFlexItemProperty();
570         if (!flexItemProperty) {
571             continue;
572         }
573         auto flexGrow = flexItemProperty->GetFlexGrow().value_or(0.0f);
574         if (GreatNotEqual(flexGrow, 0.0f)) {
575             flexItemProperties.totalGrow += flexGrow;
576         }
577     }
578 }
579 
CalcFlexGrowLayout(const RefPtr<LayoutWrapper> & itemWrapper,const FlexItemProperties & flexItemProperties,float remainSpace)580 void WrapLayoutAlgorithm::CalcFlexGrowLayout(
581     const RefPtr<LayoutWrapper>& itemWrapper, const FlexItemProperties& flexItemProperties, float remainSpace)
582 {
583     CHECK_NULL_VOID(itemWrapper);
584     auto layoutProperty = itemWrapper->GetLayoutProperty();
585     CHECK_NULL_VOID(layoutProperty);
586     auto& flexItemProperty = layoutProperty->GetFlexItemProperty();
587     CHECK_NULL_VOID(flexItemProperty);
588     auto layoutConstraint = layoutProperty->GetLayoutConstraint();
589     if (!layoutConstraint.has_value()) {
590         return;
591     }
592 
593     auto layoutConstraintValue = layoutConstraint.value();
594     float itemFlex = flexItemProperty->GetFlexGrow().value_or(0.0f);
595     if (GreatNotEqual(itemFlex, 0.0f) && GreatNotEqual(remainSpace, 0.0f) &&
596         GreatNotEqual(flexItemProperties.totalGrow, 0.0f)) {
597         float flexSize = itemFlex * remainSpace / flexItemProperties.totalGrow;
598         flexSize += GetItemMainAxisLength(itemWrapper->GetGeometryNode());
599         OptionalSizeF& selfIdealSize = layoutConstraintValue.selfIdealSize;
600         if (direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE) {
601             selfIdealSize.SetWidth(flexSize);
602         } else {
603             selfIdealSize.SetHeight(flexSize);
604         }
605         itemWrapper->Measure(layoutConstraintValue);
606     }
607 }
608 
609 } // namespace OHOS::Ace::NG
610