1 /*
2  * Copyright (c) 2021-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/wrap/render_wrap.h"
17 
18 #include <algorithm>
19 
20 #include "base/geometry/animatable_dimension.h"
21 #include "base/utils/utils.h"
22 #include "core/components/checkable/checkable_theme.h"
23 #include "core/components/common/properties/edge.h"
24 #include "core/components/flex/render_flex_item.h"
25 #include "core/components/image/render_image.h"
26 #include "core/components/marquee/render_marquee.h"
27 #include "core/components/progress/render_progress.h"
28 #include "core/components/search/render_search.h"
29 #include "core/components/slider/render_slider.h"
30 #include "core/components/text_field/render_text_field.h"
31 #include "core/components/wrap/wrap_component.h"
32 #include "frameworks/bridge/common/dom/dom_node.h"
33 
34 namespace OHOS::Ace {
35 namespace {
36 
37 constexpr int32_t MIN_COMPATIBLE_VERSION = 6;
38 
39 }
40 
Create()41 RefPtr<RenderNode> RenderWrap::Create()
42 {
43     return AceType::MakeRefPtr<RenderWrap>();
44 }
45 
Update(const RefPtr<Component> & component)46 void RenderWrap::Update(const RefPtr<Component>& component)
47 {
48     const RefPtr<WrapComponent> wrap = AceType::DynamicCast<WrapComponent>(component);
49     if (!wrap) {
50         LOGE("Wrap::RenderWrap update dynamicCast to nullptr error");
51         return;
52     }
53     direction_ = wrap->GetDirection();
54     // Whole alignment
55     alignment_ = wrap->GetAlignment();
56     // content main alignment
57     mainAlignment_ = wrap->GetMainAlignment();
58     // content cross alignment
59     crossAlignment_ = wrap->GetCrossAlignment();
60     spacing_ = wrap->GetSpacing();
61     contentSpace_ = wrap->GetContentSpacing();
62     dialogStretch_ = wrap->GetDialogStretch();
63     horizontalMeasure_ = wrap->GetHorizontalMeasure();
64     verticalMeasure_ = wrap->GetVerticalMeasure();
65     SetTextDirection(wrap->GetTextDirection());
66     isLeftToRight_ = (wrap->GetTextDirection() == TextDirection::LTR);
67     contentList_.clear();
68     MarkNeedLayout();
69 }
70 
PerformLayout()71 void RenderWrap::PerformLayout()
72 {
73     if (GetChildren().empty()) {
74         // no child will set current to empty and return
75         SetLayoutSize(Size(0.0, 0.0));
76         return;
77     }
78 
79     PerformLayoutInitialize();
80 
81     // overall size including space
82     totalMainLength_ = 0.0;
83     totalCrossLength_ = 0.0;
84 
85     LayoutParam layoutParam;
86     layoutParam.SetMinSize(Size(0.0, 0.0));
87     layoutParam.SetMaxSize(GetLeftSize(0.0, mainLengthLimit_, crossLengthLimit_));
88     if (dialogStretch_) {
89         HandleDialogStretch(layoutParam);
90     } else {
91         auto spacing = NormalizeToPx(spacing_);
92         auto contentSpace = NormalizeToPx(contentSpace_);
93         // content size
94         double currentMainLength = 0.0;
95         // the cross length is without space
96         double currentCrossLength = 0.0;
97         // number of item in content
98         int32_t count = 0;
99         // max baseline of each line
100         double baselineDistance = 0.0;
101         std::list<RefPtr<RenderNode>> itemsList;
102         bool beforeIsBlock = false;
103         double beforeMarginBottom = 0.0;
104 
105         for (auto& item : GetChildren()) {
106             auto flexItem = AceType::DynamicCast<RenderFlexItem>(item);
107             if (flexItem &&
108                 (flexItem->GetDisplayType() == DisplayType::BLOCK)) {
109                 CalculateMargin(item, beforeIsBlock, beforeMarginBottom);
110                 item->Layout(layoutParam);
111                 AddBlock(count, item, itemsList, currentMainLength, currentCrossLength, baselineDistance);
112                 continue;
113             } else if (flexItem && flexItem->GetDisplayType() == DisplayType::INLINE) {
114                 beforeIsBlock = false;
115                 beforeMarginBottom = 0.0;
116                 SetDefault(item);
117                 item->Layout(layoutParam);
118             } else {
119                 beforeIsBlock = false;
120                 beforeMarginBottom = 0.0;
121                 item->Layout(layoutParam);
122             }
123 
124             if (mainLengthLimit_ >= currentMainLength + GetMainItemLength(item)) {
125                 currentMainLength += GetMainItemLength(item);
126                 currentMainLength += spacing;
127                 currentCrossLength = std::max(currentCrossLength, GetCrossItemLength(item));
128                 if (crossAlignment_ == WrapAlignment::BASELINE) {
129                     baselineDistance = std::max(baselineDistance, item->GetBaselineDistance(TextBaseline::ALPHABETIC));
130                 }
131                 itemsList.push_back(item);
132                 count += 1;
133             } else {
134                 currentMainLength -= spacing;
135                 if ((direction_ == WrapDirection::HORIZONTAL && !isLeftToRight_) ||
136                     (direction_ == WrapDirection::HORIZONTAL_REVERSE && isLeftToRight_) ||
137                     direction_ == WrapDirection::VERTICAL_REVERSE) {
138                     itemsList.reverse();
139                 }
140                 auto contentInfo = ContentInfo(currentMainLength, currentCrossLength, count, itemsList);
141                 contentInfo.maxBaselineDistance = baselineDistance;
142                 contentList_.emplace_back(contentInfo);
143                 itemsList.clear();
144                 totalMainLength_ = std::max(currentMainLength, totalMainLength_);
145                 totalCrossLength_ += currentCrossLength + contentSpace;
146                 currentMainLength = GetMainItemLength(item) + spacing;
147                 currentCrossLength = GetCrossItemLength(item);
148                 if (crossAlignment_ == WrapAlignment::BASELINE) {
149                     baselineDistance = item->GetBaselineDistance(TextBaseline::ALPHABETIC);
150                 }
151                 itemsList.push_back(item);
152                 count = 1;
153             }
154         }
155         if (count != 0) {
156             // Add last content into list
157             currentMainLength -= spacing;
158             if ((direction_ == WrapDirection::HORIZONTAL && !isLeftToRight_) ||
159                 (direction_ == WrapDirection::HORIZONTAL_REVERSE && isLeftToRight_) ||
160                 (direction_ == WrapDirection::VERTICAL_REVERSE)) {
161                 itemsList.reverse();
162             }
163             auto contentInfo = ContentInfo(currentMainLength, currentCrossLength, count, itemsList);
164             contentInfo.maxBaselineDistance = baselineDistance;
165             contentList_.emplace_back(contentInfo);
166             if ((direction_ == WrapDirection::VERTICAL || direction_ == WrapDirection::VERTICAL_REVERSE) &&
167                 !isLeftToRight_) {
168                 contentList_.reverse();
169             }
170             totalMainLength_ = std::max(currentMainLength, totalMainLength_);
171             // n contents has n - 1 space
172             totalCrossLength_ += currentCrossLength;
173         }
174     }
175     LayoutWholeWrap();
176     SetWrapLayoutSize(mainLengthLimit_, totalCrossLength_);
177     contentList_.clear();
178 }
179 
AddBlock(int32_t & count,const RefPtr<RenderNode> & item,std::list<RefPtr<RenderNode>> & itemsList,double & currentMainLength,double & currentCrossLength,double & baselineDistance)180 void RenderWrap::AddBlock(int32_t& count, const RefPtr<RenderNode>& item, std::list<RefPtr<RenderNode>>& itemsList,
181     double& currentMainLength, double& currentCrossLength, double& baselineDistance)
182 {
183     auto contentSpace = NormalizeToPx(contentSpace_);
184     if (count != 0) {
185         auto contentInfo = ContentInfo(currentMainLength, currentCrossLength, count, itemsList);
186         contentInfo.maxBaselineDistance = item->GetBaselineDistance(TextBaseline::ALPHABETIC);
187         contentList_.emplace_back(contentInfo);
188         itemsList.clear();
189         totalCrossLength_ += currentCrossLength + contentSpace;
190     }
191     itemsList.push_back(item);
192     double itemLength = GetMainItemLength(item);
193     currentCrossLength = GetCrossItemLength(item);
194     totalMainLength_ = std::max(itemLength, totalMainLength_);
195     totalCrossLength_ += currentCrossLength + contentSpace;
196     auto contentInfo2 = ContentInfo(itemLength, currentCrossLength, 1, itemsList);
197     contentInfo2.maxBaselineDistance = baselineDistance;
198     contentList_.emplace_back(contentInfo2);
199     itemsList.clear();
200     currentCrossLength = 0.0;
201     count = 0;
202 }
203 
CalculateMargin(const RefPtr<RenderNode> & item,bool & beforeIsBlock,double & beforeMarginBottom)204 void RenderWrap::CalculateMargin(const RefPtr<RenderNode>& item, bool& beforeIsBlock, double& beforeMarginBottom)
205 {
206     double currentItemMarginBottom = 0.0;
207     RefPtr<RenderNode> itemNode = item;
208     while (itemNode) {
209         if (itemNode->GetChildren().empty()) {
210             break;
211         }
212         itemNode = itemNode->GetChildren().front();
213         auto itemTemp = AceType::DynamicCast < RenderBoxBase >(itemNode);
214         if (!itemTemp) {
215             continue;
216         }
217         if (!beforeIsBlock) {
218             beforeIsBlock = true;
219             auto setterBottom = DimensionHelper(&Edge::SetBottom, &Edge::Bottom);
220             beforeMarginBottom = itemTemp->GetMargin(setterBottom).Value();
221         } else {
222             auto setterTop = DimensionHelper(&Edge::SetTop, &Edge::Top);
223             auto setterBottom = DimensionHelper(&Edge::SetBottom, &Edge::Bottom);
224             double currentItemMarginTop = itemTemp->GetMargin(setterTop).Value();
225             currentItemMarginBottom =  itemTemp->GetMargin(setterBottom).Value();
226             if (GreatOrEqual(beforeMarginBottom, 0.0) && GreatOrEqual(currentItemMarginTop, 0.0)) {
227                 double minMargin = std::min(beforeMarginBottom, currentItemMarginTop);
228                 AnimatableDimension distance = AnimatableDimension(currentItemMarginTop - minMargin);
229                 auto setter = DimensionHelper(&Edge::SetTop, &Edge::Top);
230                 itemTemp->SetMargin(distance, setter);
231                 beforeMarginBottom = currentItemMarginBottom;
232                 break;
233             }
234         }
235     }
236 }
237 
SetDefault(const RefPtr<RenderNode> & item)238 void RenderWrap::SetDefault(const RefPtr<RenderNode>& item)
239 {
240     RefPtr<RenderBoxBase> boxItem = nullptr;
241     RefPtr<BoxComponent> boxComponent = nullptr;
242     RefPtr<RenderNode> itemNode = item;
243     while (itemNode) {
244         if (itemNode->GetChildren().empty()) {
245             break;
246         }
247         itemNode = itemNode->GetChildren().front();
248         auto itemTemp = AceType::DynamicCast <RenderBoxBase>(itemNode);
249         if (itemTemp) {
250             boxItem = itemTemp;
251         }
252 
253         auto sliderItem = AceType::DynamicCast <RenderSlider>(itemNode);
254         if (sliderItem && boxItem) {
255             boxItem->SetWidth(Dimension(400.0f, DimensionUnit::VP));
256             break;
257         }
258 
259         auto progressItem = AceType::DynamicCast <RenderProgress>(itemNode);
260         if (progressItem && boxItem) {
261             boxItem->SetWidth(Dimension(200.0f, DimensionUnit::VP));
262             break;
263         }
264 
265         auto imageItem = AceType::DynamicCast <RenderImage>(itemNode);
266         if (imageItem && boxItem) {
267             boxItem->SetWidth(Dimension(200.0f, DimensionUnit::VP));
268             boxItem->SetHeight(Dimension(200.0f, DimensionUnit::VP));
269             break;
270         }
271         auto marqueeItem = AceType::DynamicCast <RenderMarquee>(itemNode);
272         if (marqueeItem && boxItem) {
273             boxItem->SetWidth(Dimension(400.0f, DimensionUnit::VP));
274             break;
275         }
276         auto searchItem = AceType::DynamicCast <RenderSearch>(itemNode);
277         if (searchItem && boxItem) {
278             boxItem->SetWidth(Dimension(400.0f, DimensionUnit::VP));
279             break;
280         }
281         auto textFieldItem = AceType::DynamicCast <RenderTextField>(itemNode);
282         if (textFieldItem && boxItem) {
283             boxItem->SetWidth(Dimension(400.0f, DimensionUnit::VP));
284             break;
285         }
286     };
287 }
288 
HandleDialogStretch(const LayoutParam & layoutParam)289 void RenderWrap::HandleDialogStretch(const LayoutParam& layoutParam)
290 {
291     int32_t dialogButtonNum = 0;
292     double totalLength = 0.0;
293     auto spacing = NormalizeToPx(spacing_);
294     auto contentSpace = NormalizeToPx(contentSpace_);
295     // whether the btn in the wrap needs wrap
296     for (const auto& item : GetChildren()) {
297         dialogButtonNum += 1;
298         item->Layout(layoutParam);
299         totalLength += GetMainItemLength(item) + spacing;
300         if (totalLength - spacing > mainLengthLimit_) {
301             dialogDirection_ = WrapDirection::VERTICAL;
302         }
303     }
304     if (dialogButtonNum == 0) {
305         LOGW("dialog button number is 0");
306         return;
307     }
308 
309     double buttonSize = (mainLengthLimit_ - spacing * (dialogButtonNum - 1)) / dialogButtonNum;
310     std::list<RefPtr<RenderNode>> itemsList;
311     for (const auto& item : GetChildren()) {
312         LayoutParam newParam;
313         // if dialog is vertical, stretch each button equally to fill max length, otherwise stretch equally in same line
314         double stretchSize = dialogDirection_ == WrapDirection::VERTICAL ? mainLengthLimit_ : buttonSize;
315         newParam.SetFixedSize(
316             (direction_ == WrapDirection::HORIZONTAL ? Size(stretchSize, item->GetLayoutSize().Height())
317                                                      : Size(item->GetLayoutSize().Width(), stretchSize)));
318         item->Layout(newParam);
319         itemsList.push_back(item);
320         totalMainLength_ = mainLengthLimit_;
321 
322         if (dialogDirection_ == WrapDirection::VERTICAL) {
323             // stretch each button equally to fill max length
324 
325             totalCrossLength_ += direction_ == WrapDirection::HORIZONTAL ? item->GetLayoutSize().Height()
326                                                                          : item->GetLayoutSize().Width();
327             totalCrossLength_ += contentSpace;
328             contentList_.emplace_back(
329                 ContentInfo(newParam.GetMaxSize().Width(), newParam.GetMaxSize().Height(), 1, itemsList));
330             itemsList.clear();
331         } else {
332             // stretch each button equally in same line
333             totalCrossLength_ = std::max(direction_ == WrapDirection::HORIZONTAL ? item->GetLayoutSize().Height()
334                                                                                  : item->GetLayoutSize().Width(),
335                 totalCrossLength_);
336         }
337     }
338     // if wrap direction is vertical, item has already added into content list
339     if (dialogDirection_ == WrapDirection::VERTICAL) {
340         totalCrossLength_ -= contentSpace;
341         return;
342     }
343     if (!isLeftToRight_) {
344         itemsList.reverse();
345     }
346     if (direction_ == WrapDirection::HORIZONTAL) {
347         contentList_.emplace_back(ContentInfo(mainLengthLimit_, totalCrossLength_, dialogButtonNum, itemsList));
348     } else {
349         contentList_.emplace_back(ContentInfo(totalCrossLength_, mainLengthLimit_, dialogButtonNum, itemsList));
350     }
351 }
352 
GetMainItemLength(const RefPtr<RenderNode> & item) const353 double RenderWrap::GetMainItemLength(const RefPtr<RenderNode>& item) const
354 {
355     return direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE
356                ? item->GetLayoutSize().Width()
357                : item->GetLayoutSize().Height();
358 }
359 
GetCrossItemLength(const RefPtr<RenderNode> & item) const360 double RenderWrap::GetCrossItemLength(const RefPtr<RenderNode>& item) const
361 {
362     return direction_ == WrapDirection::VERTICAL || direction_ == WrapDirection::VERTICAL_REVERSE
363                ? item->GetLayoutSize().Width()
364                : item->GetLayoutSize().Height();
365 }
366 
PerformLayoutInitialize()367 void RenderWrap::PerformLayoutInitialize()
368 {
369     if (direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE) {
370         mainLengthLimit_ = GetLayoutParam().GetMaxSize().Width();
371         crossLengthLimit_ = GetLayoutParam().GetMaxSize().Height();
372     } else {
373         mainLengthLimit_ =
374             GetLayoutParam().GetMaxSize().IsInfinite() ? viewPort_.Height() : GetLayoutParam().GetMaxSize().Height();
375         crossLengthLimit_ = GetLayoutParam().GetMaxSize().Width();
376     }
377 }
378 
GetLeftSize(double crossLength,double mainLeftLength,double crossLeftLength) const379 Size RenderWrap::GetLeftSize(double crossLength, double mainLeftLength, double crossLeftLength) const
380 {
381     if (direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE) {
382         return Size(mainLeftLength, crossLeftLength - crossLength);
383     } else {
384         return Size(crossLeftLength - crossLength, mainLeftLength);
385     }
386 }
387 
LayoutWholeWrap()388 void RenderWrap::LayoutWholeWrap()
389 {
390     int32_t contentNum = static_cast<int32_t>(contentList_.size());
391     if (contentNum == 0) {
392         LOGW("no content in wrap");
393         return;
394     }
395     Offset startPosition;
396     Offset betweenPosition;
397     bool isHorizontal = direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE;
398 
399     switch (alignment_) {
400         case WrapAlignment::START: {
401             startPosition = Offset(0.0, 0.0);
402             betweenPosition = Offset();
403             break;
404         }
405         case WrapAlignment::END: {
406             startPosition = GetContentOffset(totalCrossLength_);
407             betweenPosition = Offset();
408             break;
409         }
410         case WrapAlignment::CENTER: {
411             // divided the space by two
412             startPosition = GetContentOffset(totalCrossLength_) / 2;
413             betweenPosition = Offset();
414             break;
415         }
416         case WrapAlignment::SPACE_BETWEEN: {
417             startPosition = Offset(0.0, 0.0);
418             double crossSpace =
419                 contentNum > 1 ? (crossLengthLimit_ - totalCrossLength_) / static_cast<double>(contentNum - 1) : 0.0;
420             betweenPosition = isHorizontal ? Offset(0.0, crossSpace) : Offset(crossSpace, 0.0);
421             break;
422         }
423         case WrapAlignment::SPACE_EVENLY: {
424             double leftSpace = crossLengthLimit_ - totalCrossLength_;
425             double crossSpace = leftSpace / static_cast<double>(contentNum + 1);
426             startPosition = isHorizontal ? Offset(0.0, crossSpace) : Offset(crossSpace, 0.0);
427             betweenPosition = isHorizontal ? Offset(0.0, crossSpace) : Offset(crossSpace, 0.0);
428             break;
429         }
430         case WrapAlignment::SPACE_AROUND: {
431             double leftSpace = crossLengthLimit_ - totalCrossLength_;
432             double crossSpace = leftSpace / static_cast<double>(contentNum);
433             startPosition = isHorizontal ? Offset(0.0, crossSpace / 2) : Offset(crossSpace / 2, 0.0);
434             betweenPosition = isHorizontal ? Offset(0.0, crossSpace) : Offset(crossSpace, 0.0);
435             break;
436         }
437         default: {
438             LOGE("Wrap::alignment setting error.");
439             startPosition = Offset(0.0, 0.0);
440             betweenPosition = Offset();
441             break;
442         }
443     }
444     auto context = context_.Upgrade();
445     bool applyNewOffset = context ? context->GetMinPlatformVersion() >= MIN_COMPATIBLE_VERSION : false;
446     if (applyNewOffset) {
447         // In content type, wrap is as large as children, no need to set alignment_.
448         if ((!isHorizontal && horizontalMeasure_ == MeasureType::CONTENT) ||
449             (isHorizontal && verticalMeasure_ == MeasureType::CONTENT)) {
450             startPosition = Offset();
451             betweenPosition = Offset();
452         }
453     }
454     TraverseContent(startPosition, betweenPosition);
455 }
456 
GetContentOffset(double totalCrossLength) const457 Offset RenderWrap::GetContentOffset(double totalCrossLength) const
458 {
459     if (direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE) {
460         return Offset(0.0, crossLengthLimit_ - totalCrossLength);
461     } else {
462         return Offset(crossLengthLimit_ - totalCrossLength, 0.0);
463     }
464 }
465 
TraverseContent(const Offset & startPosition,const Offset & betweenPosition) const466 void RenderWrap::TraverseContent(const Offset& startPosition, const Offset& betweenPosition) const
467 {
468     // determine the content start position by main axis
469     Offset accumulateOffset = startPosition;
470     int32_t startItemIndex = 0;
471     double currentMainSpaceLength = 0.0;
472     for (const auto& content : contentList_) {
473         // dfs positioned item in each content
474         currentMainSpaceLength = mainLengthLimit_ - content.mainLength_;
475         int32_t itemNum = content.count_;
476         if (itemNum == 0) {
477             LOGE("fail to TraverseContent due to item num is zero");
478             return;
479         }
480 
481         switch (mainAlignment_) {
482             case WrapAlignment::START: {
483                 if ((direction_ == WrapDirection::HORIZONTAL && !isLeftToRight_) ||
484                     (direction_ == WrapDirection::HORIZONTAL_REVERSE && isLeftToRight_) ||
485                     (direction_ == WrapDirection::VERTICAL_REVERSE)) {
486                     PositionedItem(0.0, content, accumulateOffset + GetItemMainOffset(currentMainSpaceLength),
487                         content.crossLength_);
488                 } else {
489                     PositionedItem(0.0, content, accumulateOffset, content.crossLength_);
490                 }
491                 break;
492             }
493             case WrapAlignment::END: {
494                 if ((direction_ == WrapDirection::HORIZONTAL && !isLeftToRight_) ||
495                     (direction_ == WrapDirection::HORIZONTAL_REVERSE && isLeftToRight_) ||
496                     direction_ == WrapDirection::VERTICAL_REVERSE) {
497                     PositionedItem(0.0, content, accumulateOffset, content.crossLength_);
498                 } else {
499                     PositionedItem(0.0, content, accumulateOffset + GetItemMainOffset(currentMainSpaceLength),
500                         content.crossLength_);
501                 }
502                 break;
503             }
504             case WrapAlignment::CENTER: {
505                 // divided the space by two
506                 PositionedItem(0.0, content, accumulateOffset + GetItemMainOffset(currentMainSpaceLength / 2),
507                     content.crossLength_);
508                 break;
509             }
510             case WrapAlignment::SPACE_BETWEEN: {
511                 double betweenSpace = (itemNum - 1 == 0) ? 0.0 : currentMainSpaceLength / (itemNum - 1);
512                 PositionedItem(betweenSpace, content, accumulateOffset, content.crossLength_);
513                 break;
514             }
515             case WrapAlignment::SPACE_AROUND: {
516                 double itemMainSpace = currentMainSpaceLength / itemNum;
517                 PositionedItem(itemMainSpace, content, accumulateOffset + GetItemMainOffset(itemMainSpace / 2),
518                     content.crossLength_);
519                 break;
520             }
521             case WrapAlignment::SPACE_EVENLY: {
522                 double itemMainSpace = currentMainSpaceLength / (itemNum + 1);
523                 PositionedItem(
524                     itemMainSpace, content, accumulateOffset + GetItemMainOffset(itemMainSpace), content.crossLength_);
525                 break;
526             }
527             default: {
528                 LOGE("Wrap::mainAlignment setting error. Now using START");
529                 PositionedItem(0.0, content, accumulateOffset, content.crossLength_);
530                 break;
531             }
532         }
533         auto contentSpace = NormalizeToPx(contentSpace_);
534         startItemIndex += itemNum;
535         accumulateOffset += betweenPosition;
536         accumulateOffset += (direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE)
537                                 ? Offset(0.0, content.crossLength_ + contentSpace)
538                                 : Offset(content.crossLength_ + contentSpace, 0.0);
539     }
540 }
541 
GetItemMainOffset(double mainSpace) const542 Offset RenderWrap::GetItemMainOffset(double mainSpace) const
543 {
544     // calculate the offset of each item in content
545     if (direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE) {
546         return Offset(mainSpace, 0.0);
547     } else {
548         return Offset(0.0, mainSpace);
549     }
550 }
551 
PositionedItem(double betweenSpace,const ContentInfo & content,const Offset & position,double totalCrossSpace) const552 void RenderWrap::PositionedItem(
553     double betweenSpace, const ContentInfo& content, const Offset& position, double totalCrossSpace) const
554 {
555     Offset itemPositionOffset;
556     // iterate every item in content
557     for (const auto& item : content.itemList_) {
558         switch (crossAlignment_) {
559             case WrapAlignment::START: {
560                 if ((direction_ == WrapDirection::VERTICAL && !isLeftToRight_) ||
561                     (direction_ == WrapDirection::VERTICAL_REVERSE && !isLeftToRight_)) {
562                     HandleEndAlignment(totalCrossSpace, item, position, betweenSpace, itemPositionOffset);
563                 } else {
564                     HandleStartAlignment(item, position, betweenSpace, itemPositionOffset);
565                 }
566                 break;
567             }
568             case WrapAlignment::STRETCH: {
569                 PlaceItemAndLog(item, position + itemPositionOffset, "STRETCH");
570                 // stretch the component in wrap
571                 LayoutParam layoutParam;
572                 auto spacing = NormalizeToPx(spacing_);
573                 if (direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE) {
574                     itemPositionOffset += Offset(item->GetLayoutSize().Width() + betweenSpace + spacing, 0.0);
575                     layoutParam.SetFixedSize(Size(item->GetLayoutSize().Width(), totalCrossSpace));
576                 } else {
577                     itemPositionOffset += Offset(0.0, item->GetLayoutSize().Height() + betweenSpace + spacing);
578                     layoutParam.SetFixedSize(Size(totalCrossSpace, item->GetLayoutSize().Height()));
579                 }
580                 item->Layout(layoutParam);
581                 break;
582             }
583             case WrapAlignment::END: {
584                 if ((direction_ == WrapDirection::VERTICAL && !isLeftToRight_) ||
585                     (direction_ == WrapDirection::VERTICAL_REVERSE && !isLeftToRight_)) {
586                     HandleStartAlignment(item, position, betweenSpace, itemPositionOffset);
587                 } else {
588                     HandleEndAlignment(totalCrossSpace, item, position, betweenSpace, itemPositionOffset);
589                 }
590                 break;
591             }
592             case WrapAlignment::CENTER: {
593                 // divide the space by two
594                 HandleCenterAlignment(totalCrossSpace, item, position, betweenSpace, itemPositionOffset);
595                 break;
596             }
597             case WrapAlignment::BASELINE: {
598                 if (direction_ == WrapDirection::VERTICAL || direction_ == WrapDirection::VERTICAL_REVERSE) {
599                     if (isLeftToRight_) {
600                         HandleStartAlignment(item, position, betweenSpace, itemPositionOffset);
601                     } else {
602                         HandleEndAlignment(totalCrossSpace, item, position, betweenSpace, itemPositionOffset);
603                     }
604                 } else {
605                     HandleBaselineAlignment(
606                         content.maxBaselineDistance, item, position, betweenSpace, itemPositionOffset);
607                 }
608                 break;
609             }
610             default: {
611                 LOGW("Wrap::crossAlignment setting error. Now using START");
612                 if ((direction_ == WrapDirection::VERTICAL && !isLeftToRight_) ||
613                     (direction_ == WrapDirection::VERTICAL_REVERSE && !isLeftToRight_)) {
614                     HandleEndAlignment(totalCrossSpace, item, position, betweenSpace, itemPositionOffset);
615                 } else {
616                     HandleStartAlignment(item, position, betweenSpace, itemPositionOffset);
617                 }
618                 break;
619             }
620         }
621     }
622 }
623 
PlaceItemAndLog(const RefPtr<RenderNode> & node,const Offset & position,const std::string & align) const624 void RenderWrap::PlaceItemAndLog(const RefPtr<RenderNode>& node, const Offset& position, const std::string& align) const
625 {
626     node->SetPosition(position);
627 }
628 
HandleCenterAlignment(double totalCrossSpace,const RefPtr<RenderNode> & item,const Offset & position,double betweenSpace,Offset & itemPositionOffset) const629 void RenderWrap::HandleCenterAlignment(double totalCrossSpace, const RefPtr<RenderNode>& item, const Offset& position,
630     double betweenSpace, Offset& itemPositionOffset) const
631 {
632     // itemPositionOffset will change in this function
633     Offset crossOffset;
634     auto spacing = NormalizeToPx(spacing_);
635     if (direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE) {
636         crossOffset = Offset(0.0, (totalCrossSpace - item->GetLayoutSize().Height()) / 2.0);
637         PlaceItemAndLog(item, position + itemPositionOffset + crossOffset, "CENTER");
638         itemPositionOffset += Offset(item->GetLayoutSize().Width() + betweenSpace + spacing, 0.0);
639     } else {
640         crossOffset = Offset((totalCrossSpace - item->GetLayoutSize().Width()) / 2, 0.0);
641         PlaceItemAndLog(item, position + itemPositionOffset + crossOffset, "CENTER");
642         itemPositionOffset += Offset(0.0, item->GetLayoutSize().Height() + betweenSpace + spacing);
643     }
644 }
645 
HandleEndAlignment(double totalCrossSpace,const RefPtr<RenderNode> & item,const Offset & position,double betweenSpace,Offset & itemPositionOffset) const646 void RenderWrap::HandleEndAlignment(double totalCrossSpace, const RefPtr<RenderNode>& item, const Offset& position,
647     double betweenSpace, Offset& itemPositionOffset) const
648 {
649     // itemPositionOffset will change in this function
650     Offset crossOffset;
651     auto spacing = NormalizeToPx(spacing_);
652     if (direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE) {
653         crossOffset = Offset(0.0, totalCrossSpace - item->GetLayoutSize().Height());
654         PlaceItemAndLog(item, position + itemPositionOffset + crossOffset, "END");
655         itemPositionOffset += Offset(item->GetLayoutSize().Width() + betweenSpace + spacing, 0.0);
656     } else {
657         crossOffset = Offset(totalCrossSpace - item->GetLayoutSize().Width(), 0.0);
658         PlaceItemAndLog(item, position + itemPositionOffset + crossOffset, "END");
659         itemPositionOffset += Offset(0.0, item->GetLayoutSize().Height() + betweenSpace + spacing);
660     }
661 }
662 
HandleBaselineAlignment(double totalCrossSpace,const RefPtr<RenderNode> & item,const Offset & position,double betweenSpace,Offset & itemPositionOffset) const663 void RenderWrap::HandleBaselineAlignment(double totalCrossSpace, const RefPtr<RenderNode>& item, const Offset& position,
664     double betweenSpace, Offset& itemPositionOffset) const
665 {
666     Offset crossOffset;
667     auto spacing = NormalizeToPx(spacing_);
668     crossOffset = Offset(0.0, totalCrossSpace - item->GetBaselineDistance(TextBaseline::ALPHABETIC));
669     PlaceItemAndLog(item, position + itemPositionOffset + crossOffset, "Baseline");
670     itemPositionOffset += Offset(item->GetLayoutSize().Width() + betweenSpace + spacing, 0.0);
671 }
672 
HandleStartAlignment(const RefPtr<RenderNode> & item,const Offset & position,double betweenSpace,Offset & itemPositionOffset) const673 void RenderWrap::HandleStartAlignment(
674     const RefPtr<RenderNode>& item, const Offset& position, double betweenSpace, Offset& itemPositionOffset) const
675 {
676     PlaceItemAndLog(item, position + itemPositionOffset, "START");
677     // Decide content offset position
678     auto spacing = NormalizeToPx(spacing_);
679     bool isHorizontal = direction_ == WrapDirection::HORIZONTAL || direction_ == WrapDirection::HORIZONTAL_REVERSE;
680     itemPositionOffset += Offset(isHorizontal ? item->GetLayoutSize().Width() + betweenSpace + spacing : 0.0,
681         isHorizontal ? 0.0 : item->GetLayoutSize().Height() + betweenSpace + spacing);
682 }
683 
ClearRenderObject()684 void RenderWrap::ClearRenderObject()
685 {
686     RenderNode::ClearRenderObject();
687     direction_ = WrapDirection::VERTICAL;
688     alignment_ = WrapAlignment::START;
689     mainAlignment_ = WrapAlignment::START;
690     crossAlignment_ = WrapAlignment::START;
691     spacing_ = Dimension();
692     contentSpace_ = Dimension();
693     mainLengthLimit_ = 0.0;
694     crossLengthLimit_ = 0.0;
695     totalMainLength_ = 0.0;
696     totalCrossLength_ = 0.0;
697 
698     dialogDirection_ = WrapDirection::HORIZONTAL;
699     dialogStretch_ = false;
700     isLeftToRight_ = true;
701 }
702 
MaybeRelease()703 bool RenderWrap::MaybeRelease()
704 {
705     auto context = GetContext().Upgrade();
706     if (context && context->GetRenderFactory() && context->GetRenderFactory()->GetRenderWrapFactory()->Recycle(this)) {
707         ClearRenderObject();
708         return false;
709     }
710     return true;
711 }
712 
713 } // namespace OHOS::Ace