• 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 "core/components_ng/pattern/flex/flex_layout_property.h"
19 
20 namespace OHOS::Ace::NG {
21 
22 /**
23  * Determine whether to start the layout from the upper left corner
24  */
25 
IsStartTopLeft(WrapDirection direction,TextDirection textDirection)26 bool IsStartTopLeft(WrapDirection direction, TextDirection textDirection)
27 {
28     switch (direction) {
29         case WrapDirection::HORIZONTAL:
30             return textDirection == TextDirection::LTR;
31         case WrapDirection::HORIZONTAL_REVERSE:
32             return textDirection == TextDirection::RTL;
33         case WrapDirection::VERTICAL:
34             return true;
35         case WrapDirection::VERTICAL_REVERSE:
36             return false;
37         default:
38             return true;
39     }
40 }
41 
IsColumnReverse(WrapDirection direction)42 bool IsColumnReverse(WrapDirection direction)
43 {
44     switch (direction) {
45         case WrapDirection::VERTICAL:
46             return false;
47         case WrapDirection::VERTICAL_REVERSE:
48             return true;
49         default:
50             return false;
51     }
52 }
53 
UpdatePercentSensitive(LayoutWrapper * layoutWrapper)54 void WrapLayoutAlgorithm::UpdatePercentSensitive(LayoutWrapper *layoutWrapper)
55 {
56     CHECK_NULL_VOID(layoutWrapper && layoutWrapper->GetHostTag() == V2::FLEX_ETS_TAG);
57     auto layoutAlgorithmWrapper = layoutWrapper->GetLayoutAlgorithm();
58     CHECK_NULL_VOID(layoutAlgorithmWrapper);
59     layoutAlgorithmWrapper->SetPercentWidth(true);
60     layoutAlgorithmWrapper->SetPercentHeight(true);
61 }
62 
Measure(LayoutWrapper * layoutWrapper)63 void WrapLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
64 {
65     CHECK_NULL_VOID(layoutWrapper);
66     auto children = layoutWrapper->GetAllChildrenWithBuild();
67     if (children.empty()) {
68         layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF());
69         return;
70     }
71     outOfLayoutChildren_.clear();
72     auto flexProp = AceType::DynamicCast<FlexLayoutProperty>(layoutWrapper->GetLayoutProperty());
73     CHECK_NULL_VOID(flexProp);
74     UpdatePercentSensitive(layoutWrapper);
75     direction_ = flexProp->GetWrapDirection().value_or(WrapDirection::HORIZONTAL);
76     // alignment for alignContent, alignment when cross axis has extra space
77     alignment_ = flexProp->GetAlignment().value_or(WrapAlignment::START);
78     // alignment for justifyContent, main axis alignment
79     mainAlignment_ = flexProp->GetMainAlignment().value_or(WrapAlignment::START);
80     // alignment for alignItems, crossAxisAlignment
81     crossAlignment_ = flexProp->GetCrossAlignment().value_or(WrapAlignment::START);
82     textDir_ = flexProp->GetLayoutDirection();
83     if (textDir_ == TextDirection::AUTO) {
84         textDir_ = AceApplicationInfo::GetInstance().IsRightToLeft() ? TextDirection::RTL : TextDirection::LTR;
85     }
86     isHorizontal_ = direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE;
87     isReverse_ = !IsStartTopLeft(direction_, textDir_);
88     isRightDirection_ = textDir_ == TextDirection::RTL;
89     isColumnReverse_ = IsColumnReverse(direction_);
90     PerformLayoutInitialize(flexProp);
91     totalMainLength_ = 0.0f;
92     totalCrossLength_ = 0.0f;
93     auto realMaxSize = GetLeftSize(0.0f, mainLengthLimit_, crossLengthLimit_);
94     auto childLayoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
95     padding_ = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
96     MinusPaddingToSize(padding_, realMaxSize);
97     mainLengthLimit_ = GetMainAxisLengthOfSize(realMaxSize);
98     crossLengthLimit_ = GetCrossAxisLengthOfSize(realMaxSize);
99     childLayoutConstraint.UpdateMaxSizeWithCheck(realMaxSize);
100     childLayoutConstraint.UpdateMinSizeWithCheck(SizeF(0.0f, 0.0f));
101     if (isDialogStretch_) {
102         HandleDialogStretch();
103         return;
104     }
105     spacing_ = flexProp->GetSpaceValue({});
106     contentSpace_ = flexProp->GetCrossSpaceValue({});
107     auto spacing = static_cast<float>(spacing_.ConvertToPx());
108     auto contentSpace = static_cast<float>(contentSpace_.ConvertToPx());
109     float currentMainLength = 0.0f;
110     float currentCrossLength = 0.0f;
111     int32_t currentItemCount = 0;
112     float baselineDistance = 0.0f;
113     contentList_.clear();
114     std::list<RefPtr<LayoutWrapper>> currentMainAxisItemsList;
115     for (auto& item : children) {
116         if (item->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::GONE) {
117             continue;
118         }
119         item->Measure(childLayoutConstraint);
120         if (item->IsOutOfLayout()) {
121             outOfLayoutChildren_.emplace_back(item);
122             continue;
123         }
124         // can place current child at current row
125         if (GreatOrEqual(mainLengthLimit_, currentMainLength + GetItemMainAxisLength(item->GetGeometryNode()))) {
126             currentMainLength += GetItemMainAxisLength(item->GetGeometryNode());
127             currentMainLength += spacing;
128             currentCrossLength = std::max(currentCrossLength, GetItemCrossAxisLength(item->GetGeometryNode()));
129             if (crossAlignment_ == WrapAlignment::BASELINE) {
130                 baselineDistance = std::max(baselineDistance, item->GetBaselineDistance());
131             }
132             currentMainAxisItemsList.emplace_back(item);
133             currentItemCount += 1;
134         } else {
135             // after finish processing previous row, reverse align order if developer meant to
136             currentMainLength -= spacing;
137             // save info of current main axis items into struct
138             auto contentInfo =
139                 ContentInfo(currentMainLength, currentCrossLength, currentItemCount, currentMainAxisItemsList);
140             contentInfo.maxBaselineDistance = baselineDistance;
141             // measure items again if cross axis alignment is stretch
142             // and a item has main axis size differ than content height
143             StretchItemsInContent(layoutWrapper, contentInfo);
144             contentList_.emplace_back(contentInfo);
145             currentMainAxisItemsList.clear();
146             // place current item on a new main axis
147             totalMainLength_ = std::max(currentMainLength, totalMainLength_);
148             totalCrossLength_ += currentCrossLength + contentSpace;
149             currentMainLength = GetItemMainAxisLength(item->GetGeometryNode()) + spacing;
150             currentCrossLength = GetItemCrossAxisLength(item->GetGeometryNode());
151             if (crossAlignment_ == WrapAlignment::BASELINE) {
152                 baselineDistance = item->GetBaselineDistance();
153             }
154             currentMainAxisItemsList.emplace_back(item);
155             currentItemCount = 1;
156         }
157     }
158     if (currentItemCount != 0) {
159         // Add last content into list
160         currentMainLength -= spacing;
161         auto contentInfo =
162             ContentInfo(currentMainLength, currentCrossLength, currentItemCount, currentMainAxisItemsList);
163         contentInfo.maxBaselineDistance = baselineDistance;
164         StretchItemsInContent(layoutWrapper, contentInfo);
165         contentList_.emplace_back(contentInfo);
166         totalMainLength_ = std::max(currentMainLength, totalMainLength_);
167         totalCrossLength_ += currentCrossLength;
168     }
169     if (isHorizontal_) {
170         frameSize_ = SizeF(mainLengthLimit_, hasIdealHeight_ ? crossLengthLimit_ : totalCrossLength_);
171     } else {
172         frameSize_ = SizeF(hasIdealWidth_ ? crossLengthLimit_ : totalCrossLength_, mainLengthLimit_);
173     }
174     auto& calcLayoutConstraint = layoutWrapper->GetLayoutProperty()->GetCalcLayoutConstraint();
175     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) && calcLayoutConstraint) {
176         OptionalSizeF finalSize(frameSize_.Width(), frameSize_.Height());
177         finalSize = UpdateOptionSizeByCalcLayoutConstraint(finalSize, calcLayoutConstraint,
178             layoutWrapper->GetLayoutProperty()->GetLayoutConstraint()->percentReference);
179         frameSize_.SetHeight(finalSize.Height().value_or(frameSize_.Height()));
180         frameSize_.SetWidth(finalSize.Width().value_or(frameSize_.Width()));
181     }
182     AddPaddingToSize(padding_, frameSize_);
183     layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize_);
184     frameOffset_ = layoutWrapper->GetGeometryNode()->GetFrameOffset();
185 }
186 
GetMainAxisLengthOfSize(const SizeF & size) const187 float WrapLayoutAlgorithm::GetMainAxisLengthOfSize(const SizeF& size) const
188 {
189     if (!isHorizontal_) {
190         return size.Height();
191     }
192     return size.Width();
193 }
194 
GetCrossAxisLengthOfSize(const SizeF & size) const195 float WrapLayoutAlgorithm::GetCrossAxisLengthOfSize(const SizeF& size) const
196 {
197     if (!isHorizontal_) {
198         return size.Width();
199     }
200     return size.Height();
201 }
202 
StretchItemsInContent(LayoutWrapper * layoutWrapper,const ContentInfo & content)203 void WrapLayoutAlgorithm::StretchItemsInContent(LayoutWrapper* layoutWrapper, const ContentInfo& content)
204 {
205     if (crossAlignment_ != WrapAlignment::STRETCH) {
206         return;
207     }
208     auto childLayoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
209     for (const auto& item : content.itemList) {
210         auto itemCrossAxisLength = GetItemCrossAxisLength(item->GetGeometryNode());
211         // if content cross axis size is larger than item cross axis size,
212         // measure items again with content cross axis size as ideal size
213         if (GreatNotEqual(content.crossLength, itemCrossAxisLength)) {
214             if (isHorizontal_) {
215                 childLayoutConstraint.selfIdealSize.SetHeight(content.crossLength);
216             } else {
217                 childLayoutConstraint.selfIdealSize.SetWidth(content.crossLength);
218             }
219             item->Measure(childLayoutConstraint);
220         }
221     }
222 }
223 
Layout(LayoutWrapper * layoutWrapper)224 void WrapLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
225 {
226     auto children = layoutWrapper->GetAllChildrenWithBuild();
227     if (children.empty()) {
228         return;
229     }
230     OffsetF startPosition;
231     OffsetF spaceBetweenContentsOnCrossAxis;
232     if (isHorizontal_) {
233         LayoutWholeWrap(startPosition, spaceBetweenContentsOnCrossAxis, layoutWrapper);
234         TraverseContent(startPosition, spaceBetweenContentsOnCrossAxis);
235     } else {
236         LayoutWholeColumnWrap(startPosition, spaceBetweenContentsOnCrossAxis, layoutWrapper);
237         TraverseColumnContent(startPosition, spaceBetweenContentsOnCrossAxis);
238     }
239 
240     for (const auto& child : children) {
241         child->Layout();
242     }
243     contentList_.clear();
244 }
245 
HandleDialogStretch()246 void WrapLayoutAlgorithm::HandleDialogStretch()
247 {
248 }
249 
PerformLayoutInitialize(const RefPtr<LayoutProperty> & layoutProp)250 void WrapLayoutAlgorithm::PerformLayoutInitialize(const RefPtr<LayoutProperty>& layoutProp)
251 {
252     CHECK_NULL_VOID(layoutProp);
253     auto constraint = layoutProp->GetLayoutConstraint();
254     // if flex width and height is not set, wrap is as large as children, no need to set alignment_.
255     if (constraint->selfIdealSize.Height() || constraint->selfIdealSize.Width()) {
256         auto widthValue = constraint->selfIdealSize.Width();
257         auto heightValue = constraint->selfIdealSize.Height();
258         hasIdealWidth_ = widthValue.has_value();
259         hasIdealHeight_ = heightValue.has_value();
260         if (isHorizontal_) {
261             mainLengthLimit_ = hasIdealWidth_ ? widthValue.value() : constraint->maxSize.Width();
262             crossLengthLimit_ = hasIdealHeight_ ? heightValue.value() : constraint->maxSize.Height();
263         } else {
264             mainLengthLimit_ = hasIdealHeight_ ? heightValue.value() : constraint->maxSize.Height();
265             crossLengthLimit_ = hasIdealWidth_ ? widthValue.value() : constraint->maxSize.Width();
266         }
267         return;
268     }
269     if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
270         if (isHorizontal_) {
271             mainLengthLimit_ = std::min(constraint->maxSize.Width(), constraint->percentReference.Width());
272             crossLengthLimit_ = std::min(constraint->maxSize.Height(), constraint->percentReference.Height());
273         } else {
274             mainLengthLimit_ = std::min(constraint->maxSize.Height(), constraint->percentReference.Height());
275             crossLengthLimit_ = std::min(constraint->maxSize.Width(), constraint->percentReference.Width());
276         }
277     } else {
278         if (isHorizontal_) {
279             mainLengthLimit_ = constraint->maxSize.Width();
280             crossLengthLimit_ = constraint->maxSize.Height();
281         } else {
282             mainLengthLimit_ = constraint->maxSize.Height();
283             crossLengthLimit_ = constraint->maxSize.Width();
284         }
285     }
286 }
287 
GetLeftSize(float crossLength,float mainLeftLength,float crossLeftLength)288 SizeF WrapLayoutAlgorithm::GetLeftSize(float crossLength, float mainLeftLength, float crossLeftLength)
289 {
290     if (isHorizontal_) {
291         return SizeF(mainLeftLength, crossLeftLength - crossLength);
292     }
293     return SizeF(crossLeftLength - crossLength, mainLeftLength);
294 }
295 
GetItemMainAxisLength(const RefPtr<GeometryNode> & item) const296 float WrapLayoutAlgorithm::GetItemMainAxisLength(const RefPtr<GeometryNode>& item) const
297 {
298     return isHorizontal_ ? item->GetMarginFrameSize().Width() : item->GetMarginFrameSize().Height();
299 }
300 
GetItemCrossAxisLength(const RefPtr<GeometryNode> & item) const301 float WrapLayoutAlgorithm::GetItemCrossAxisLength(const RefPtr<GeometryNode>& item) const
302 {
303     return !isHorizontal_ ? item->GetMarginFrameSize().Width() : item->GetMarginFrameSize().Height();
304 }
305 
AddPaddingToStartPosition(OffsetF & startPosition) const306 void WrapLayoutAlgorithm::AddPaddingToStartPosition(OffsetF& startPosition) const
307 {
308     switch (direction_) {
309         // horizontal or vertical will start from top left
310         case WrapDirection::HORIZONTAL:
311         case WrapDirection::VERTICAL:
312             if (textDir_ == TextDirection::RTL) {
313                 startPosition.AddX(-padding_.right.value_or(0.0f));
314             } else {
315                 startPosition.AddX(padding_.left.value_or(0.0f));
316             }
317             startPosition.AddY(padding_.top.value_or(0.0f));
318             break;
319         case WrapDirection::HORIZONTAL_REVERSE:
320             if (textDir_ == TextDirection::RTL) {
321                 startPosition.AddX(padding_.left.value_or(0.0f));
322             } else {
323                 startPosition.AddX(-padding_.right.value_or(0.0f));
324             }
325             startPosition.AddY(padding_.top.value_or(0.0f));
326             break;
327         case WrapDirection::VERTICAL_REVERSE:
328             startPosition.AddX(padding_.left.value_or(0.0f));
329             startPosition.AddY(-padding_.bottom.value_or(0.0f));
330             break;
331         default:
332             LOGW("Unknown direction");
333     }
334 }
335 
AddExtraSpaceToStartPosition(OffsetF & startPosition,float extraSpace,bool onMainAxis) const336 void WrapLayoutAlgorithm::AddExtraSpaceToStartPosition(OffsetF& startPosition, float extraSpace, bool onMainAxis) const
337 {
338     if (isReverse_) {
339         extraSpace = -extraSpace;
340     }
341     if (onMainAxis) {
342         if (isHorizontal_) {
343             startPosition.AddX(extraSpace);
344         } else {
345             startPosition.AddY(extraSpace);
346         }
347         return;
348     }
349     if (isHorizontal_) {
350         startPosition.AddY(extraSpace);
351         return;
352     }
353     startPosition.AddX(extraSpace);
354 }
355 
LayoutWholeWrap(OffsetF & startPosition,OffsetF & spaceBetweenContentsOnCrossAxis,LayoutWrapper * layoutWrapper)356 void WrapLayoutAlgorithm::LayoutWholeWrap(
357     OffsetF& startPosition, OffsetF& spaceBetweenContentsOnCrossAxis, LayoutWrapper* layoutWrapper)
358 {
359     auto contentNum = static_cast<int32_t>(contentList_.size());
360     if (contentNum == 0) {
361         return;
362     }
363 
364     const auto& layoutProp = layoutWrapper->GetLayoutProperty();
365     CHECK_NULL_VOID(layoutProp);
366     AddPaddingToStartPosition(startPosition);
367     if (isReverse_) {
368         AddExtraSpaceToStartPosition(startPosition, isHorizontal_ ? -frameSize_.Width() : -frameSize_.Height(), true);
369     }
370     // if cross axis size is not set, cross axis size is as large as children cross axis size sum
371     // no need to set alignment_.
372     if ((!isHorizontal_ && hasIdealWidth_ && crossLengthLimit_ <= totalCrossLength_) ||
373         (!isHorizontal_ && !hasIdealWidth_)) {
374         return;
375     }
376     if ((isHorizontal_ && hasIdealHeight_ && crossLengthLimit_ <= totalCrossLength_) ||
377         (isHorizontal_ && !hasIdealHeight_)) {
378         return;
379     }
380 
381     auto crossAxisRemainSpace = crossLengthLimit_ - totalCrossLength_;
382 
383     if (isReverse_) {
384         crossAxisRemainSpace = -crossAxisRemainSpace;
385     }
386     // switch align content enum, alignment when extra space exists in container extra spaces
387 
388     switch (alignment_) {
389         case WrapAlignment::START:
390             break;
391         // for reverse cases, start position will not include "first" item's main axis size
392         case WrapAlignment::END: {
393             AddExtraSpaceToStartPosition(startPosition, crossAxisRemainSpace, false);
394             break;
395         }
396         case WrapAlignment::CENTER: {
397             // divided the space by two
398             crossAxisRemainSpace /= 2.0f;
399             AddExtraSpaceToStartPosition(startPosition, crossAxisRemainSpace, false);
400             break;
401         }
402         case WrapAlignment::SPACE_BETWEEN: {
403             // space between will not affect start position, update space between only
404             float crossSpace =
405                 contentNum > 1 ? (crossLengthLimit_ - totalCrossLength_) / static_cast<float>(contentNum - 1) : 0.0f;
406             spaceBetweenContentsOnCrossAxis = isHorizontal_ ? OffsetF(0.0f, crossSpace) : OffsetF(crossSpace, 0.0f);
407             break;
408         }
409         case WrapAlignment::SPACE_EVENLY: {
410             float crossSpace = contentNum != -1 ? crossAxisRemainSpace / static_cast<float>(contentNum + 1) : 0.0f;
411             AddExtraSpaceToStartPosition(startPosition, crossSpace, false);
412             spaceBetweenContentsOnCrossAxis =
413                 isHorizontal_ ? OffsetF(0.0f, std::abs(crossSpace)) : OffsetF(std::abs(crossSpace), 0.0f);
414             break;
415         }
416         case WrapAlignment::SPACE_AROUND: {
417             float crossSpace = crossAxisRemainSpace / static_cast<float>(contentNum);
418             AddExtraSpaceToStartPosition(startPosition, crossSpace / 2.0f, false);
419             spaceBetweenContentsOnCrossAxis =
420                 isHorizontal_ ? OffsetF(0.0f, std::abs(crossSpace)) : OffsetF(std::abs(crossSpace), 0.0);
421             break;
422         }
423         default: {
424             break;
425         }
426     }
427 }
428 
GetMainAxisRemainSpace(float totalMainLength) const429 SizeF WrapLayoutAlgorithm::GetMainAxisRemainSpace(float totalMainLength) const
430 {
431     if (isHorizontal_) {
432         return SizeF(mainLengthLimit_ - totalMainLength, 0.0f);
433     }
434     return SizeF(0.0f, mainLengthLimit_ - totalMainLength);
435 }
436 
GetCrossAxisRemainSpace(float totalCrossLength) const437 SizeF WrapLayoutAlgorithm::GetCrossAxisRemainSpace(float totalCrossLength) const
438 {
439     if (isHorizontal_) {
440         return SizeF(0.0f, crossLengthLimit_ - totalCrossLength);
441     }
442     return SizeF(crossLengthLimit_ - totalCrossLength, 0.0f);
443 }
444 
GetMainAxisOffset(const OffsetF & offset) const445 float WrapLayoutAlgorithm::GetMainAxisOffset(const OffsetF& offset) const
446 {
447     if (isHorizontal_) {
448         return offset.GetX();
449     }
450     return offset.GetY();
451 }
452 
GetCrossAxisOffset(const OffsetF & offset) const453 float WrapLayoutAlgorithm::GetCrossAxisOffset(const OffsetF& offset) const
454 {
455     if (isHorizontal_) {
456         return offset.GetY();
457     }
458     return offset.GetX();
459 }
460 
TraverseContent(const OffsetF & startPosition,const OffsetF & spaceBetweenContentsOnCrossAxis)461 void WrapLayoutAlgorithm::TraverseContent(const OffsetF& startPosition, const OffsetF& spaceBetweenContentsOnCrossAxis)
462 {
463     // determine the content start position by main axis
464     OffsetF contentPosition(startPosition.GetX(), startPosition.GetY());
465     auto contentSpace = static_cast<float>(contentSpace_.ConvertToPx());
466     auto spaceBetween = isHorizontal_ ? spaceBetweenContentsOnCrossAxis.GetY() : spaceBetweenContentsOnCrossAxis.GetX();
467     for (const auto& content : contentList_) {
468         LayoutContent(content, contentPosition);
469         if (isHorizontal_) {
470             contentPosition.AddY(content.crossLength + contentSpace + spaceBetween);
471         } else {
472             contentPosition.AddX(content.crossLength + contentSpace + spaceBetween);
473         }
474     }
475 }
476 
GetItemMainOffset(float mainSpace) const477 OffsetF WrapLayoutAlgorithm::GetItemMainOffset(float mainSpace) const
478 {
479     // calculate the offset of each item in content
480     if (isHorizontal_) {
481         return OffsetF(mainSpace, 0.0);
482     }
483     return OffsetF(0.0, mainSpace);
484 }
485 
CalcItemCrossAxisOffset(const ContentInfo & content,const OffsetF & contentOffset,const RefPtr<GeometryNode> & node)486 float WrapLayoutAlgorithm::CalcItemCrossAxisOffset(
487     const ContentInfo& content, const OffsetF& contentOffset, const RefPtr<GeometryNode>& node)
488 {
489     switch (crossAlignment_) {
490         case WrapAlignment::START:
491         // stretch has been processed in measure, result is the same as start
492         case WrapAlignment::STRETCH: {
493             if (isHorizontal_) {
494                 return contentOffset.GetY();
495             }
496             return contentOffset.GetX();
497         }
498         case WrapAlignment::END: {
499             auto itemFrameSize = node->GetMarginFrameSize();
500             if (isHorizontal_) {
501                 return contentOffset.GetY() + content.crossLength - itemFrameSize.Height();
502             }
503             return contentOffset.GetX() + content.crossLength - itemFrameSize.Width();
504         }
505         case WrapAlignment::CENTER: {
506             // divide the space by two
507             auto itemFrameSize = node->GetMarginFrameSize();
508             if (isHorizontal_) {
509                 return contentOffset.GetY() + (content.crossLength - itemFrameSize.Height()) / 2.0f;
510             }
511             return contentOffset.GetX() + (content.crossLength - itemFrameSize.Width()) / 2.0f;
512         }
513         case WrapAlignment::BASELINE: {
514             break;
515         }
516         default: {
517             if (isHorizontal_) {
518                 return contentOffset.GetY();
519             }
520             return contentOffset.GetX();
521 
522             break;
523         }
524     }
525     if (isHorizontal_) {
526         return contentOffset.GetY();
527     }
528     return contentOffset.GetX();
529 }
530 
CalcItemMainAxisStartAndSpaceBetween(OffsetF & startPosition,OffsetF & spaceBetweenItemsOnMainAxis,const ContentInfo & content)531 void WrapLayoutAlgorithm::CalcItemMainAxisStartAndSpaceBetween(
532     OffsetF& startPosition, OffsetF& spaceBetweenItemsOnMainAxis, const ContentInfo& content)
533 {
534     // switch align content enum, alignment when extra space exists in container extra spaces
535     float spaceLeftOnMainAxis = mainLengthLimit_ - content.mainLength;
536     switch (mainAlignment_) {
537         case WrapAlignment::START:
538             break;
539         case WrapAlignment::END: {
540             AddExtraSpaceToStartPosition(startPosition, spaceLeftOnMainAxis, true);
541             break;
542         }
543         case WrapAlignment::CENTER: {
544             AddExtraSpaceToStartPosition(startPosition, spaceLeftOnMainAxis / 2.0f, true);
545             break;
546         }
547         case WrapAlignment::SPACE_BETWEEN: {
548             float mainSpace = content.count > 1 ? spaceLeftOnMainAxis / static_cast<float>(content.count - 1) : 0.0f;
549             spaceBetweenItemsOnMainAxis = isHorizontal_ ? OffsetF(mainSpace, 0.0f) : OffsetF(0.0f, mainSpace);
550             break;
551         }
552         case WrapAlignment::SPACE_EVENLY: {
553             float mainSpace = content.count != -1 ? spaceLeftOnMainAxis / static_cast<float>(content.count + 1) : 0.0f;
554             AddExtraSpaceToStartPosition(startPosition, mainSpace, true);
555             spaceBetweenItemsOnMainAxis = isHorizontal_ ? OffsetF(mainSpace, 0.0f) : OffsetF(0.0f, mainSpace);
556             break;
557         }
558         case WrapAlignment::SPACE_AROUND: {
559             float mainSpace = content.count != 0 ? spaceLeftOnMainAxis / static_cast<float>(content.count) : 0.0f;
560             AddExtraSpaceToStartPosition(startPosition, mainSpace / 2.0f, true);
561             spaceBetweenItemsOnMainAxis = isHorizontal_ ? OffsetF(mainSpace, 0.0f) : OffsetF(0.0f, mainSpace);
562             break;
563         }
564         default: {
565             break;
566         }
567     }
568 }
569 
LayoutContent(const ContentInfo & content,const OffsetF & position)570 void WrapLayoutAlgorithm::LayoutContent(const ContentInfo& content, const OffsetF& position)
571 {
572     int32_t itemNum = content.count;
573     if (itemNum == 0) {
574         return;
575     }
576     OffsetF contentStartPosition(position.GetX(), position.GetY());
577     OffsetF spaceBetweenItemsOnMainAxis;
578     CalcItemMainAxisStartAndSpaceBetween(contentStartPosition, spaceBetweenItemsOnMainAxis, content);
579 
580     FlexItemProperties flexItemProperties;
581     GetFlexItemProperties(content, flexItemProperties);
582     float remainSpace = mainLengthLimit_ - currentMainLength_;
583     for (const auto& itemWrapper : content.itemList) {
584         auto item = itemWrapper->GetGeometryNode();
585         if (GreatNotEqual(remainSpace, 0.0f)) {
586             CalcFlexGrowLayout(itemWrapper, flexItemProperties, remainSpace);
587         }
588         // calc start position and between space
589         auto itemMainAxisOffset = isHorizontal_ ? contentStartPosition.GetX() : contentStartPosition.GetY();
590         if (isReverse_) {
591             itemMainAxisOffset -= GetItemMainAxisLength(item);
592         }
593         auto itemCrossAxisOffset = CalcItemCrossAxisOffset(content, contentStartPosition, item);
594         OffsetF offset;
595         float contentMainAxisSpan = 0.0f;
596         if (isHorizontal_) {
597             offset = OffsetF(itemMainAxisOffset, itemCrossAxisOffset);
598             contentMainAxisSpan = item->GetMarginFrameSize().Width() + static_cast<float>(spacing_.ConvertToPx()) +
599                                   spaceBetweenItemsOnMainAxis.GetX();
600             contentStartPosition.AddX(isReverse_ ? -contentMainAxisSpan : contentMainAxisSpan);
601         } else {
602             offset = OffsetF(itemCrossAxisOffset, itemMainAxisOffset);
603             contentMainAxisSpan = item->GetMarginFrameSize().Height() + static_cast<float>(spacing_.ConvertToPx()) +
604                                   spaceBetweenItemsOnMainAxis.GetY();
605             contentStartPosition.AddY(isReverse_ ? -contentMainAxisSpan : contentMainAxisSpan);
606         }
607         itemWrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
608     }
609 }
610 
GetFlexItemProperties(const ContentInfo & content,FlexItemProperties & flexItemProperties)611 void WrapLayoutAlgorithm::GetFlexItemProperties(const ContentInfo& content, FlexItemProperties& flexItemProperties)
612 {
613     auto spacing = static_cast<float>(spacing_.ConvertToPx());
614     currentMainLength_ = 0.0f;
615     for (const auto& itemWrapper : content.itemList) {
616         if (!itemWrapper) {
617             continue;
618         }
619         currentMainLength_ += GetItemMainAxisLength(itemWrapper->GetGeometryNode()) + spacing;
620         auto layoutProperty = itemWrapper->GetLayoutProperty();
621         if (!layoutProperty) {
622             continue;
623         }
624         const auto& flexItemProperty = layoutProperty->GetFlexItemProperty();
625         if (!flexItemProperty) {
626             continue;
627         }
628         auto flexGrow = flexItemProperty->GetFlexGrow().value_or(0.0f);
629         if (GreatNotEqual(flexGrow, 0.0f)) {
630             flexItemProperties.totalGrow += flexGrow;
631         }
632     }
633 }
634 
CalcFlexGrowLayout(const RefPtr<LayoutWrapper> & itemWrapper,const FlexItemProperties & flexItemProperties,float remainSpace)635 void WrapLayoutAlgorithm::CalcFlexGrowLayout(
636     const RefPtr<LayoutWrapper>& itemWrapper, const FlexItemProperties& flexItemProperties, float remainSpace)
637 {
638     CHECK_NULL_VOID(itemWrapper);
639     auto layoutProperty = itemWrapper->GetLayoutProperty();
640     CHECK_NULL_VOID(layoutProperty);
641     auto& flexItemProperty = layoutProperty->GetFlexItemProperty();
642     CHECK_NULL_VOID(flexItemProperty);
643     auto layoutConstraint = layoutProperty->GetLayoutConstraint();
644     if (!layoutConstraint.has_value()) {
645         return;
646     }
647 
648     auto layoutConstraintValue = layoutConstraint.value();
649     float itemFlex = flexItemProperty->GetFlexGrow().value_or(0.0f);
650     if (GreatNotEqual(itemFlex, 0.0f) && GreatNotEqual(remainSpace, 0.0f) &&
651         GreatNotEqual(flexItemProperties.totalGrow, 0.0f)) {
652         float flexSize = itemFlex * remainSpace / flexItemProperties.totalGrow;
653         flexSize += GetItemMainAxisLength(itemWrapper->GetGeometryNode());
654         OptionalSizeF& selfIdealSize = layoutConstraintValue.selfIdealSize;
655         if (direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE) {
656             selfIdealSize.SetWidth(flexSize);
657         } else {
658             selfIdealSize.SetHeight(flexSize);
659         }
660         itemWrapper->Measure(layoutConstraintValue);
661     }
662 }
663 
AddPaddingToStartPositionForColumn(OffsetF & startPosition) const664 void WrapLayoutAlgorithm::AddPaddingToStartPositionForColumn(OffsetF& startPosition) const
665 {
666     switch (direction_) {
667         // vertical will start from top left
668         case WrapDirection::VERTICAL:
669             if (isRightDirection_) {
670                 startPosition.AddX(-padding_.right.value_or(0.0f));
671             } else {
672                 startPosition.AddX(padding_.left.value_or(0.0f));
673             }
674             startPosition.AddY(padding_.top.value_or(0.0f));
675             break;
676         case WrapDirection::VERTICAL_REVERSE:
677             if (isRightDirection_) {
678                 startPosition.AddX(-padding_.right.value_or(0.0f));
679             } else {
680                 startPosition.AddX(padding_.left.value_or(0.0f));
681             }
682             startPosition.AddY(-padding_.bottom.value_or(0.0f));
683             break;
684         default:
685             LOGW("Unknown direction");
686     }
687 }
688 
UpdateStartPositionByAlign(OffsetF & startPosition,float crossAxisRemainSpace,OffsetF & spaceBetweenContentsOnCrossAxis,int32_t contentNum)689 void WrapLayoutAlgorithm::UpdateStartPositionByAlign(
690     OffsetF& startPosition, float crossAxisRemainSpace, OffsetF& spaceBetweenContentsOnCrossAxis, int32_t contentNum)
691 {
692     // switch align content enum, alignment when extra space exists in container extra spaces
693     switch (alignment_) {
694         case WrapAlignment::START:
695             break;
696         // for reverse cases, start position will not include "first" item's main axis size
697         case WrapAlignment::END: {
698             startPosition.AddX(crossAxisRemainSpace);
699             break;
700         }
701         case WrapAlignment::CENTER: {
702             // divided the space by two
703             crossAxisRemainSpace /= 2.0f;
704             startPosition.AddX(crossAxisRemainSpace);
705             break;
706         }
707         case WrapAlignment::SPACE_BETWEEN: {
708             // space between will not affect start position, update space between only
709             float crossSpace =
710                 contentNum > 1 ? (crossLengthLimit_ - totalCrossLength_) / static_cast<float>(contentNum - 1) : 0.0f;
711             spaceBetweenContentsOnCrossAxis = OffsetF(crossSpace, 0.0f);
712             break;
713         }
714         case WrapAlignment::SPACE_EVENLY: {
715             float crossSpace = contentNum != -1 ? crossAxisRemainSpace / static_cast<float>(contentNum + 1) : 0.0f;
716             startPosition.AddX(crossSpace);
717             spaceBetweenContentsOnCrossAxis =
718                 isHorizontal_ ? OffsetF(0.0f, std::abs(crossSpace)) : OffsetF(std::abs(crossSpace), 0.0f);
719             break;
720         }
721         case WrapAlignment::SPACE_AROUND: {
722             float crossSpace = contentNum != 0 ? crossAxisRemainSpace / static_cast<float>(contentNum) : 0.0f;
723             startPosition.AddX(crossSpace / 2.0f);
724             spaceBetweenContentsOnCrossAxis = OffsetF(std::abs(crossSpace), 0.0);
725             break;
726         }
727         default: {
728             break;
729         }
730     }
731 }
732 
LayoutWholeColumnWrap(OffsetF & startPosition,OffsetF & spaceBetweenContentsOnCrossAxis,LayoutWrapper * layoutWrapper)733 void WrapLayoutAlgorithm::LayoutWholeColumnWrap(
734     OffsetF& startPosition, OffsetF& spaceBetweenContentsOnCrossAxis, LayoutWrapper* layoutWrapper)
735 {
736     auto contentNum = static_cast<int32_t>(contentList_.size());
737     if (contentNum == 0) {
738         return;
739     }
740 
741     const auto& layoutProp = layoutWrapper->GetLayoutProperty();
742     CHECK_NULL_VOID(layoutProp);
743     AddPaddingToStartPositionForColumn(startPosition);
744     if (isRightDirection_) {
745         startPosition.AddX(frameSize_.Width());
746     }
747     if (isColumnReverse_) {
748         AddExtraSpaceToStartPosition(startPosition, -frameSize_.Height(), true);
749     }
750     // if cross axis size is not set, cross axis size is as large as children cross axis size sum
751     // no need to set alignment_.
752     if ((!isHorizontal_ && hasIdealWidth_ && crossLengthLimit_ <= totalCrossLength_) ||
753         (!isHorizontal_ && !hasIdealWidth_)) {
754         return;
755     }
756     auto crossAxisRemainSpace = crossLengthLimit_ - totalCrossLength_;
757     if (isRightDirection_) {
758         crossAxisRemainSpace = -crossAxisRemainSpace;
759     }
760     UpdateStartPositionByAlign(startPosition, crossAxisRemainSpace, spaceBetweenContentsOnCrossAxis, contentNum);
761 }
762 
LayoutColumnContent(const ContentInfo & content,const OffsetF & position)763 void WrapLayoutAlgorithm::LayoutColumnContent(const ContentInfo& content, const OffsetF& position)
764 {
765     int32_t itemNum = content.count;
766     if (itemNum == 0) {
767         return;
768     }
769     OffsetF contentStartPosition(position.GetX(), position.GetY());
770     OffsetF spaceBetweenItemsOnMainAxis;
771     CalcItemMainAxisStartAndSpaceBetween(contentStartPosition, spaceBetweenItemsOnMainAxis, content);
772 
773     FlexItemProperties flexItemProperties;
774     GetFlexItemProperties(content, flexItemProperties);
775     float remainSpace = mainLengthLimit_ - currentMainLength_;
776     for (const auto& itemWrapper : content.itemList) {
777         auto item = itemWrapper->GetGeometryNode();
778         if (GreatNotEqual(remainSpace, 0.0f)) {
779             CalcFlexGrowLayout(itemWrapper, flexItemProperties, remainSpace);
780         }
781         // calc start position and between space
782         auto itemMainAxisOffset = isHorizontal_ ? contentStartPosition.GetX() : contentStartPosition.GetY();
783         auto itemCrossAxisOffset = CalcItemCrossAxisOffset(content, contentStartPosition, item);
784         if (isRightDirection_) {
785             itemCrossAxisOffset -= GetItemCrossAxisLength(item);
786         }
787         OffsetF offset;
788         float contentMainAxisSpan = 0.0f;
789         if (isColumnReverse_) {
790             itemMainAxisOffset -= GetItemMainAxisLength(item);
791         }
792         offset = OffsetF(itemCrossAxisOffset, itemMainAxisOffset);
793         contentMainAxisSpan = item->GetMarginFrameSize().Height() + static_cast<float>(spacing_.ConvertToPx()) +
794                               spaceBetweenItemsOnMainAxis.GetY();
795         contentStartPosition.AddY(isColumnReverse_ ? -contentMainAxisSpan : contentMainAxisSpan);
796         itemWrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
797     }
798 }
799 
TraverseColumnContent(const OffsetF & startPosition,const OffsetF & spaceBetweenContentsOnCrossAxis)800 void WrapLayoutAlgorithm::TraverseColumnContent(
801     const OffsetF& startPosition, const OffsetF& spaceBetweenContentsOnCrossAxis)
802 {
803     // determine the content start position by main axis
804     OffsetF contentPosition(startPosition.GetX(), startPosition.GetY());
805     auto contentSpace = static_cast<float>(contentSpace_.ConvertToPx());
806     auto spaceBetween = spaceBetweenContentsOnCrossAxis.GetX();
807     for (const auto& content : contentList_) {
808         LayoutColumnContent(content, contentPosition);
809         if (isRightDirection_) {
810             float leftSpace = content.crossLength + contentSpace + spaceBetween;
811             contentPosition.AddX(-leftSpace);
812         } else {
813             contentPosition.AddX(content.crossLength + contentSpace + spaceBetween);
814         }
815     }
816 }
817 } // namespace OHOS::Ace::NG
818