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