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