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