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