• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/flex/flex_layout_algorithm.h"
17 
18 #include <algorithm>
19 #include <iterator>
20 
21 #include "base/geometry/axis.h"
22 #include "base/geometry/dimension.h"
23 #include "base/geometry/ng/offset_t.h"
24 #include "base/log/ace_trace.h"
25 #include "base/utils/utils.h"
26 #include "core/common/ace_application_info.h"
27 #include "core/components/common/layout/constants.h"
28 #include "core/components_ng/layout/layout_wrapper.h"
29 #include "core/components_ng/pattern/flex/flex_layout_property.h"
30 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
31 #include "core/components_ng/pattern/navigation/navigation_group_node.h"
32 #include "core/components_ng/property/layout_constraint.h"
33 #include "core/components_ng/property/measure_property.h"
34 #include "core/components_ng/property/measure_utils.h"
35 #include "core/components_v2/inspector/inspector_constants.h"
36 
37 namespace OHOS::Ace::NG {
38 
39 namespace {
40 /**
41  * Get the main axis direction based on direction.
42  */
FlipAxis(FlexDirection direction)43 FlexDirection FlipAxis(FlexDirection direction)
44 {
45     if (direction == FlexDirection::ROW || direction == FlexDirection::ROW_REVERSE) {
46         return FlexDirection::COLUMN;
47     }
48     return FlexDirection::ROW;
49 }
50 
51 /**
52  * Determine whether to start the layout from the upper left corner
53  */
IsStartTopLeft(FlexDirection direction,TextDirection textDirection)54 bool IsStartTopLeft(FlexDirection direction, TextDirection textDirection)
55 {
56     switch (direction) {
57         case FlexDirection::ROW:
58             return textDirection == TextDirection::LTR;
59         case FlexDirection::ROW_REVERSE:
60             return textDirection == TextDirection::RTL;
61         case FlexDirection::COLUMN:
62             return true;
63         case FlexDirection::COLUMN_REVERSE:
64             return false;
65         default:
66             return true;
67     }
68 }
69 
GetCrossAxisSizeHelper(const SizeF & size,FlexDirection direction)70 float GetCrossAxisSizeHelper(const SizeF& size, FlexDirection direction)
71 {
72     if (direction == FlexDirection::ROW || direction == FlexDirection::ROW_REVERSE) {
73         return size.Height();
74     }
75     return size.Width();
76 }
77 
GetMainAxisSizeHelper(const SizeF & size,FlexDirection direction)78 float GetMainAxisSizeHelper(const SizeF& size, FlexDirection direction)
79 {
80     if (direction == FlexDirection::ROW || direction == FlexDirection::ROW_REVERSE) {
81         return size.Width();
82     }
83     return size.Height();
84 }
85 
GetCalcSizeHelper(float mainAxisSize,float crossAxisSize,FlexDirection direction)86 OptionalSizeF GetCalcSizeHelper(float mainAxisSize, float crossAxisSize, FlexDirection direction)
87 {
88     OptionalSizeF size;
89     if (direction == FlexDirection::ROW || direction == FlexDirection::ROW_REVERSE) {
90         size.SetWidth(mainAxisSize);
91         size.SetHeight(crossAxisSize);
92     } else {
93         size.SetHeight(mainAxisSize);
94         size.SetWidth(crossAxisSize);
95     }
96     return size;
97 }
98 
IsHorizontal(FlexDirection direction)99 bool IsHorizontal(FlexDirection direction)
100 {
101     return direction == FlexDirection::ROW || direction == FlexDirection::ROW_REVERSE;
102 }
103 
UpdateChildLayoutConstrainByFlexBasis(FlexDirection direction,const RefPtr<LayoutWrapper> & child,LayoutConstraintF & layoutConstraint)104 void UpdateChildLayoutConstrainByFlexBasis(
105     FlexDirection direction, const RefPtr<LayoutWrapper>& child, LayoutConstraintF& layoutConstraint)
106 {
107     const auto& flexItemProperty = child->GetLayoutProperty()->GetFlexItemProperty();
108     CHECK_NULL_VOID_NOLOG(flexItemProperty);
109     const auto& flexBasis = flexItemProperty->GetFlexBasis();
110     CHECK_NULL_VOID_NOLOG(flexBasis);
111     if (flexBasis->Unit() == DimensionUnit::AUTO || !flexBasis->IsValid()) {
112         return;
113     }
114     if (child->GetLayoutProperty()->GetCalcLayoutConstraint()) {
115         auto selfIdealSize = child->GetLayoutProperty()->GetCalcLayoutConstraint()->selfIdealSize;
116         if (child->GetHostTag() == V2::BLANK_ETS_TAG && selfIdealSize.has_value()) {
117             if (IsHorizontal(direction) && selfIdealSize->Width().has_value() &&
118                 selfIdealSize->Width()->GetDimension().ConvertToPx() > flexBasis->ConvertToPx()) {
119                 return;
120             } else if (!IsHorizontal(direction) && selfIdealSize->Height().has_value() &&
121                     selfIdealSize->Height()->GetDimension().ConvertToPx() > flexBasis->ConvertToPx()) {
122                 return;
123             }
124         }
125     }
126     if (direction == FlexDirection::ROW || direction == FlexDirection::ROW_REVERSE) {
127         layoutConstraint.selfIdealSize.SetWidth(flexBasis->ConvertToPx());
128     } else {
129         layoutConstraint.selfIdealSize.SetHeight(flexBasis->ConvertToPx());
130     }
131 }
132 
133 } // namespace
134 
GetChildMainAxisSize(const RefPtr<LayoutWrapper> & layoutWrapper) const135 float FlexLayoutAlgorithm::GetChildMainAxisSize(const RefPtr<LayoutWrapper>& layoutWrapper) const
136 {
137     float size = 0.0f;
138     CHECK_NULL_RETURN_NOLOG(layoutWrapper, size);
139     return GetMainAxisSizeHelper(layoutWrapper->GetGeometryNode()->GetMarginFrameSize(), direction_);
140 }
141 
GetChildCrossAxisSize(const RefPtr<LayoutWrapper> & layoutWrapper) const142 float FlexLayoutAlgorithm::GetChildCrossAxisSize(const RefPtr<LayoutWrapper>& layoutWrapper) const
143 {
144     CHECK_NULL_RETURN_NOLOG(layoutWrapper, 0.0f);
145     return GetCrossAxisSizeHelper(layoutWrapper->GetGeometryNode()->GetMarginFrameSize(), direction_);
146 }
147 
GetSelfCrossAxisSize(const RefPtr<LayoutWrapper> & layoutWrapper) const148 float FlexLayoutAlgorithm::GetSelfCrossAxisSize(const RefPtr<LayoutWrapper>& layoutWrapper) const
149 {
150     CHECK_NULL_RETURN_NOLOG(layoutWrapper, 0.0f);
151     return GetCrossAxisSizeHelper(layoutWrapper->GetGeometryNode()->GetFrameSize(), direction_);
152 }
153 
CheckSizeValidity(const RefPtr<LayoutWrapper> & layoutWrapper)154 void FlexLayoutAlgorithm::CheckSizeValidity(const RefPtr<LayoutWrapper>& layoutWrapper)
155 {
156     if (layoutWrapper->GetHostNode()->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE) ==
157         VisibleType::GONE) {
158         return;
159     }
160     ++validSizeCount_;
161 }
162 
163 /**
164  * Check and record baseline distance.
165  */
CheckBaselineProperties(const RefPtr<LayoutWrapper> & layoutWrapper)166 void FlexLayoutAlgorithm::CheckBaselineProperties(const RefPtr<LayoutWrapper>& layoutWrapper)
167 {
168     bool isChildBaselineAlign = false;
169     const auto& flexItemProperty = layoutWrapper->GetLayoutProperty()->GetFlexItemProperty();
170     isChildBaselineAlign =
171         flexItemProperty ? flexItemProperty->GetAlignSelf().value_or(crossAxisAlign_) == FlexAlign::BASELINE : false;
172     if (crossAxisAlign_ == FlexAlign::BASELINE || isChildBaselineAlign) {
173         float distance = layoutWrapper->GetBaselineDistance();
174         baselineProperties_.maxBaselineDistance = std::max(baselineProperties_.maxBaselineDistance, distance);
175         baselineProperties_.maxDistanceAboveBaseline = std::max(baselineProperties_.maxDistanceAboveBaseline, distance);
176         baselineProperties_.maxDistanceBelowBaseline =
177             std::max(baselineProperties_.maxDistanceBelowBaseline, GetSelfCrossAxisSize(layoutWrapper) - distance);
178         if (crossAxisAlign_ == FlexAlign::BASELINE) {
179             crossAxisSize_ =
180                 baselineProperties_.maxDistanceAboveBaseline + baselineProperties_.maxDistanceBelowBaseline;
181         }
182     }
183 }
184 
185 /**
186  * Initialize the FlexLayoutAlgorithm property.
187  */
InitFlexProperties(LayoutWrapper * layoutWrapper)188 void FlexLayoutAlgorithm::InitFlexProperties(LayoutWrapper* layoutWrapper)
189 {
190     mainAxisSize_ = 0.0f;
191     crossAxisSize_ = 0.0f;
192     allocatedSize_ = 0.0f;
193     selfIdealCrossAxisSize_ = -1.0f;
194     validSizeCount_ = 0;
195     realSize_.Reset();
196     isInfiniteLayout_ = false;
197     auto layoutProperty = AceType::DynamicCast<FlexLayoutProperty>(layoutWrapper->GetLayoutProperty());
198     CHECK_NULL_VOID(layoutProperty);
199     space_ = static_cast<float>(layoutProperty->GetSpaceValue({}).ConvertToPx());
200     direction_ = layoutProperty->GetFlexDirection().value_or(FlexDirection::ROW);
201     mainAxisAlign_ = layoutProperty->GetMainAxisAlignValue(FlexAlign::FLEX_START);
202     secondaryMeasureList_.clear();
203     crossAxisAlign_ =
204         layoutProperty->GetCrossAxisAlignValue(isLinearLayoutFeature_ ? FlexAlign::CENTER : FlexAlign::FLEX_START);
205     baselineProperties_.Reset();
206     textDir_ = layoutProperty->GetLayoutDirection();
207     if (textDir_ == TextDirection::AUTO) {
208         textDir_ = AceApplicationInfo::GetInstance().IsRightToLeft() ? TextDirection::RTL : TextDirection::LTR;
209     }
210     /**
211      * FlexLayoutAlgorithm, as the parent class, should not handle the special logic of the subclass
212      * LinearLayout.
213      */
214     if (isLinearLayoutFeature_) {
215         bool isVertical = DynamicCast<LinearLayoutProperty>(layoutWrapper->GetLayoutProperty())->IsVertical();
216         direction_ = isVertical ? FlexDirection::COLUMN : FlexDirection::ROW;
217     }
218 }
219 
TravelChildrenFlexProps(LayoutWrapper * layoutWrapper,const SizeF & realSize)220 void FlexLayoutAlgorithm::TravelChildrenFlexProps(LayoutWrapper* layoutWrapper, const SizeF& realSize)
221 {
222     if (!magicNodes_.empty()) {
223         LOGD("second measure feature, only update child layout constraint");
224         const auto& childLayoutConstraint = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
225         for (auto& [index, children] : magicNodes_) {
226             for (auto& item : children) {
227                 item.layoutConstraint = childLayoutConstraint;
228             }
229         }
230         return;
231     }
232     maxDisplayPriority_ = 0;
233     totalFlexWeight_ = 0.0f;
234     outOfLayoutChildren_.clear();
235     magicNodes_.clear();
236     magicNodeWeights_.clear();
237     const auto& layoutProperty = layoutWrapper->GetLayoutProperty();
238     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
239     auto childLayoutConstraint = layoutProperty->CreateChildConstraint();
240     for (const auto& child : children) {
241         if (child->IsOutOfLayout()) {
242             outOfLayoutChildren_.emplace_back(child);
243             continue;
244         }
245         const auto& childLayoutProperty = child->GetLayoutProperty();
246         const auto& childMagicItemProperty = childLayoutProperty->GetMagicItemProperty();
247         const auto& childFlexItemProperty = childLayoutProperty->GetFlexItemProperty();
248         MagicLayoutNode node;
249         node.layoutWrapper = child;
250         node.layoutConstraint = childLayoutConstraint;
251 
252         if (child->GetHostNode()->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::GONE) {
253             node.layoutConstraint.selfIdealSize = OptionalSize<float>(0.0f, 0.0f);
254             continue;
255         }
256         int32_t childDisplayPriority = 1;
257         float childLayoutWeight = 0.0f;
258         if (childMagicItemProperty) {
259             childLayoutWeight = childMagicItemProperty->GetLayoutWeight().value_or(0.0f);
260         }
261         if (childFlexItemProperty) {
262             childDisplayPriority = childFlexItemProperty->GetDisplayIndex().value_or(1);
263         }
264         if (!magicNodes_.count(childDisplayPriority)) {
265             magicNodes_.insert(
266                 std::map<int32_t, std::list<MagicLayoutNode>>::value_type(childDisplayPriority, { node }));
267             if (GreatNotEqual(childLayoutWeight, 0.0f)) {
268                 magicNodeWeights_.insert(std::map<int32_t, float>::value_type(childDisplayPriority, childLayoutWeight));
269             }
270         } else {
271             magicNodes_[childDisplayPriority].emplace_back(node);
272             if (GreatNotEqual(childLayoutWeight, 0.0f)) {
273                 magicNodeWeights_[childDisplayPriority] += childLayoutWeight;
274             }
275         }
276         totalFlexWeight_ += GreatNotEqual(childLayoutWeight, 0.0f) ? childLayoutWeight : 0.0f;
277         maxDisplayPriority_ = std::max(childDisplayPriority, maxDisplayPriority_);
278     }
279 }
280 
UpdateAllocatedSize(const RefPtr<LayoutWrapper> & childLayoutWrapper,float & crossAxisSize)281 void FlexLayoutAlgorithm::UpdateAllocatedSize(const RefPtr<LayoutWrapper>& childLayoutWrapper, float& crossAxisSize)
282 {
283     float mainAxisSize = GetChildMainAxisSize(childLayoutWrapper);
284     if (GreaterOrEqualToInfinity(mainAxisSize)) {
285         mainAxisSize = 0.0f;
286     }
287     crossAxisSize = std::max(crossAxisSize, GetChildCrossAxisSize(childLayoutWrapper));
288     allocatedSize_ += mainAxisSize;
289     allocatedSize_ += space_;
290 }
291 
MeasureOutOfLayoutChildren(LayoutWrapper * layoutWrapper)292 void FlexLayoutAlgorithm::MeasureOutOfLayoutChildren(LayoutWrapper* layoutWrapper)
293 {
294     const auto& layoutConstrain = layoutWrapper->GetLayoutProperty()->CreateChildConstraint();
295     for (const auto& child : outOfLayoutChildren_) {
296         child->Measure(layoutConstrain);
297     }
298 }
299 
MeasureAndCleanMagicNodes(FlexItemProperties & flexItemProperties)300 void FlexLayoutAlgorithm::MeasureAndCleanMagicNodes(FlexItemProperties& flexItemProperties)
301 {
302     if (GreatNotEqual(totalFlexWeight_, 0.0f) && !isInfiniteLayout_) {
303         auto newTotalFlexWeight = totalFlexWeight_;
304         /**
305          * The child elements with layoutWeight=0 are measured first.
306          * Then, measure the sub elements of layoutWeight>1 based on the remaining space.
307          * If the total main axis size of the element is larger than the main axis size of Flex, the lower priority
308          * element will be deleted.
309          */
310         auto firstLoopIter = magicNodes_.rbegin();
311         auto loopIter = firstLoopIter;
312         bool outOfDisplay = false;
313         while (loopIter != magicNodes_.rend()) {
314             auto& childList = loopIter->second;
315             float crossAxisSize = crossAxisSize_;
316             for (auto& child : childList) {
317                 if (!outOfDisplay) {
318                     const auto& childLayoutWrapper = child.layoutWrapper;
319                     float childLayoutWeight = 0.0f;
320                     const auto& childMagicItemProperty =
321                         childLayoutWrapper->GetLayoutProperty()->GetMagicItemProperty();
322                     if (childMagicItemProperty) {
323                         childLayoutWeight = childMagicItemProperty->GetLayoutWeight().value_or(0.0f);
324                     }
325                     if (LessOrEqual(childLayoutWeight, 0.0f)) {
326                         const auto& childLayoutProperty = childLayoutWrapper->GetLayoutProperty();
327                         if (childLayoutProperty->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::GONE) {
328                             continue;
329                         }
330                         childLayoutWrapper->Measure(child.layoutConstraint);
331                         UpdateAllocatedSize(childLayoutWrapper, crossAxisSize);
332                         CheckSizeValidity(childLayoutWrapper);
333                         CheckBaselineProperties(childLayoutWrapper);
334                     } else {
335                         allocatedSize_ += space_;
336                     }
337                 } else {
338                     child.layoutWrapper->SetActive(false);
339                     child.layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF());
340                 }
341             }
342             if (outOfDisplay) {
343                 ++loopIter;
344                 continue;
345             }
346             /**
347              * The main axis size of the element with layoutWeight of 0 is larger than the Flex main axis size
348              */
349             if (allocatedSize_ - space_ > mainAxisSize_ && magicNodes_.size() > 1) {
350                 for (const auto& child : childList) {
351                     allocatedSize_ -= GetChildMainAxisSize(child.layoutWrapper);
352                     allocatedSize_ -= space_;
353                     // TODO: reset size validity and baseline properties.
354                     child.layoutWrapper->SetActive(false);
355                     child.layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF());
356                 }
357                 outOfDisplay = true;
358                 firstLoopIter = loopIter++;
359             } else {
360                 crossAxisSize_ = crossAxisSize;
361                 firstLoopIter = ++loopIter;
362             }
363         }
364         allocatedSize_ -= space_;
365         auto remainedMainAxisSize = mainAxisSize_ - allocatedSize_;
366         auto spacePerWeight = remainedMainAxisSize / newTotalFlexWeight;
367         auto secondIterLoop = magicNodes_.rbegin();
368         while (secondIterLoop != firstLoopIter) {
369             auto& childList = secondIterLoop->second;
370             bool isExceed = false;
371             for (auto& child : childList) {
372                 auto childLayoutWrapper = child.layoutWrapper;
373                 auto& childConstraint = child.layoutConstraint;
374                 float childLayoutWeight = 0.0f;
375                 const auto& childMagicItemProperty = childLayoutWrapper->GetLayoutProperty()->GetMagicItemProperty();
376                 if (childMagicItemProperty) {
377                     childLayoutWeight = childMagicItemProperty->GetLayoutWeight().value_or(0.0f);
378                 }
379                 if (LessOrEqual(childLayoutWeight, 0.0)) {
380                     continue;
381                 }
382                 float childCalcSize = std::max(spacePerWeight * childLayoutWeight, 0.0f);
383                 if (GetMainAxisSizeHelper(childConstraint.minSize, direction_) > childCalcSize) {
384                     isExceed = true;
385                 }
386                 UpdateLayoutConstraintOnMainAxis(childConstraint, childCalcSize);
387             }
388             if (isExceed) {
389                 if (magicNodes_.size() <= 1) {
390                     break;
391                 }
392                 isExceed = true;
393                 auto& lowPriorityChildList = magicNodes_.begin()->second;
394                 for (const auto& child : lowPriorityChildList) {
395                     allocatedSize_ -= GetChildMainAxisSize(child.layoutWrapper);
396                     allocatedSize_ -= space_;
397                     child.layoutWrapper->SetActive(false);
398                     child.layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF());
399                 }
400                 newTotalFlexWeight -= magicNodeWeights_[magicNodes_.begin()->first];
401                 remainedMainAxisSize = mainAxisSize_ - allocatedSize_;
402                 spacePerWeight = remainedMainAxisSize / newTotalFlexWeight;
403                 isExceed = false;
404                 magicNodes_.erase(magicNodes_.begin());
405                 secondIterLoop = magicNodes_.rbegin();
406             } else {
407                 secondIterLoop++;
408             }
409         }
410         auto iter = magicNodes_.rbegin();
411         while (iter != magicNodes_.rend()) {
412             auto& childList = iter->second;
413             for (auto& child : childList) {
414                 auto childLayoutWrapper = child.layoutWrapper;
415                 if (!childLayoutWrapper->IsActive()) {
416                     continue;
417                 }
418                 float childLayoutWeight = 0.0f;
419                 const auto& childMagicItemProperty = childLayoutWrapper->GetLayoutProperty()->GetMagicItemProperty();
420                 if (childMagicItemProperty) {
421                     childLayoutWeight = childMagicItemProperty->GetLayoutWeight().value_or(0.0f);
422                 }
423                 secondaryMeasureList_.emplace_back(child);
424                 if (LessOrEqual(childLayoutWeight, 0.0)) {
425                     continue;
426                 }
427                 childLayoutWrapper->Measure(child.layoutConstraint);
428                 UpdateAllocatedSize(childLayoutWrapper, crossAxisSize_);
429                 CheckSizeValidity(childLayoutWrapper);
430                 CheckBaselineProperties(childLayoutWrapper);
431             }
432             iter++;
433         }
434     } else if (GreatNotEqual(maxDisplayPriority_, 1) && !isInfiniteLayout_) {
435         bool outOfDisplay = false;
436         auto iter = magicNodes_.rbegin();
437         while (iter != magicNodes_.rend()) {
438             auto childList = iter->second;
439             if (outOfDisplay) {
440                 for (auto& child : childList) {
441                     child.layoutWrapper->SetActive(false);
442                     child.layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF());
443                 }
444                 ++iter;
445                 continue;
446             }
447             float crossAxisSize = crossAxisSize_;
448             for (auto& child : childList) {
449                 const auto& childLayoutWrapper = child.layoutWrapper;
450                 auto childLayoutConstraint = child.layoutConstraint;
451                 UpdateChildLayoutConstrainByFlexBasis(direction_, childLayoutWrapper, childLayoutConstraint);
452                 childLayoutWrapper->Measure(childLayoutConstraint);
453                 UpdateAllocatedSize(childLayoutWrapper, crossAxisSize);
454                 CheckSizeValidity(childLayoutWrapper);
455                 CheckBaselineProperties(childLayoutWrapper);
456                 const auto& flexItemProperty = childLayoutWrapper->GetLayoutProperty()->GetFlexItemProperty();
457                 if (flexItemProperty && GreatNotEqual(flexItemProperty->GetFlexGrow().value_or(0.0f), 0.0f)) {
458                     flexItemProperties.totalGrow += flexItemProperty->GetFlexGrowValue();
459                 }
460                 secondaryMeasureList_.emplace_back(child);
461             }
462             if (allocatedSize_ - space_ > mainAxisSize_) {
463                 outOfDisplay = true;
464                 for (auto& child : childList) {
465                     allocatedSize_ -= GetChildMainAxisSize(child.layoutWrapper);
466                     allocatedSize_ -= space_;
467                     child.layoutWrapper->SetActive(false);
468                     child.layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF());
469                     const auto& flexItemProperty = child.layoutWrapper->GetLayoutProperty()->GetFlexItemProperty();
470                     if (flexItemProperty && GreatNotEqual(flexItemProperty->GetFlexGrow().value_or(0.0f), 0.0f)) {
471                         flexItemProperties.totalGrow -= flexItemProperty->GetFlexGrowValue();
472                     }
473                     secondaryMeasureList_.pop_back();
474                 }
475             } else {
476                 crossAxisSize_ = crossAxisSize;
477             }
478             ++iter;
479         }
480     } else {
481         auto iter = magicNodes_.rbegin();
482         while (iter != magicNodes_.rend()) {
483             auto childList = iter->second;
484             for (auto& child : childList) {
485                 const auto& childLayoutWrapper = child.layoutWrapper;
486                 UpdateChildLayoutConstrainByFlexBasis(direction_, childLayoutWrapper, child.layoutConstraint);
487                 childLayoutWrapper->Measure(child.layoutConstraint);
488                 UpdateAllocatedSize(childLayoutWrapper, crossAxisSize_);
489                 const auto& childLayoutProperty = childLayoutWrapper->GetLayoutProperty();
490                 if (childLayoutProperty->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::GONE) {
491                     continue;
492                 }
493                 CheckSizeValidity(childLayoutWrapper);
494                 CheckBaselineProperties(childLayoutWrapper);
495                 if (!isInfiniteLayout_) {
496                     const auto& flexItemProperty = childLayoutWrapper->GetLayoutProperty()->GetFlexItemProperty();
497                     float flexShrink = isLinearLayoutFeature_ ? 0.0f : 1.0f;
498                     float flexGrow = 0.0f;
499                     if (flexItemProperty) {
500                         flexShrink = flexItemProperty->GetFlexShrink().value_or(flexShrink);
501                         flexGrow = flexItemProperty->GetFlexGrow().value_or(flexGrow);
502                     }
503                     flexItemProperties.totalGrow += flexGrow;
504                     flexItemProperties.totalShrink += (flexShrink * GetChildMainAxisSize(childLayoutWrapper));
505                 }
506                 secondaryMeasureList_.emplace_back(child);
507             }
508             ++iter;
509         }
510         allocatedSize_ -= space_;
511     }
512 }
513 
SecondaryMeasureByProperty(FlexItemProperties & flexItemProperties,LayoutWrapper * layoutWrapper)514 void FlexLayoutAlgorithm::SecondaryMeasureByProperty(
515     FlexItemProperties& flexItemProperties, LayoutWrapper* layoutWrapper)
516 {
517     float remainSpace = mainAxisSize_ - allocatedSize_;
518     float spacePerFlex = 0;
519     float allocatedFlexSpace = 0;
520     std::function<float(const RefPtr<LayoutWrapper>&)> getFlex;
521     RefPtr<LayoutWrapper> lastChild;
522     if (GreatOrEqual(remainSpace, 0.0f) || GreatNotEqual(maxDisplayPriority_, 1)) {
523         getFlex = [](const RefPtr<LayoutWrapper>& item) -> float {
524             const auto& flexItemProperty = item->GetLayoutProperty()->GetFlexItemProperty();
525             float ret = 0.0f;
526             if (flexItemProperty) {
527                 ret = flexItemProperty->GetFlexGrow().value_or(ret);
528                 /**
529                  * handle non positive flex grow.
530                  */
531                 if (NonPositive(ret)) {
532                     ret = 0.0f;
533                 }
534             }
535             return ret;
536         };
537         spacePerFlex = NearZero(flexItemProperties.totalGrow) ? 0.0f : remainSpace / flexItemProperties.totalGrow;
538         lastChild = flexItemProperties.lastGrowChild;
539     } else {
540         getFlex = [isLinearLayoutFeature = isLinearLayoutFeature_](const RefPtr<LayoutWrapper>& item) -> float {
541             const auto& flexItemProperty = item->GetLayoutProperty()->GetFlexItemProperty();
542             float ret = isLinearLayoutFeature ? 0.0f : 1.0f;
543             if (flexItemProperty) {
544                 ret = flexItemProperty->GetFlexShrink().value_or(ret);
545                 /**
546                  * handle non positive flex shrink.
547                  */
548                 if (NonPositive(ret)) {
549                     ret = 0.0f;
550                 }
551             }
552             return ret;
553         };
554         spacePerFlex = NearZero(flexItemProperties.totalShrink) ? 0.0f : remainSpace / flexItemProperties.totalShrink;
555         lastChild = flexItemProperties.lastShrinkChild;
556     }
557     /**
558      * get the real cross axis size.
559      */
560     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
561     auto paddingLeft = padding.left.value_or(0.0f);
562     auto paddingRight = padding.right.value_or(0.0f);
563     auto paddingTop = padding.top.value_or(0.0f);
564     auto paddingBottom = padding.bottom.value_or(0.0f);
565     auto crossAxisSize = crossAxisSize_;
566     if (NonNegative(selfIdealCrossAxisSize_)) {
567         if (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE) {
568             crossAxisSize = selfIdealCrossAxisSize_ - paddingTop - paddingBottom;
569         } else {
570             crossAxisSize = selfIdealCrossAxisSize_ - paddingLeft - paddingRight;
571         }
572     }
573     if (Negative(crossAxisSize)) {
574         crossAxisSize = 0.0f;
575     }
576     auto iter = secondaryMeasureList_.rbegin();
577     while (iter != secondaryMeasureList_.rend()) {
578         auto child = *iter;
579         bool needSecondaryLayout = false;
580         auto childLayoutWrapper = child.layoutWrapper;
581         if (GetSelfAlign(childLayoutWrapper) == FlexAlign::STRETCH) {
582             UpdateLayoutConstraintOnCrossAxis(child.layoutConstraint, crossAxisSize);
583             needSecondaryLayout = true;
584         }
585         if (LessOrEqual(totalFlexWeight_, 0.0f) && !isInfiniteLayout_) {
586             float itemFlex = getFlex(child.layoutWrapper);
587             float flexSize = (child.layoutWrapper == lastChild) ? (remainSpace - allocatedFlexSpace)
588                              : GreatOrEqual(remainSpace, 0.0f) || GreatNotEqual(maxDisplayPriority_, 1)
589                                  ? spacePerFlex * itemFlex
590                                  : spacePerFlex * itemFlex * GetChildMainAxisSize(child.layoutWrapper);
591             if (!NearZero(flexSize)) {
592                 flexSize += GetChildMainAxisSize(childLayoutWrapper);
593                 UpdateLayoutConstraintOnMainAxis(child.layoutConstraint, flexSize);
594                 needSecondaryLayout = true;
595             }
596         }
597         if (needSecondaryLayout) {
598             childLayoutWrapper->Measure(child.layoutConstraint);
599             CheckBaselineProperties(child.layoutWrapper);
600         }
601         ++iter;
602     }
603 }
604 
UpdateLayoutConstraintOnMainAxis(LayoutConstraintF & layoutConstraint,float size)605 void FlexLayoutAlgorithm::UpdateLayoutConstraintOnMainAxis(LayoutConstraintF& layoutConstraint, float size)
606 {
607     if (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE) {
608         layoutConstraint.selfIdealSize.SetWidth(size);
609     } else {
610         layoutConstraint.selfIdealSize.SetHeight(size);
611     }
612 }
613 
UpdateLayoutConstraintOnCrossAxis(LayoutConstraintF & layoutConstraint,float size)614 void FlexLayoutAlgorithm::UpdateLayoutConstraintOnCrossAxis(LayoutConstraintF& layoutConstraint, float size)
615 {
616     OptionalSizeF& selfIdealSize = layoutConstraint.selfIdealSize;
617     if (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE) {
618         selfIdealSize.SetHeight(size);
619     } else {
620         selfIdealSize.SetWidth(size);
621     }
622 }
623 
Measure(LayoutWrapper * layoutWrapper)624 void FlexLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
625 {
626     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
627     /**
628      * Obtain the main axis size and cross axis size based on user setting.
629      */
630     const auto& layoutConstraint = layoutWrapper->GetLayoutProperty()->GetLayoutConstraint();
631     const auto& measureType = layoutWrapper->GetLayoutProperty()->GetMeasureType();
632     InitFlexProperties(layoutWrapper);
633     Axis axis = (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE) ? Axis::HORIZONTAL
634                                                                                                : Axis::VERTICAL;
635     auto realSize = CreateIdealSize(layoutConstraint.value(), axis, measureType).ConvertToSizeT();
636     if (children.empty()) {
637         layoutWrapper->GetGeometryNode()->SetFrameSize(realSize);
638         return;
639     }
640     mainAxisSize_ = GetMainAxisSizeHelper(realSize, direction_);
641     /**
642      * The user has not set the main axis size
643      */
644     isInfiniteLayout_ = false;
645     if (NearEqual(mainAxisSize_, -1.0f)) {
646         mainAxisSize_ = direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE
647                             ? layoutConstraint->maxSize.Width()
648                             : layoutConstraint->maxSize.Height();
649         isInfiniteLayout_ = isLinearLayoutFeature_;
650     }
651     if (!isInfiniteLayout_) {
652         isInfiniteLayout_ = GreaterOrEqualToInfinity(mainAxisSize_);
653     }
654     if (isInfiniteLayout_) {
655         LOGD("The main axis size is not defined or infinity, disallow flex and weight mode");
656     }
657     auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
658     auto horizontalPadding = padding.left.value_or(0.0f) + padding.right.value_or(0.0f);
659     auto verticalPadding = padding.top.value_or(0.0f) + padding.bottom.value_or(0.0f);
660     if (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE) {
661         mainAxisSize_ -= horizontalPadding;
662     } else {
663         mainAxisSize_ -= verticalPadding;
664     }
665     if (Negative(mainAxisSize_)) {
666         mainAxisSize_ = 0.0f;
667     }
668     TravelChildrenFlexProps(layoutWrapper, realSize);
669     if (GreatNotEqual(totalFlexWeight_, 0.0f)) {
670         LOGD("Flex weight only supported in match parent");
671         isInfiniteLayout_ = false;
672     }
673     selfIdealCrossAxisSize_ = GetCrossAxisSizeHelper(realSize, direction_);
674     FlexItemProperties flexItemProperties;
675 
676     /**
677      * first measure
678      */
679     MeasureAndCleanMagicNodes(flexItemProperties);
680 
681     /**
682      * secondary measure
683      */
684     SecondaryMeasureByProperty(flexItemProperties, layoutWrapper);
685 
686     /**
687      *  position property measure.
688      */
689     MeasureOutOfLayoutChildren(layoutWrapper);
690 
691     AdjustTotalAllocatedSize(layoutWrapper);
692 
693     /**
694      * For Row and Column, the main axis size is wrapContent.
695      * And, FlexLayoutAlgorithm, as the parent class, should not handle the special logic of the subclass LinearLayout.
696      */
697     if (isInfiniteLayout_) {
698         mainAxisSize_ = allocatedSize_;
699     }
700 
701     auto finialMainAxisSize = mainAxisSize_;
702     auto finialCrossAxisSize = crossAxisSize_;
703     if (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE) {
704         finialCrossAxisSize += verticalPadding;
705         finialMainAxisSize += horizontalPadding;
706     } else {
707         finialCrossAxisSize += horizontalPadding;
708         finialMainAxisSize += verticalPadding;
709     }
710     finialMainAxisSize = std::clamp(finialMainAxisSize, GetMainAxisSizeHelper(layoutConstraint->minSize, direction_),
711         GetMainAxisSizeHelper(layoutConstraint->maxSize, direction_));
712     finialCrossAxisSize = std::clamp(finialCrossAxisSize, GetCrossAxisSizeHelper(layoutConstraint->minSize, direction_),
713         GetCrossAxisSizeHelper(layoutConstraint->maxSize, direction_));
714 
715     realSize.UpdateIllegalSizeWithCheck(
716         GetCalcSizeHelper(finialMainAxisSize, finialCrossAxisSize, direction_).ConvertToSizeT());
717     layoutWrapper->GetGeometryNode()->SetFrameSize(realSize);
718 }
719 
AdjustTotalAllocatedSize(LayoutWrapper * layoutWrapper)720 void FlexLayoutAlgorithm::AdjustTotalAllocatedSize(LayoutWrapper* layoutWrapper)
721 {
722     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
723     if (children.empty()) {
724         LOGD("FlexLayoutAlgorithm::GetTotalAllocatedSize, children is empty");
725         allocatedSize_ = 0.0f;
726         return;
727     }
728     allocatedSize_ = 0.0f;
729     allocatedSize_ += space_ * (validSizeCount_ - 1);
730     // space is not valid when mainAxisAlign is SPACE_AROUND/SPACE_BETWEEN/SPACE_EVENLY
731     if (mainAxisAlign_ == FlexAlign::SPACE_AROUND || mainAxisAlign_ == FlexAlign::SPACE_BETWEEN ||
732         mainAxisAlign_ == FlexAlign::SPACE_EVENLY) {
733         allocatedSize_ = 0.0;
734     }
735     for (const auto& child : children) {
736         if (child->IsOutOfLayout() ||
737             child->GetHostNode()->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::GONE) {
738             continue;
739         }
740         allocatedSize_ += GetChildMainAxisSize(child);
741     }
742 }
743 
744 /**
745  * Get cross axis size in stretch.
746  * At this time, the cross axis size has been determined.
747  */
GetStretchCrossAxisLimit() const748 float FlexLayoutAlgorithm::GetStretchCrossAxisLimit() const
749 {
750     float crossAxisLimit = GreatNotEqual(selfIdealCrossAxisSize_, -1.0f) ? selfIdealCrossAxisSize_ : crossAxisSize_;
751     return crossAxisLimit;
752 }
753 
Layout(LayoutWrapper * layoutWrapper)754 void FlexLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
755 {
756     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
757     if (children.empty()) {
758         LOGD("FlexLayoutAlgorithm::Layout, children is empty");
759         return;
760     }
761     auto layoutProperty = AceType::DynamicCast<FlexLayoutProperty>(layoutWrapper->GetLayoutProperty());
762     CHECK_NULL_VOID(layoutProperty);
763     space_ = static_cast<float>(layoutProperty->GetSpaceValue({}).ConvertToPx());
764     direction_ = layoutProperty->GetFlexDirection().value_or(FlexDirection::ROW);
765     mainAxisAlign_ = layoutProperty->GetMainAxisAlignValue(FlexAlign::FLEX_START);
766     crossAxisAlign_ =
767         layoutProperty->GetCrossAxisAlignValue(isLinearLayoutFeature_ ? FlexAlign::CENTER : FlexAlign::FLEX_START);
768     textDir_ = layoutProperty->GetLayoutDirection();
769     if (textDir_ == TextDirection::AUTO) {
770         textDir_ = AceApplicationInfo::GetInstance().IsRightToLeft() ? TextDirection::RTL : TextDirection::LTR;
771     }
772     auto contentSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
773     const auto& padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
774     MinusPaddingToSize(padding, contentSize);
775     mainAxisSize_ = GetMainAxisSizeHelper(contentSize, direction_);
776     crossAxisSize_ = GetCrossAxisSizeHelper(contentSize, direction_);
777     auto paddingOffset = OffsetF(padding.left.value_or(0.0f), padding.top.value_or(0.0f));
778     float remainSpace = std::max(mainAxisSize_ - allocatedSize_, 0.0f);
779     float frontSpace = 0.0f;
780     float betweenSpace = 0.0f;
781     CalculateSpace(remainSpace, frontSpace, betweenSpace);
782     PlaceChildren(layoutWrapper, frontSpace, betweenSpace, paddingOffset);
783 
784     for (auto&& child : children) {
785         child->Layout();
786     }
787 }
788 
CalculateSpace(float remainSpace,float & frontSpace,float & betweenSpace) const789 void FlexLayoutAlgorithm::CalculateSpace(float remainSpace, float& frontSpace, float& betweenSpace) const
790 {
791     switch (mainAxisAlign_) {
792         case FlexAlign::FLEX_START:
793             frontSpace = 0.0f;
794             betweenSpace = space_;
795             break;
796         case FlexAlign::FLEX_END:
797             frontSpace = remainSpace;
798             betweenSpace = space_;
799             break;
800         case FlexAlign::CENTER:
801             frontSpace = remainSpace / 2;
802             betweenSpace = space_;
803             break;
804         case FlexAlign::SPACE_BETWEEN:
805             frontSpace = 0.0f;
806             betweenSpace = validSizeCount_ > 1 ? remainSpace / static_cast<float>(validSizeCount_ - 1) : 0.0f;
807             break;
808         case FlexAlign::SPACE_AROUND:
809             betweenSpace = validSizeCount_ > 0 ? remainSpace / static_cast<float>(validSizeCount_) : 0.0f;
810             frontSpace = betweenSpace / 2;
811             break;
812         case FlexAlign::SPACE_EVENLY:
813             betweenSpace = validSizeCount_ > 0 ? remainSpace / static_cast<float>(validSizeCount_ + 1) : 0.0f;
814             frontSpace = betweenSpace;
815             break;
816         default:
817             break;
818     }
819     LOGD("CalculateSpace end front space is %{public}f, between space is %{public}f, remain space is %{public}f",
820         frontSpace, betweenSpace, remainSpace);
821 }
822 
PlaceChildren(LayoutWrapper * layoutWrapper,float frontSpace,float betweenSpace,const OffsetF & paddingOffset)823 void FlexLayoutAlgorithm::PlaceChildren(
824     LayoutWrapper* layoutWrapper, float frontSpace, float betweenSpace, const OffsetF& paddingOffset)
825 {
826     LOGD("Place children, mainSize %{public}f, crossSize %{public}f, direction %{public}d, frontSpace %{public}f, "
827          "betweenSpace %{public}f",
828         mainAxisSize_, crossAxisSize_, direction_, frontSpace, betweenSpace);
829     float childMainPos = IsStartTopLeft(direction_, textDir_) ? frontSpace : mainAxisSize_ - frontSpace;
830     float childCrossPos = 0.0f;
831     const auto& children = layoutWrapper->GetAllChildrenWithBuild();
832     for (const auto& child : children) {
833         if (child->IsOutOfLayout()) {
834             // adjust by postion property.
835             child->GetGeometryNode()->SetMarginFrameOffset({});
836             child->Layout();
837             continue;
838         }
839         if (child->GetHostNode()->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::GONE) {
840             continue;
841         }
842         auto alignItem = GetSelfAlign(child);
843         auto crossDirection = FlipAxis(direction_);
844         switch (alignItem) {
845             case FlexAlign::FLEX_START:
846             case FlexAlign::FLEX_END:
847                 childCrossPos = (IsStartTopLeft(crossDirection, textDir_) == (alignItem == FlexAlign::FLEX_START))
848                                     ? 0.0f
849                                     : crossAxisSize_ - GetChildCrossAxisSize(child);
850                 break;
851             case FlexAlign::CENTER:
852                 childCrossPos = crossAxisSize_ / 2 - GetChildCrossAxisSize(child) / 2;
853                 break;
854             case FlexAlign::STRETCH:
855                 childCrossPos =
856                     IsStartTopLeft(crossDirection, textDir_) ? 0.0f : crossAxisSize_ - GetChildCrossAxisSize(child);
857                 break;
858             case FlexAlign::BASELINE:
859                 childCrossPos = 0.0;
860                 if (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE) {
861                     float distance = child->GetBaselineDistance();
862                     childCrossPos = baselineProperties_.maxBaselineDistance - distance;
863                 }
864                 break;
865             default:
866                 break;
867         }
868         OffsetF offset;
869         if (direction_ == FlexDirection::ROW || direction_ == FlexDirection::ROW_REVERSE) {
870             offset = OffsetF(childMainPos, childCrossPos);
871         } else {
872             offset = OffsetF(childCrossPos, childMainPos);
873         }
874 
875         if (!IsStartTopLeft(direction_, textDir_)) {
876             if (direction_ != FlexDirection::COLUMN_REVERSE) {
877                 offset.SetX(offset.GetX() - GetChildMainAxisSize(child));
878             } else {
879                 offset.SetY(offset.GetY() - GetChildMainAxisSize(child));
880             }
881             child->GetGeometryNode()->SetMarginFrameOffset(offset + paddingOffset);
882             childMainPos -= GetChildMainAxisSize(child) + betweenSpace;
883         } else {
884             child->GetGeometryNode()->SetMarginFrameOffset(offset + paddingOffset);
885             childMainPos += GetChildMainAxisSize(child) + betweenSpace;
886         }
887         LOGD("Child %{public}s has margin frame offset %{public}s", child->GetHostTag().c_str(),
888             child->GetGeometryNode()->GetMarginFrameOffset().ToString().c_str());
889     }
890 }
891 
GetSelfAlign(const RefPtr<LayoutWrapper> & layoutWrapper) const892 FlexAlign FlexLayoutAlgorithm::GetSelfAlign(const RefPtr<LayoutWrapper>& layoutWrapper) const
893 {
894     const auto& flexItemProperty = layoutWrapper->GetLayoutProperty()->GetFlexItemProperty();
895     return flexItemProperty ? flexItemProperty->GetAlignSelf().value_or(crossAxisAlign_) : crossAxisAlign_;
896 }
897 
898 } // namespace OHOS::Ace::NG
899