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/relative_container/relative_container_layout_algorithm.h"
17 
18 #include "base/geometry/ng/offset_t.h"
19 #include "base/geometry/ng/size_t.h"
20 #include "base/log/ace_trace.h"
21 #include "base/utils/utils.h"
22 #include "core/common/container.h"
23 #include "core/components_ng/layout/layout_algorithm.h"
24 #include "core/components_ng/layout/layout_wrapper.h"
25 #include "core/components_ng/pattern/relative_container/relative_container_layout_property.h"
26 #include "core/components_ng/pattern/relative_container/relative_container_pattern.h"
27 #include "core/components_ng/property/flex_property.h"
28 #include "core/components_ng/property/layout_constraint.h"
29 #include "core/components_ng/property/measure_property.h"
30 #include "core/components_ng/property/measure_utils.h"
31 #include "core/pipeline_ng/pipeline_context.h"
32 
33 namespace OHOS::Ace::NG {
34 namespace {
35 constexpr float DEFAULT_BIAS = 0.5f;
36 constexpr float HALF_MULTIPLY = 0.5f;
37 constexpr float DEFAULT_WEIGHT = 0.0f;
38 const std::string CONCAT_ID_PREFIX = "@concat";
IsAnchorContainer(const std::string & anchor)39 inline bool IsAnchorContainer(const std::string& anchor)
40 {
41     return anchor == "__container__";
42 }
43 
GetOrCreateNodeInspectorId(const RefPtr<FrameNode> & node)44 std::string GetOrCreateNodeInspectorId(const RefPtr<FrameNode>& node)
45 {
46     auto inspectorId = node->GetInspectorIdValue("");
47     if (!node->HasInspectorId()) {
48         inspectorId = CONCAT_ID_PREFIX + node->GetTag() + std::to_string(node->GetId());
49     }
50     return inspectorId;
51 }
52 } // namespace
53 
UpdateTwoAlignValues(TwoAlignedValues & twoAlignedValues,AlignRule alignRule,LineDirection direction)54 void RelativeContainerLayoutAlgorithm::UpdateTwoAlignValues(
55     TwoAlignedValues& twoAlignedValues, AlignRule alignRule, LineDirection direction)
56 {
57     if (twoAlignedValues.first.has_value() && twoAlignedValues.second.has_value()) {
58         return;
59     }
60 
61     auto result = (direction == LineDirection::HORIZONTAL) ? GetHorizontalAnchorValueByAlignRule(alignRule)
62                                                            : GetVerticalAnchorValueByAlignRule(alignRule);
63 
64     if (!twoAlignedValues.first.has_value()) {
65         twoAlignedValues.first = result;
66         return;
67     }
68     if (!twoAlignedValues.second.has_value()) {
69         twoAlignedValues.second = result;
70     }
71 }
72 
DetermineTopologicalOrder(LayoutWrapper * layoutWrapper)73 void RelativeContainerLayoutAlgorithm::DetermineTopologicalOrder(LayoutWrapper* layoutWrapper)
74 {
75     auto relativeContainerLayoutProperty = layoutWrapper->GetLayoutProperty();
76     CHECK_NULL_VOID(relativeContainerLayoutProperty);
77     idNodeMap_.clear();
78     reliedOnMap_.clear();
79     recordOffsetMap_.clear();
80     incomingDegreeMap_.clear();
81     horizontalChainNodeMap_.clear();
82     verticalChainNodeMap_.clear();
83     renderList_.clear();
84     auto layoutConstraint = relativeContainerLayoutProperty->GetLayoutConstraint();
85     CHECK_NULL_VOID(layoutConstraint.has_value());
86     bool idealWidthValid = layoutConstraint.value().selfIdealSize.Width().has_value();
87     bool idealHeightValid = layoutConstraint.value().selfIdealSize.Height().has_value();
88     auto idealSize = CreateIdealSizeByPercentRef(layoutConstraint.value(), Axis::HORIZONTAL, MeasureType::MATCH_PARENT);
89     if (!idealWidthValid) {
90         idealSize.SetWidth(
91             std::min(idealSize.Width().value_or(Infinity<float>()), layoutConstraint.value().maxSize.Width()));
92         idealSize.SetWidth(std::max(idealSize.Width().value_or(0.0f), layoutConstraint.value().minSize.Width()));
93     }
94     if (!idealHeightValid) {
95         idealSize.SetHeight(
96             std::min(idealSize.Height().value_or(Infinity<float>()), layoutConstraint.value().maxSize.Height()));
97         idealSize.SetHeight(std::max(idealSize.Height().value_or(0.0f), layoutConstraint.value().minSize.Height()));
98     }
99     containerSizeWithoutPaddingBorder_ = idealSize.ConvertToSizeT();
100     layoutWrapper->GetGeometryNode()->SetFrameSize(containerSizeWithoutPaddingBorder_);
101     if (relativeContainerLayoutProperty->GetPaddingProperty() ||
102         relativeContainerLayoutProperty->GetBorderWidthProperty()) {
103         padding_ = relativeContainerLayoutProperty->CreatePaddingAndBorder();
104         MinusPaddingToSize(padding_, containerSizeWithoutPaddingBorder_);
105     }
106     CollectNodesById(layoutWrapper);
107     CheckChain(layoutWrapper);
108     GetDependencyRelationship();
109     if (!PreTopologicalLoopDetection()) {
110         const auto& childrenWrappers = layoutWrapper->GetAllChildrenWithBuild();
111         auto constraint = relativeContainerLayoutProperty->CreateChildConstraint();
112         for (const auto& childrenWrapper : childrenWrappers) {
113             childrenWrapper->SetActive(false);
114             constraint.selfIdealSize = OptionalSizeF(0.0f, 0.0f);
115             childrenWrapper->Measure(constraint);
116             constraint.Reset();
117         }
118         return;
119     }
120     TopologicalSort(renderList_);
121 }
122 
UpdateSizeWhenChildrenEmpty(LayoutWrapper * layoutWrapper)123 void RelativeContainerLayoutAlgorithm::UpdateSizeWhenChildrenEmpty(LayoutWrapper* layoutWrapper)
124 {
125     CHECK_NULL_VOID(layoutWrapper);
126     auto relativeContainerLayoutProperty = layoutWrapper->GetLayoutProperty();
127     CHECK_NULL_VOID(relativeContainerLayoutProperty);
128     const auto& layoutConstraint = relativeContainerLayoutProperty->GetLayoutConstraint();
129     CHECK_NULL_VOID(layoutConstraint.has_value());
130     auto idealSize =
131         CreateIdealSizeByPercentRef(layoutConstraint.value(), Axis::FREE, MeasureType::MATCH_PARENT).ConvertToSizeT();
132     layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize.IsPositive() ? idealSize : SizeF(0.0f, 0.0f));
133     const auto& calcLayoutConstraint = relativeContainerLayoutProperty->GetCalcLayoutConstraint();
134     CHECK_NULL_VOID(calcLayoutConstraint);
135     auto selfIdealSize = calcLayoutConstraint->selfIdealSize;
136     padding_ = relativeContainerLayoutProperty->CreatePaddingAndBorder();
137     if (selfIdealSize->Width()->GetDimension().Unit() == DimensionUnit::AUTO) {
138         layoutWrapper->GetGeometryNode()->SetFrameSize(
139             SizeF(padding_.Width(), layoutWrapper->GetGeometryNode()->GetFrameSize().Height()));
140     }
141     if (selfIdealSize->Height()->GetDimension().Unit() == DimensionUnit::AUTO) {
142         layoutWrapper->GetGeometryNode()->SetFrameSize(
143             SizeF(layoutWrapper->GetGeometryNode()->GetFrameSize().Width(), padding_.Height()));
144     }
145 }
146 
CalcHorizontalGuideline(std::optional<CalcSize> & selfIdealSize,float containerHeight,const GuidelineInfo & guidelineInfo)147 void RelativeContainerLayoutAlgorithm::CalcHorizontalGuideline(
148     std::optional<CalcSize>& selfIdealSize, float containerHeight, const GuidelineInfo& guidelineInfo)
149 {
150     ScaleProperty scaleProperty = ScaleProperty::CreateScaleProperty();
151     bool heightAuto = (selfIdealSize->Height()->GetDimension().Unit() == DimensionUnit::AUTO);
152     if (guidelineInfo.start.has_value()) {
153         if ((guidelineInfo.start.value().Unit() == DimensionUnit::PERCENT) && heightAuto) {
154             guidelines_[guidelineInfo.id] = std::make_pair(LineDirection::HORIZONTAL, 0.0f);
155         } else {
156             auto start = ConvertToPx(guidelineInfo.start.value(), scaleProperty, containerHeight);
157             guidelines_[guidelineInfo.id] = std::make_pair(LineDirection::HORIZONTAL, start.value_or(0.0f));
158         }
159     } else if (guidelineInfo.end.has_value()) {
160         if (heightAuto) {
161             guidelines_[guidelineInfo.id] = std::make_pair(LineDirection::HORIZONTAL, 0.0f);
162         } else {
163             auto end = ConvertToPx(guidelineInfo.end.value(), scaleProperty, containerHeight);
164             guidelines_[guidelineInfo.id] =
165                 std::make_pair(LineDirection::HORIZONTAL, (containerHeight - end.value_or(0.0f)));
166         }
167     }
168 }
169 
CalcVerticalGuideline(std::optional<CalcSize> & selfIdealSize,float containerWidth,const GuidelineInfo & guidelineInfo)170 void RelativeContainerLayoutAlgorithm::CalcVerticalGuideline(
171     std::optional<CalcSize>& selfIdealSize, float containerWidth, const GuidelineInfo& guidelineInfo)
172 {
173     ScaleProperty scaleProperty = ScaleProperty::CreateScaleProperty();
174     bool widthAuto = (selfIdealSize->Width()->GetDimension().Unit() == DimensionUnit::AUTO);
175     if (guidelineInfo.start.has_value()) {
176         if ((guidelineInfo.start.value().Unit() == DimensionUnit::PERCENT) && widthAuto) {
177             guidelines_[guidelineInfo.id] = std::make_pair(LineDirection::VERTICAL, 0.0f);
178         } else {
179             auto start = ConvertToPx(guidelineInfo.start.value(), scaleProperty, containerWidth);
180             guidelines_[guidelineInfo.id] = std::make_pair(LineDirection::VERTICAL, start.value_or(0.0f));
181         }
182     } else if (guidelineInfo.end.has_value()) {
183         if (widthAuto) {
184             guidelines_[guidelineInfo.id] = std::make_pair(LineDirection::VERTICAL, 0.0f);
185         } else {
186             auto end = ConvertToPx(guidelineInfo.end.value(), scaleProperty, containerWidth);
187             guidelines_[guidelineInfo.id] =
188                 std::make_pair(LineDirection::VERTICAL, (containerWidth - end.value_or(0.0f)));
189         }
190     }
191 }
192 
CalcBarrier(LayoutWrapper * layoutWrapper)193 void RelativeContainerLayoutAlgorithm::CalcBarrier(LayoutWrapper* layoutWrapper)
194 {
195     CHECK_NULL_VOID(versionGreatorOrEqualToEleven_);
196     CHECK_NULL_VOID(layoutWrapper);
197     auto layoutProperty = DynamicCast<RelativeContainerLayoutProperty>(layoutWrapper->GetLayoutProperty());
198     CHECK_NULL_VOID(layoutProperty);
199 
200     barriers_.clear();
201     if (!layoutProperty->HasBarrier()) {
202         return;
203     }
204 
205     for (const auto& barrierInfo : layoutProperty->GetBarrierValue()) {
206         if (barrierInfo.id.empty() || IsGuideline(barrierInfo.id) ||
207             (idNodeMap_.find(barrierInfo.id) != idNodeMap_.end())) {
208             continue;
209         }
210         auto barrierDirection = BarrierDirectionRtl(barrierInfo.direction);
211         barriers_[barrierInfo.id] = std::make_pair(barrierDirection, barrierInfo.referencedId);
212     }
213 }
214 
CalcGuideline(LayoutWrapper * layoutWrapper)215 void RelativeContainerLayoutAlgorithm::CalcGuideline(LayoutWrapper* layoutWrapper)
216 {
217     CHECK_NULL_VOID(versionGreatorOrEqualToEleven_);
218     CHECK_NULL_VOID(layoutWrapper);
219     auto layoutProperty = DynamicCast<RelativeContainerLayoutProperty>(layoutWrapper->GetLayoutProperty());
220     CHECK_NULL_VOID(layoutProperty);
221     const auto& calcLayoutConstraint = layoutProperty->GetCalcLayoutConstraint();
222     CHECK_NULL_VOID(calcLayoutConstraint);
223     auto calcSelfIdealSize = calcLayoutConstraint->selfIdealSize;
224     const auto& layoutConstraint = layoutProperty->GetLayoutConstraint();
225     CHECK_NULL_VOID(layoutConstraint);
226     auto selfIdealSize = layoutConstraint->selfIdealSize;
227 
228     guidelines_.clear();
229     if (!layoutProperty->HasGuideline()) {
230         return;
231     }
232 
233     for (const auto& guidelineInfo : layoutProperty->GetGuidelineValue()) {
234         if (guidelineInfo.id.empty() || (idNodeMap_.find(guidelineInfo.id) != idNodeMap_.end())) {
235             continue;
236         }
237         if (guidelineInfo.direction == LineDirection::HORIZONTAL) {
238             CalcHorizontalGuideline(calcSelfIdealSize, selfIdealSize.Height().value_or(0), guidelineInfo);
239         } else {
240             CalcVerticalGuideline(calcSelfIdealSize, selfIdealSize.Width().value_or(0), guidelineInfo);
241         }
242     }
243 
244     for (const auto& guideline : guidelines_) {
245         if (guideline.second.first == LineDirection::HORIZONTAL) {
246             recordOffsetMap_[guideline.first] = OffsetF(0.0f, guideline.second.second);
247         } else {
248             recordOffsetMap_[guideline.first] = OffsetF(guideline.second.second, 0.0f);
249         }
250     }
251 }
252 
IsGuideline(const std::string & id)253 bool RelativeContainerLayoutAlgorithm::IsGuideline(const std::string& id)
254 {
255     CHECK_NULL_RETURN(versionGreatorOrEqualToEleven_, false);
256     CHECK_NULL_RETURN(!guidelines_.empty(), false);
257     if (guidelines_.find(id) == guidelines_.end()) {
258         return false;
259     }
260 
261     return true;
262 }
263 
IsBarrier(const std::string & id)264 bool RelativeContainerLayoutAlgorithm::IsBarrier(const std::string& id)
265 {
266     CHECK_NULL_RETURN(versionGreatorOrEqualToEleven_, false);
267     CHECK_NULL_RETURN(!barriers_.empty(), false);
268 
269     if (barriers_.find(id) == barriers_.end()) {
270         return false;
271     }
272 
273     return true;
274 }
275 
GetBarrierRectByReferencedIds(const std::vector<std::string> & referencedIds)276 RelativeContainerLayoutAlgorithm::BarrierRect RelativeContainerLayoutAlgorithm::GetBarrierRectByReferencedIds(
277     const std::vector<std::string>& referencedIds)
278 {
279     BarrierRect barrierRect;
280     for (const auto& nodeName : referencedIds) {
281         if (IsGuideline(nodeName)) {
282             if (guidelines_[nodeName].first == LineDirection::VERTICAL) {
283                 barrierRect.minLeft = std::min(barrierRect.minLeft, recordOffsetMap_[nodeName].GetX());
284                 barrierRect.maxRight = std::max(barrierRect.maxRight, recordOffsetMap_[nodeName].GetX());
285             } else {
286                 barrierRect.minTop = std::min(barrierRect.minTop, recordOffsetMap_[nodeName].GetY());
287                 barrierRect.maxBottom = std::max(barrierRect.maxBottom, recordOffsetMap_[nodeName].GetY());
288             }
289             continue;
290         }
291 
292         if (IsBarrier(nodeName)) {
293             switch (barriers_[nodeName].first) {
294                 case BarrierDirection::LEFT:
295                     barrierRect.minLeft = std::min(barrierRect.minLeft, recordOffsetMap_[nodeName].GetX());
296                     break;
297                 case BarrierDirection::RIGHT:
298                     barrierRect.maxRight = std::max(barrierRect.maxRight, recordOffsetMap_[nodeName].GetX());
299                     break;
300                 case BarrierDirection::TOP:
301                     barrierRect.minTop = std::min(barrierRect.minTop, recordOffsetMap_[nodeName].GetY());
302                     break;
303                 case BarrierDirection::BOTTOM:
304                     barrierRect.maxBottom = std::max(barrierRect.maxBottom, recordOffsetMap_[nodeName].GetY());
305                     break;
306                 default:
307                     break;
308             }
309             continue;
310         }
311         auto it = idNodeMap_.find(nodeName);
312         if (it == idNodeMap_.end()) {
313             continue;
314         }
315 
316         auto childWrapper = it->second.layoutWrapper;
317         if (childWrapper->GetLayoutProperty()->GetVisibility() == VisibleType::GONE) {
318             continue;
319         }
320         auto& recordOffset = recordOffsetMap_[nodeName];
321         barrierRect.minLeft = std::min(barrierRect.minLeft, recordOffset.GetX());
322         barrierRect.minTop = std::min(barrierRect.minTop, recordOffset.GetY());
323         barrierRect.maxRight = std::max(
324             barrierRect.maxRight, recordOffset.GetX() + childWrapper->GetGeometryNode()->GetMarginFrameSize().Width());
325         barrierRect.maxBottom = std::max(barrierRect.maxBottom,
326             recordOffset.GetY() + childWrapper->GetGeometryNode()->GetMarginFrameSize().Height());
327     }
328     return barrierRect;
329 }
330 
MeasureBarrier(const std::string & barrierName)331 void RelativeContainerLayoutAlgorithm::MeasureBarrier(const std::string& barrierName)
332 {
333     BarrierRect barrierRect = GetBarrierRectByReferencedIds(barriers_[barrierName].second);
334     switch (barriers_[barrierName].first) {
335         case BarrierDirection::LEFT:
336             recordOffsetMap_[barrierName] = OffsetF(barrierRect.minLeft, 0.0f);
337             break;
338         case BarrierDirection::RIGHT:
339             recordOffsetMap_[barrierName] = OffsetF(barrierRect.maxRight, 0.0f);
340             break;
341         case BarrierDirection::TOP:
342             recordOffsetMap_[barrierName] = OffsetF(0.0f, barrierRect.minTop);
343             break;
344         case BarrierDirection::BOTTOM:
345             recordOffsetMap_[barrierName] = OffsetF(0.0f, barrierRect.maxBottom);
346             break;
347         default:
348             break;
349     }
350 }
351 
CheckNodeInHorizontalChain(std::string & currentNode,AlignRulesItem & currentAlignRules,std::vector<std::string> & chainNodes,AlignRule & rightAnchor,float & totalChainWeight)352 void RelativeContainerLayoutAlgorithm::CheckNodeInHorizontalChain(std::string& currentNode,
353     AlignRulesItem& currentAlignRules, std::vector<std::string>& chainNodes,
354     AlignRule& rightAnchor, float& totalChainWeight)
355 {
356     std::string nextNode = rightAnchor.anchor;
357     while (idNodeMap_.find(nextNode) != idNodeMap_.end()) {
358         if (currentAlignRules[AlignDirection::RIGHT].horizontal != HorizontalAlign::START) {
359             break;
360         }
361         CHECK_NULL_BREAK(idNodeMap_.find(nextNode) != idNodeMap_.end());
362         auto nextNodeWrapper = idNodeMap_[nextNode].layoutWrapper;
363         const auto& nextNodeFlexItem = nextNodeWrapper->GetLayoutProperty()->GetFlexItemProperty();
364         if (!nextNodeFlexItem) {
365             break;
366         }
367         AlignRulesItem nextNodeAlignRules = nextNodeFlexItem->GetAlignRulesValue();
368         if (nextNodeAlignRules.find(AlignDirection::LEFT) == nextNodeAlignRules.end() ||
369             nextNodeAlignRules.find(AlignDirection::RIGHT) == nextNodeAlignRules.end()) {
370             break;
371         }
372         if (nextNodeAlignRules[AlignDirection::LEFT].anchor != currentNode ||
373             nextNodeAlignRules[AlignDirection::LEFT].horizontal != HorizontalAlign::END) {
374             break;
375         }
376         chainNodes.emplace_back(nextNode);
377         bool childGone = nextNodeWrapper->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE) ==
378                          VisibleType::GONE;
379         float childLayoutWeight = DEFAULT_WEIGHT;
380         if (!childGone) {
381             childLayoutWeight = nextNodeFlexItem->GetChainWeight()->first.value_or(0.0f);
382             if (GreatNotEqual(childLayoutWeight, 0.0f)) {
383                 isChainWeightMode_ = true;
384                 totalChainWeight += childLayoutWeight;
385             }
386         }
387         currentNode = nextNode;
388         currentAlignRules = nextNodeAlignRules;
389         nextNode = nextNodeAlignRules[AlignDirection::RIGHT].anchor;
390         rightAnchor = nextNodeAlignRules[AlignDirection::RIGHT];
391     }
392 }
393 
CheckHorizontalChain(const ChildMeasureWrapper & measureParam)394 void RelativeContainerLayoutAlgorithm::CheckHorizontalChain(const ChildMeasureWrapper& measureParam)
395 {
396     auto childWrapper = measureParam.layoutWrapper;
397     auto childHostNode = childWrapper->GetHostNode();
398     const auto& flexItem = childWrapper->GetLayoutProperty()->GetFlexItemProperty();
399     AlignRulesItem currentAlignRules = flexItem->GetAlignRulesValue();
400     ChainInfo chainInfo = flexItem->GetHorizontalChainStyleValue();
401     CHECK_NULL_VOID(chainInfo.direction.has_value());
402     CHECK_NULL_VOID(chainInfo.style.has_value());
403     BiasPair bias(0.5f, 0.5f);
404     float totalChainWeight = DEFAULT_WEIGHT;
405     if (flexItem->HasBias()) {
406         bias = flexItem->GetBiasValue();
407     }
408     if (currentAlignRules.find(AlignDirection::LEFT) == currentAlignRules.end() ||
409         currentAlignRules.find(AlignDirection::RIGHT) == currentAlignRules.end()) {
410         return;
411     }
412     AlignRule leftAnchor = currentAlignRules[AlignDirection::LEFT];
413     CHECK_NULL_VOID(IsAnchorLegal(leftAnchor.anchor));
414     AlignRule rightAnchor = currentAlignRules[AlignDirection::RIGHT];
415     std::string currentNode = measureParam.id;
416     std::vector<std::string> chainNodes;
417     chainNodes.emplace_back(currentNode);
418     if (childWrapper->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE) != VisibleType::GONE) {
419         float childLayoutWeight = flexItem->GetChainWeight()->first.value_or(DEFAULT_WEIGHT);
420         isChainWeightMode_ += GreatNotEqual(childLayoutWeight, DEFAULT_WEIGHT);
421         totalChainWeight += std::max(childLayoutWeight, DEFAULT_WEIGHT);
422     }
423     CheckNodeInHorizontalChain(currentNode, currentAlignRules, chainNodes, rightAnchor, totalChainWeight);
424     CHECK_NULL_VOID(IsAnchorLegal(rightAnchor.anchor) && chainNodes.size() > 1);
425     if (IsAnchorContainer(leftAnchor.anchor) || IsAnchorContainer(rightAnchor.anchor)) {
426         isHorizontalRelyOnContainer_ = true;
427     }
428     ChainParam chainParam;
429     chainParam.ids = chainNodes;
430     chainParam.anchorHead = leftAnchor;
431     chainParam.anchorTail = rightAnchor;
432     chainParam.chainStyle = chainInfo.style.value();
433     chainParam.bias = bias;
434     chainParam.totalChainWeight = totalChainWeight;
435     for (const auto& id : chainParam.ids) {
436         chainParam.itemSize[id] = std::nullopt;
437         horizontalChainNodeMap_[id] = measureParam.id;
438     }
439     horizontalChains_[measureParam.id] = chainParam;
440 }
441 
CheckNodeInVerticalChain(std::string & currentNode,AlignRulesItem & currentAlignRules,std::vector<std::string> & chainNodes,AlignRule & bottomAnchor,float & totalChainWeight)442 void RelativeContainerLayoutAlgorithm::CheckNodeInVerticalChain(std::string& currentNode,
443     AlignRulesItem& currentAlignRules, std::vector<std::string>& chainNodes,
444     AlignRule& bottomAnchor, float& totalChainWeight)
445 {
446     std::string nextNode = bottomAnchor.anchor;
447     while (idNodeMap_.find(nextNode) != idNodeMap_.end()) {
448         if (currentAlignRules[AlignDirection::BOTTOM].vertical != VerticalAlign::TOP) {
449             break;
450         }
451         CHECK_NULL_BREAK(idNodeMap_.find(nextNode) != idNodeMap_.end());
452         auto nextNodeWrapper = idNodeMap_[nextNode].layoutWrapper;
453         const auto& nextNodeFlexItem = nextNodeWrapper->GetLayoutProperty()->GetFlexItemProperty();
454         if (!nextNodeFlexItem) {
455             break;
456         }
457         AlignRulesItem nextNodeAlignRules = nextNodeFlexItem->GetAlignRulesValue();
458         if (nextNodeAlignRules.find(AlignDirection::TOP) == nextNodeAlignRules.end() ||
459             nextNodeAlignRules.find(AlignDirection::BOTTOM) == nextNodeAlignRules.end()) {
460             break;
461         }
462         if (nextNodeAlignRules[AlignDirection::TOP].anchor != currentNode ||
463             nextNodeAlignRules[AlignDirection::TOP].vertical != VerticalAlign::BOTTOM) {
464             break;
465         }
466         chainNodes.emplace_back(nextNode);
467         bool childGone = nextNodeWrapper->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE) ==
468                          VisibleType::GONE;
469         float childLayoutWeight = DEFAULT_WEIGHT;
470         if (!childGone) {
471             childLayoutWeight = nextNodeFlexItem->GetChainWeight()->second.value_or(0.0f);
472             if (GreatNotEqual(childLayoutWeight, 0.0f)) {
473                 isChainWeightMode_ = true;
474                 totalChainWeight += childLayoutWeight;
475             }
476         }
477         currentNode = nextNode;
478         currentAlignRules = nextNodeAlignRules;
479         nextNode = nextNodeAlignRules[AlignDirection::BOTTOM].anchor;
480         bottomAnchor = nextNodeAlignRules[AlignDirection::BOTTOM];
481     }
482 }
483 
CheckVerticalChain(const ChildMeasureWrapper & measureParam)484 void RelativeContainerLayoutAlgorithm::CheckVerticalChain(const ChildMeasureWrapper& measureParam)
485 {
486     auto childWrapper = measureParam.layoutWrapper;
487     auto childHostNode = childWrapper->GetHostNode();
488     const auto& flexItem = childWrapper->GetLayoutProperty()->GetFlexItemProperty();
489     AlignRulesItem currentAlignRules = flexItem->GetAlignRulesValue();
490     ChainInfo chainInfo = flexItem->GetVerticalChainStyleValue();
491     BiasPair bias(0.5f, 0.5f);
492     float totalChainWeight = DEFAULT_WEIGHT;
493     CHECK_NULL_VOID(chainInfo.direction.has_value());
494     CHECK_NULL_VOID(chainInfo.style.has_value());
495     if (flexItem->HasBias()) {
496         bias = flexItem->GetBiasValue();
497     }
498     if (currentAlignRules.find(AlignDirection::TOP) == currentAlignRules.end() ||
499         currentAlignRules.find(AlignDirection::BOTTOM) == currentAlignRules.end()) {
500         return;
501     }
502     AlignRule topAnchor = currentAlignRules[AlignDirection::TOP];
503     CHECK_NULL_VOID(IsAnchorLegal(topAnchor.anchor));
504     AlignRule bottomAnchor = currentAlignRules[AlignDirection::BOTTOM];
505     std::string currentNode = measureParam.id;
506     std::vector<std::string> chainNodes;
507     chainNodes.emplace_back(currentNode);
508     if (childWrapper->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE) != VisibleType::GONE) {
509         float childLayoutWeight = flexItem->GetChainWeight()->second.value_or(DEFAULT_WEIGHT);
510         isChainWeightMode_ += GreatNotEqual(childLayoutWeight, DEFAULT_WEIGHT);
511         totalChainWeight += std::max(childLayoutWeight, DEFAULT_WEIGHT);
512     }
513     CheckNodeInVerticalChain(currentNode, currentAlignRules, chainNodes, bottomAnchor, totalChainWeight);
514     CHECK_NULL_VOID(IsAnchorLegal(bottomAnchor.anchor) && chainNodes.size() > 1);
515     if (IsAnchorContainer(topAnchor.anchor) || IsAnchorContainer(bottomAnchor.anchor)) {
516         isVerticalRelyOnContainer_ = true;
517     }
518     ChainParam chainParam;
519     chainParam.ids = chainNodes;
520     chainParam.anchorHead = topAnchor;
521     chainParam.anchorTail = bottomAnchor;
522     chainParam.chainStyle = chainInfo.style.value();
523     chainParam.bias = bias;
524     chainParam.totalChainWeight = totalChainWeight;
525     for (const auto& id : chainParam.ids) {
526         chainParam.itemSize[id] = std::nullopt;
527         verticalChainNodeMap_[id] = measureParam.id;
528     }
529     verticalChains_[measureParam.id] = chainParam;
530 }
531 
CheckChain(LayoutWrapper * layoutWrapper)532 void RelativeContainerLayoutAlgorithm::CheckChain(LayoutWrapper* layoutWrapper)
533 {
534     CHECK_NULL_VOID(versionGreatorOrEqualToEleven_);
535     horizontalChains_.clear();
536     verticalChains_.clear();
537     for (const auto& mapItem : idNodeMap_) {
538         auto childWrapper = mapItem.second.layoutWrapper;
539         auto childLayoutProperty = childWrapper->GetLayoutProperty();
540         CHECK_NULL_VOID(childLayoutProperty);
541         const auto& flexItem = childLayoutProperty->GetFlexItemProperty();
542         if (!flexItem || !flexItem->HasAlignRules()) {
543             continue;
544         }
545 
546         std::string chainName;
547         if (flexItem->HasHorizontalChainStyle() && !IsNodeInHorizontalChain(mapItem.first, chainName)) {
548             CheckHorizontalChain(mapItem.second);
549         }
550 
551         if (flexItem->HasVerticalChainStyle() && !IsNodeInVerticalChain(mapItem.first, chainName)) {
552             CheckVerticalChain(mapItem.second);
553         }
554     }
555 }
556 
RecordSizeInChain(const std::string & nodeName)557 void RelativeContainerLayoutAlgorithm::RecordSizeInChain(const std::string& nodeName)
558 {
559     CHECK_NULL_VOID(versionGreatorOrEqualToEleven_);
560     CHECK_NULL_VOID(idNodeMap_.find(nodeName) != idNodeMap_.end());
561     auto childWrapper = idNodeMap_[nodeName].layoutWrapper;
562     CHECK_NULL_VOID(childWrapper);
563     auto childLayoutProperty = childWrapper->GetLayoutProperty();
564     CHECK_NULL_VOID(childLayoutProperty);
565     const auto& flexItem = childLayoutProperty->GetFlexItemProperty();
566     std::string chainName;
567     if (IsNodeInHorizontalChain(nodeName, chainName) &&
568         !GreatNotEqual(flexItem->GetChainWeight()->first.value_or(0.0f), 0.0f)) {
569         horizontalChains_[chainName].itemSize[nodeName] =
570             childWrapper->GetGeometryNode()->GetMarginFrameSize().Width();
571         horizontalChains_[chainName].remainingSpace -=  childWrapper->GetGeometryNode()->GetMarginFrameSize().Width();
572     }
573     if (IsNodeInVerticalChain(nodeName, chainName) &&
574         !GreatNotEqual(flexItem->GetChainWeight()->second.value_or(0.0f), 0.0f)) {
575         verticalChains_[chainName].itemSize[nodeName] =
576             childWrapper->GetGeometryNode()->GetMarginFrameSize().Height();
577         verticalChains_[chainName].remainingSpace -=  childWrapper->GetGeometryNode()->GetMarginFrameSize().Height();
578     }
579 }
580 
IsNodeInHorizontalChain(const std::string & nodeName,std::string & chainName)581 bool RelativeContainerLayoutAlgorithm::IsNodeInHorizontalChain(const std::string& nodeName, std::string& chainName)
582 {
583     CHECK_NULL_RETURN(versionGreatorOrEqualToEleven_, false);
584     CHECK_NULL_RETURN(!horizontalChains_.empty(), false);
585     auto it = horizontalChainNodeMap_.find(nodeName);
586     if (it != horizontalChainNodeMap_.end()) {
587         chainName = it->second;
588         return true;
589     }
590     return false;
591 }
IsNodeInVerticalChain(const std::string & nodeName,std::string & chainName)592 bool RelativeContainerLayoutAlgorithm::IsNodeInVerticalChain(const std::string& nodeName, std::string& chainName)
593 {
594     CHECK_NULL_RETURN(versionGreatorOrEqualToEleven_, false);
595     CHECK_NULL_RETURN(!verticalChains_.empty(), false);
596     auto it = verticalChainNodeMap_.find(nodeName);
597     if (it != verticalChainNodeMap_.end()) {
598         chainName = it->second;
599         return true;
600     }
601     return false;
602 }
603 
GetHorizontalAnchorValueByAlignRule(AlignRule & alignRule)604 float RelativeContainerLayoutAlgorithm::GetHorizontalAnchorValueByAlignRule(AlignRule& alignRule)
605 {
606     if (IsGuideline(alignRule.anchor) || IsBarrier(alignRule.anchor)) {
607         return recordOffsetMap_[alignRule.anchor].GetX();
608     }
609     bool anchorIsContainer = IsAnchorContainer(alignRule.anchor);
610     float anchorWidth = 0.0f;
611     if (anchorIsContainer) {
612         anchorWidth = containerSizeWithoutPaddingBorder_.Width();
613     } else {
614         anchorWidth = versionGreatorOrEqualToEleven_
615                           ? idNodeMap_[alignRule.anchor].layoutWrapper->GetGeometryNode()->GetFrameSize().Width()
616                           : idNodeMap_[alignRule.anchor].layoutWrapper->GetGeometryNode()->GetMarginFrameSize().Width();
617     }
618 
619     std::optional<float> marginLeft;
620     if (!anchorIsContainer && versionGreatorOrEqualToEleven_) {
621         auto anchorWrapper = idNodeMap_[alignRule.anchor].layoutWrapper;
622         if (anchorWrapper->GetGeometryNode()->GetMargin()) {
623             marginLeft = anchorWrapper->GetGeometryNode()->GetMargin()->left;
624         }
625     }
626 
627     float offsetX = 0.0f;
628     switch (alignRule.horizontal) {
629         case HorizontalAlign::START:
630             offsetX = 0.0f;
631             break;
632         case HorizontalAlign::CENTER:
633             offsetX = anchorWidth * HALF_MULTIPLY;
634             break;
635         case HorizontalAlign::END:
636             offsetX = anchorWidth;
637             break;
638         default:
639             break;
640     }
641 
642     offsetX += anchorIsContainer ? 0.0f : recordOffsetMap_[alignRule.anchor].GetX() + marginLeft.value_or(0);
643     return offsetX;
644 }
645 
GetVerticalAnchorValueByAlignRule(AlignRule & alignRule)646 float RelativeContainerLayoutAlgorithm::GetVerticalAnchorValueByAlignRule(AlignRule& alignRule)
647 {
648     if (IsGuideline(alignRule.anchor) || IsBarrier(alignRule.anchor)) {
649         return recordOffsetMap_[alignRule.anchor].GetY();
650     }
651     bool anchorIsContainer = IsAnchorContainer(alignRule.anchor);
652     float anchorHeight = 0.0f;
653     if (anchorIsContainer) {
654         anchorHeight = containerSizeWithoutPaddingBorder_.Height();
655     } else {
656         anchorHeight =
657             versionGreatorOrEqualToEleven_
658                 ? idNodeMap_[alignRule.anchor].layoutWrapper->GetGeometryNode()->GetFrameSize().Height()
659                 : idNodeMap_[alignRule.anchor].layoutWrapper->GetGeometryNode()->GetMarginFrameSize().Height();
660     }
661 
662     std::optional<float> marginTop;
663     if (!anchorIsContainer && versionGreatorOrEqualToEleven_) {
664         auto anchorWrapper = idNodeMap_[alignRule.anchor].layoutWrapper;
665         if (anchorWrapper->GetGeometryNode()->GetMargin()) {
666             marginTop = anchorWrapper->GetGeometryNode()->GetMargin()->top;
667         }
668     }
669 
670     float offsetY = 0.0f;
671     switch (alignRule.vertical) {
672         case VerticalAlign::TOP:
673             offsetY = 0.0f;
674             break;
675         case VerticalAlign::CENTER:
676             offsetY = anchorHeight * HALF_MULTIPLY;
677             break;
678         case VerticalAlign::BOTTOM:
679             offsetY = anchorHeight;
680             break;
681         default:
682             break;
683     }
684 
685     offsetY += anchorIsContainer ? 0.0f : recordOffsetMap_[alignRule.anchor].GetY() + marginTop.value_or(0);
686     return offsetY;
687 }
688 
CalcOffsetInChainGetStart(const float & anchorDistance,const float & contentSize,int32_t itemCount,const ChainParam & chainParam,LineDirection direction)689 std::pair<float, float> RelativeContainerLayoutAlgorithm::CalcOffsetInChainGetStart(const float& anchorDistance,
690     const float& contentSize, int32_t itemCount, const ChainParam& chainParam, LineDirection direction)
691 {
692     float spaceSize = 0.0f;
693     float start = 0.0f;
694     float bias = (direction == LineDirection::HORIZONTAL) ? chainParam.bias.first : chainParam.bias.second;
695     if (GreatOrEqual(anchorDistance, contentSize)) {
696         switch (chainParam.chainStyle) {
697             case ChainStyle::SPREAD:
698                 spaceSize = (anchorDistance - contentSize) / (itemCount + 1);
699                 start = spaceSize;
700                 break;
701             case ChainStyle::SPREAD_INSIDE:
702                 spaceSize = anchorDistance - contentSize;
703                 spaceSize = GreatNotEqual(itemCount, 1) ? spaceSize / (itemCount - 1) : spaceSize;
704                 break;
705             case ChainStyle::PACKED:
706                 spaceSize = 0.0f;
707                 start = (anchorDistance - contentSize) * bias;
708                 break;
709             default:
710                 break;
711         }
712     } else {
713         switch (chainParam.chainStyle) {
714             case ChainStyle::SPREAD:
715             case ChainStyle::SPREAD_INSIDE:
716                 start = (anchorDistance - contentSize) * HALF_MULTIPLY;
717                 break;
718             case ChainStyle::PACKED:
719                 start = (anchorDistance - contentSize) * bias;
720                 break;
721             default:
722                 break;
723         }
724     }
725     return { spaceSize, start };
726 }
727 
RecordOffsetInChain(float offset,float spaceSize,const std::string & chainName,LineDirection direction)728 void RelativeContainerLayoutAlgorithm::RecordOffsetInChain(
729     float offset, float spaceSize, const std::string& chainName, LineDirection direction)
730 {
731     std::unordered_map<std::string, ChainParam>& chains =
732         (direction == LineDirection::HORIZONTAL) ? horizontalChains_ : verticalChains_;
733 
734     if (direction == LineDirection::HORIZONTAL) {
735         for (const auto& nodeName : chains[chainName].ids) {
736             auto childWrapper = idNodeMap_[nodeName].layoutWrapper;
737             if (childWrapper->GetLayoutProperty()->GetVisibility() == VisibleType::GONE) {
738                 continue;
739             }
740             recordOffsetMap_[nodeName] = OffsetF(offset, recordOffsetMap_[nodeName].GetY());
741             offset += chains[chainName].itemSize[nodeName].value() + spaceSize;
742         }
743     } else {
744         for (const auto& nodeName : chains[chainName].ids) {
745             auto childWrapper = idNodeMap_[nodeName].layoutWrapper;
746             if (childWrapper->GetLayoutProperty()->GetVisibility() == VisibleType::GONE) {
747                 continue;
748             }
749             recordOffsetMap_[nodeName] = OffsetF(recordOffsetMap_[nodeName].GetX(), offset);
750             offset += chains[chainName].itemSize[nodeName].value() + spaceSize;
751         }
752     }
753 }
754 
CalcOffsetInChain(const std::string & chainName,LineDirection direction)755 bool RelativeContainerLayoutAlgorithm::CalcOffsetInChain(const std::string& chainName, LineDirection direction)
756 {
757     float contentSize = 0.0f;
758     float anchorDistance = 0.0f;
759     float spaceSize = 0.0f;
760     float start = 0.0f;
761     float end = 0.0f;
762     std::unordered_map<std::string, ChainParam>& chains =
763         (direction == LineDirection::HORIZONTAL) ? horizontalChains_ : verticalChains_;
764     if (chains[chainName].isCalculated) {
765         return true;
766     }
767     auto itemCount = chains[chainName].ids.size();
768     for (const auto& itemSize : chains[chainName].itemSize) {
769         auto childWrapper = idNodeMap_[itemSize.first].layoutWrapper;
770         if (childWrapper->GetLayoutProperty()->GetVisibility() == VisibleType::GONE) {
771             itemCount--;
772             continue;
773         }
774         if (!itemSize.second.has_value()) {
775             return false;
776         }
777         contentSize += itemSize.second.value();
778     }
779 
780     if (direction == LineDirection::HORIZONTAL) {
781         start = GetHorizontalAnchorValueByAlignRule(chains[chainName].anchorHead);
782         end = GetHorizontalAnchorValueByAlignRule(chains[chainName].anchorTail);
783     } else {
784         start = GetVerticalAnchorValueByAlignRule(chains[chainName].anchorHead);
785         end = GetVerticalAnchorValueByAlignRule(chains[chainName].anchorTail);
786     }
787     anchorDistance = end - start;
788     std::pair<float, float> spaceSizeAndStart =
789         CalcOffsetInChainGetStart(anchorDistance, contentSize, itemCount, chains[chainName], direction);
790     spaceSize = spaceSizeAndStart.first;
791     start += spaceSizeAndStart.second;
792     RecordOffsetInChain(start, spaceSize, chainName, direction);
793     chains[chainName].isCalculated = true;
794     return true;
795 }
796 
Measure(LayoutWrapper * layoutWrapper)797 void RelativeContainerLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
798 {
799     CHECK_NULL_VOID(layoutWrapper);
800     auto relativeContainerLayoutProperty = layoutWrapper->GetLayoutProperty();
801     CHECK_NULL_VOID(relativeContainerLayoutProperty);
802     versionGreatorOrEqualToEleven_ = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN);
803     if (layoutWrapper->GetAllChildrenWithBuild().empty()) {
804         UpdateSizeWhenChildrenEmpty(layoutWrapper);
805         return;
806     }
807     DetermineTopologicalOrder(layoutWrapper);
808     if (SystemProperties::GetDebugEnabled()) {
809         std::string result = "[";
810         for (const auto& nodeName : renderList_) {
811             result += nodeName + ",";
812         }
813         if (!renderList_.empty()) {
814             result = result.substr(0, result.length() - 1);
815         }
816         result += "]";
817         auto pattern = layoutWrapper->GetHostNode()->GetPattern<RelativeContainerPattern>();
818         CHECK_NULL_VOID(pattern);
819         pattern->SetTopologicalResult(result);
820     }
821 
822     MeasureChild(layoutWrapper);
823     MeasureChainWeight(layoutWrapper);
824     const auto& calcLayoutConstraint = relativeContainerLayoutProperty->GetCalcLayoutConstraint();
825     CHECK_NULL_VOID(calcLayoutConstraint);
826     auto selfIdealSize = calcLayoutConstraint->selfIdealSize;
827     CHECK_NULL_VOID(selfIdealSize.has_value());
828     if ((selfIdealSize.value().Width().has_value() &&
829             selfIdealSize.value().Width().value().GetDimension().Unit() == DimensionUnit::AUTO) ||
830         (selfIdealSize.value().Height().has_value() &&
831             selfIdealSize.value().Height().value().GetDimension().Unit() == DimensionUnit::AUTO)) {
832         MeasureSelf(layoutWrapper);
833     }
834     AdjustOffsetRtl(layoutWrapper);
835 }
836 
MeasureChainWeight(LayoutWrapper * layoutWrapper)837 void RelativeContainerLayoutAlgorithm::MeasureChainWeight(LayoutWrapper* layoutWrapper)
838 {
839     CHECK_NULL_VOID(isChainWeightMode_);
840     auto relativeContainerLayoutProperty = layoutWrapper->GetLayoutProperty();
841     CHECK_NULL_VOID(relativeContainerLayoutProperty);
842     for (const auto& nodeName : renderList_) {
843         auto it = idNodeMap_.find(nodeName);
844         if (it == idNodeMap_.end()) {
845             continue;
846         }
847         auto childWrapper = it->second.layoutWrapper;
848         auto childConstraint = relativeContainerLayoutProperty->CreateChildConstraint();
849         if (!childWrapper->IsActive()||!childWrapper->GetLayoutProperty() ||
850             !childWrapper->GetLayoutProperty()->GetFlexItemProperty()) {
851             continue;
852         }
853         const auto& flexItem = childWrapper->GetLayoutProperty()->GetFlexItemProperty();
854         std::string chainName;
855         if (!flexItem->HasAlignRules()) {
856             continue;
857         }
858         if (!(IsNodeInHorizontalChain(nodeName, chainName) || IsNodeInVerticalChain(nodeName, chainName))) {
859             flexItem->ClearAlignValue();
860             CalcSizeParam(layoutWrapper, nodeName);
861             CalcOffsetParam(layoutWrapper, nodeName);
862             continue;
863         }
864         if (IsNodeInHorizontalChain(nodeName, chainName) && HasWeight(flexItem, LineDirection::HORIZONTAL)) {
865             CalcChainWeightSize(flexItem, childConstraint, chainName, LineDirection::HORIZONTAL);
866         }
867         if (IsNodeInVerticalChain(nodeName, chainName) && HasWeight(flexItem, LineDirection::VERTICAL)) {
868             CalcChainWeightSize(flexItem, childConstraint, chainName, LineDirection::VERTICAL);
869         }
870         childWrapper->Measure(childConstraint);
871         if (IsNodeInHorizontalChain(nodeName, chainName)) {
872             horizontalChains_[chainName].itemSize[nodeName] =
873                 childWrapper->GetGeometryNode()->GetMarginFrameSize().Width();
874         }
875         if (IsNodeInVerticalChain(nodeName, chainName)) {
876             verticalChains_[chainName].itemSize[nodeName] =
877                 childWrapper->GetGeometryNode()->GetMarginFrameSize().Height();
878         }
879         CalcOffsetParam(layoutWrapper, nodeName);
880     }
881 }
882 
InitRemainingSpace(const std::string & chainName,LineDirection direction)883 void RelativeContainerLayoutAlgorithm::InitRemainingSpace(const std::string & chainName, LineDirection direction)
884 {
885     std::unordered_map<std::string, ChainParam> &chains =
886         (direction == LineDirection::HORIZONTAL) ? horizontalChains_ : verticalChains_;
887     CHECK_NULL_VOID(!chains[chainName].isWeightCalculated);
888     float start = 0.0f;
889     float end = 0.0f;
890     if (direction == LineDirection::HORIZONTAL) {
891         start = GetHorizontalAnchorValueByAlignRule(chains[chainName].anchorHead);
892         end = GetHorizontalAnchorValueByAlignRule(chains[chainName].anchorTail);
893     } else {
894         start = GetVerticalAnchorValueByAlignRule(chains[chainName].anchorHead);
895         end = GetVerticalAnchorValueByAlignRule(chains[chainName].anchorTail);
896     }
897     chains[chainName].remainingSpace += end - start;
898     chains[chainName].isWeightCalculated = true;
899 }
900 
HasWeight(const std::unique_ptr<FlexItemProperty> & flexItem,LineDirection direction)901 bool RelativeContainerLayoutAlgorithm::HasWeight(
902     const std::unique_ptr<FlexItemProperty>& flexItem, LineDirection direction)
903 {
904     if (direction == LineDirection::HORIZONTAL) {
905         return GreatNotEqual(flexItem->GetChainWeight()->first.value_or(DEFAULT_WEIGHT), DEFAULT_WEIGHT);
906     } else {
907         return GreatNotEqual(flexItem->GetChainWeight()->second.value_or(DEFAULT_WEIGHT), DEFAULT_WEIGHT);
908     }
909 }
910 
CalcChainWeightSize(const std::unique_ptr<FlexItemProperty> & flexItem,LayoutConstraintF & childConstraint,const std::string & chainName,LineDirection direction)911 void RelativeContainerLayoutAlgorithm::CalcChainWeightSize(
912     const std::unique_ptr<FlexItemProperty>& flexItem, LayoutConstraintF& childConstraint,
913     const std::string & chainName, LineDirection direction)
914 {
915     std::optional<float> childIdealSize;
916     std::unordered_map<std::string, ChainParam> &chains =
917         (direction == LineDirection::HORIZONTAL) ? horizontalChains_ : verticalChains_;
918     if (!chains[chainName].isWeightCalculated) {
919         InitRemainingSpace(chainName, direction);
920     }
921     if (chains[chainName].remainingSpace <= DEFAULT_WEIGHT) {
922         (direction == LineDirection::HORIZONTAL) ?
923         childConstraint.selfIdealSize.SetWidth(0.0f) : childConstraint.selfIdealSize.SetHeight(0.0f);
924     } else {
925         auto chainWeight = (direction == LineDirection::HORIZONTAL) ?
926             flexItem->GetChainWeight()->first.value_or(DEFAULT_WEIGHT) :
927             flexItem->GetChainWeight()->second.value_or(DEFAULT_WEIGHT);
928         childIdealSize = chains[chainName].remainingSpace * chainWeight / chains[chainName].totalChainWeight;
929         (direction == LineDirection::HORIZONTAL) ?
930         childConstraint.selfIdealSize.SetWidth(childIdealSize.value()) :
931         childConstraint.selfIdealSize.SetHeight(childIdealSize.value());
932     }
933 }
934 
MeasureChild(LayoutWrapper * layoutWrapper)935 void RelativeContainerLayoutAlgorithm::MeasureChild(LayoutWrapper* layoutWrapper)
936 {
937     auto relativeContainerLayoutProperty = layoutWrapper->GetLayoutProperty();
938     CHECK_NULL_VOID(relativeContainerLayoutProperty);
939     for (const auto& nodeName : renderList_) {
940         if (IsBarrier(nodeName)) {
941             MeasureBarrier(nodeName);
942         }
943         auto it = idNodeMap_.find(nodeName);
944         if (it == idNodeMap_.end()) {
945             continue;
946         }
947         auto childWrapper = it->second.layoutWrapper;
948         auto childConstraint = relativeContainerLayoutProperty->CreateChildConstraint();
949         if (!childWrapper->IsActive()) {
950             childWrapper->Measure(childConstraint);
951             continue;
952         }
953         if (!childWrapper->GetLayoutProperty() || !childWrapper->GetLayoutProperty()->GetFlexItemProperty()) {
954             childWrapper->Measure(childConstraint);
955             recordOffsetMap_[nodeName] = OffsetF(0.0f, 0.0f);
956             continue;
957         }
958         const auto& flexItem = childWrapper->GetLayoutProperty()->GetFlexItemProperty();
959         if (!flexItem->HasAlignRules()) {
960             childWrapper->Measure(childConstraint);
961             recordOffsetMap_[nodeName] = OffsetF(0.0f, 0.0f);
962             continue;
963         }
964         flexItem->ClearAlignValue();
965         auto alignRules = flexItem->GetAlignRulesValue();
966         auto frameNode = childWrapper->GetHostNode();
967         if (!alignRules.empty() && frameNode && frameNode->GetLayoutProperty()) {
968             // when child has alignRules and position, the position property do not work.
969             frameNode->GetLayoutProperty()->SetUsingPosition(false);
970         }
971         CalcSizeParam(layoutWrapper, nodeName);
972         CalcOffsetParam(layoutWrapper, nodeName);
973     }
974 }
975 
MeasureSelf(LayoutWrapper * layoutWrapper)976 void RelativeContainerLayoutAlgorithm::MeasureSelf(LayoutWrapper* layoutWrapper)
977 {
978     CHECK_NULL_VOID(versionGreatorOrEqualToEleven_);
979     CHECK_NULL_VOID(layoutWrapper);
980     auto relativeContainerLayoutProperty = layoutWrapper->GetLayoutProperty();
981     CHECK_NULL_VOID(relativeContainerLayoutProperty);
982     RectF relativeContainerRect(0, 0, 0, 0);
983     auto selfIdealSize = relativeContainerLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize;
984     for (const auto& nodeName : renderList_) {
985         auto it = idNodeMap_.find(nodeName);
986         if (it == idNodeMap_.end()) {
987             continue;
988         }
989         auto childWrapper = it->second.layoutWrapper;
990         if (childWrapper->GetLayoutProperty()->GetVisibility() == VisibleType::GONE) {
991             continue;
992         }
993         RectF tempRect(recordOffsetMap_[nodeName].GetX(), recordOffsetMap_[nodeName].GetY(),
994             childWrapper->GetGeometryNode()->GetMarginFrameSize().Width(),
995             childWrapper->GetGeometryNode()->GetMarginFrameSize().Height());
996         relativeContainerRect = relativeContainerRect.CombineRectT(tempRect);
997     }
998 
999     relativeContainerRect =
1000         relativeContainerRect.IntersectRectT(RectF(0.0f, 0.0f, Infinity<float>(), Infinity<float>()));
1001     if (selfIdealSize->Width()->GetDimension().Unit() == DimensionUnit::AUTO && !isHorizontalRelyOnContainer_) {
1002         layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF(relativeContainerRect.Width() + padding_.Width(),
1003             layoutWrapper->GetGeometryNode()->GetFrameSize().Height()));
1004     }
1005     if (selfIdealSize->Height()->GetDimension().Unit() == DimensionUnit::AUTO && !isVerticalRelyOnContainer_) {
1006         layoutWrapper->GetGeometryNode()->SetFrameSize(SizeF(layoutWrapper->GetGeometryNode()->GetFrameSize().Width(),
1007             relativeContainerRect.Height() + padding_.Height()));
1008     }
1009 }
1010 
Layout(LayoutWrapper * layoutWrapper)1011 void RelativeContainerLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1012 {
1013     auto relativeContainerLayoutProperty = layoutWrapper->GetLayoutProperty();
1014     CHECK_NULL_VOID(relativeContainerLayoutProperty);
1015     auto left = padding_.left.value_or(0);
1016     auto top = padding_.top.value_or(0);
1017     auto paddingOffset = OffsetF(left, top);
1018     auto textDirection = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection();
1019     for (auto&& childWrapper : layoutWrapper->GetAllChildrenWithBuild()) {
1020         auto nodeName = GetOrCreateNodeInspectorId(childWrapper->GetHostNode());
1021         if (!childWrapper->GetLayoutProperty()->GetFlexItemProperty() && (textDirection != TextDirection::RTL)) {
1022             childWrapper->GetGeometryNode()->SetMarginFrameOffset(OffsetF(0.0f, 0.0f) + paddingOffset);
1023             childWrapper->Layout();
1024             continue;
1025         }
1026         auto it = recordOffsetMap_.find(nodeName);
1027         if (it == recordOffsetMap_.end()) {
1028             continue;
1029         }
1030         if (it == recordOffsetMap_.end()) {
1031             childWrapper->GetGeometryNode()->SetMarginFrameOffset(OffsetF(0.0f, 0.0f) + paddingOffset);
1032             childWrapper->Layout();
1033             continue;
1034         }
1035         auto curOffset = it->second;
1036         childWrapper->GetGeometryNode()->SetMarginFrameOffset(curOffset + paddingOffset);
1037         childWrapper->Layout();
1038     }
1039 }
1040 
CollectNodesById(LayoutWrapper * layoutWrapper)1041 void RelativeContainerLayoutAlgorithm::CollectNodesById(LayoutWrapper* layoutWrapper)
1042 {
1043     idNodeMap_.clear();
1044     auto relativeContainerLayoutProperty = layoutWrapper->GetLayoutProperty();
1045     auto left = padding_.left.value_or(0);
1046     auto top = padding_.top.value_or(0);
1047     auto paddingOffset = OffsetF(left, top);
1048     const auto& childrenWrappers = layoutWrapper->GetAllChildrenWithBuild();
1049     for (const auto& childWrapper : childrenWrappers) {
1050         if (!childWrapper) {
1051             continue;
1052         }
1053         auto childHostNode = childWrapper->GetHostNode();
1054         if (!childHostNode) {
1055             continue;
1056         }
1057         childWrapper->SetActive();
1058         ChildMeasureWrapper childMeasureWrapper = { .layoutWrapper = childWrapper,
1059             .id = GetOrCreateNodeInspectorId(childHostNode) };
1060         idNodeMap_.emplace(childMeasureWrapper.id, childMeasureWrapper);
1061     }
1062     CalcGuideline(layoutWrapper);
1063     CalcBarrier(layoutWrapper);
1064 }
1065 
GetDependencyRelationshipInChain(const std::string & anchor,const std::string & nodeName)1066 void RelativeContainerLayoutAlgorithm::GetDependencyRelationshipInChain(
1067     const std::string& anchor, const std::string& nodeName)
1068 {
1069     if (IsAnchorContainer(anchor) || IsGuideline(anchor)) {
1070         return;
1071     }
1072     if (IsBarrier(anchor) || idNodeMap_.find(anchor) != idNodeMap_.end()) {
1073         InsertToReliedOnMap(anchor, nodeName);
1074     }
1075 }
1076 
GetDependencyRelationshipInBarrier()1077 void RelativeContainerLayoutAlgorithm::GetDependencyRelationshipInBarrier()
1078 {
1079     CHECK_NULL_VOID(versionGreatorOrEqualToEleven_);
1080     for (const auto& barrier : barriers_) {
1081         for (const auto& nodeName : barrier.second.second) {
1082             InsertToReliedOnMap(nodeName, barrier.first);
1083         }
1084     }
1085 }
1086 
IsAlignRuleInChain(const AlignDirection & direction,const std::string & nodeName)1087 bool RelativeContainerLayoutAlgorithm::IsAlignRuleInChain(const AlignDirection& direction, const std::string& nodeName)
1088 {
1089     CHECK_NULL_RETURN(versionGreatorOrEqualToEleven_, false);
1090     std::string chainName;
1091     if ((direction == AlignDirection::LEFT || direction == AlignDirection::RIGHT) &&
1092         IsNodeInHorizontalChain(nodeName, chainName)) {
1093         GetDependencyRelationshipInChain(horizontalChains_[chainName].anchorHead.anchor, nodeName);
1094         GetDependencyRelationshipInChain(horizontalChains_[chainName].anchorTail.anchor, nodeName);
1095         return true;
1096     }
1097     if ((direction == AlignDirection::TOP || direction == AlignDirection::BOTTOM) &&
1098         IsNodeInVerticalChain(nodeName, chainName)) {
1099         GetDependencyRelationshipInChain(verticalChains_[chainName].anchorHead.anchor, nodeName);
1100         GetDependencyRelationshipInChain(verticalChains_[chainName].anchorTail.anchor, nodeName);
1101         return true;
1102     }
1103     return false;
1104 }
1105 
InsertToReliedOnMap(const std::string & anchorName,const std::string & nodeName)1106 void RelativeContainerLayoutAlgorithm::InsertToReliedOnMap(const std::string& anchorName, const std::string& nodeName)
1107 {
1108     auto iter = reliedOnMap_.find(anchorName);
1109     if (iter == reliedOnMap_.end()) {
1110         std::set<std::string> reliedList;
1111         reliedList.insert(nodeName);
1112         reliedOnMap_[anchorName] = reliedList;
1113         return;
1114     }
1115     iter->second.insert(nodeName);
1116 }
1117 
GetDependencyRelationship()1118 void RelativeContainerLayoutAlgorithm::GetDependencyRelationship()
1119 {
1120     for (const auto& mapItem : idNodeMap_) {
1121         auto childWrapper = mapItem.second.layoutWrapper;
1122         const auto& flexItem = childWrapper->GetLayoutProperty()->GetFlexItemProperty();
1123         auto childHostNode = childWrapper->GetHostNode();
1124         if (!flexItem || !flexItem->HasAlignRules()) {
1125             continue;
1126         }
1127         for (const auto& alignRule : flexItem->GetAlignRulesValue()) {
1128             if (IsAlignRuleInChain(alignRule.first, mapItem.first)) {
1129                 continue;
1130             }
1131 
1132             if (IsBarrier(alignRule.second.anchor)) {
1133                 InsertToReliedOnMap(alignRule.second.anchor, mapItem.second.id);
1134                 continue;
1135             }
1136 
1137             if (IsAnchorContainer(alignRule.second.anchor) || IsGuideline(alignRule.second.anchor)) {
1138                 if (static_cast<uint32_t>(alignRule.first) < HORIZONTAL_DIRECTION_RANGE) {
1139                     isHorizontalRelyOnContainer_ = true;
1140                 } else if (static_cast<uint32_t>(alignRule.first) < VERTICAL_DIRECTION_RANGE) {
1141                     isVerticalRelyOnContainer_ = true;
1142                 }
1143                 continue;
1144             }
1145             auto it = idNodeMap_.find(alignRule.second.anchor);
1146             if (it == idNodeMap_.end()) {
1147                 continue;
1148             }
1149 
1150             if (!AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
1151                 auto anchorChildWrapper = it->second.layoutWrapper;
1152                 auto anchorChildLayoutProp = anchorChildWrapper->GetLayoutProperty();
1153                 auto anchorChildVisibility = anchorChildLayoutProp->GetVisibility();
1154                 if (anchorChildVisibility == VisibleType::GONE) {
1155                     childWrapper->SetActive(false);
1156                 }
1157             }
1158             // if a is the anchor of b, then reliedOnMap should place <a, [b]> for the first appearance
1159             // of key a. Otherwise b will be inserted into the existing value list
1160             InsertToReliedOnMap(alignRule.second.anchor, mapItem.second.id);
1161         }
1162     }
1163     GetDependencyRelationshipInBarrier();
1164 }
1165 
PreTopologicalLoopDetectionGetAnchorSet(const std::string & nodeName,const AlignRulesItem & alignRulesItem,std::set<std::string> & anchorSet)1166 void RelativeContainerLayoutAlgorithm::PreTopologicalLoopDetectionGetAnchorSet(
1167     const std::string& nodeName, const AlignRulesItem& alignRulesItem, std::set<std::string>& anchorSet)
1168 {
1169     for (const auto& alignRule : alignRulesItem) {
1170         std::string anchor = alignRule.second.anchor;
1171         std::string chainName;
1172         if (IsNodeInHorizontalChain(nodeName, chainName)) {
1173             if (alignRule.first == AlignDirection::LEFT) {
1174                 anchor = horizontalChains_[chainName].anchorHead.anchor;
1175             } else if (alignRule.first == AlignDirection::RIGHT) {
1176                 anchor = horizontalChains_[chainName].anchorTail.anchor;
1177             }
1178         }
1179         if (IsNodeInVerticalChain(nodeName, chainName)) {
1180             if (alignRule.first == AlignDirection::TOP) {
1181                 anchor = verticalChains_[chainName].anchorHead.anchor;
1182             } else if (alignRule.first == AlignDirection::BOTTOM) {
1183                 anchor = verticalChains_[chainName].anchorTail.anchor;
1184             }
1185         }
1186 
1187         if (IsBarrier(anchor)) {
1188             anchorSet.insert(anchor);
1189             continue;
1190         }
1191 
1192         if (IsAnchorContainer(anchor) || IsGuideline(anchor) || idNodeMap_.find(anchor) == idNodeMap_.end()) {
1193             continue;
1194         }
1195         anchorSet.insert(anchor);
1196     }
1197 }
1198 
PreTopologicalLoopDetection()1199 bool RelativeContainerLayoutAlgorithm::PreTopologicalLoopDetection()
1200 {
1201     std::queue<std::string> visitedNode;
1202     std::queue<std::string> layoutQueue;
1203 
1204     for (const auto& mapItem : idNodeMap_) {
1205         auto childWrapper = mapItem.second.layoutWrapper;
1206         auto childHostNode = childWrapper->GetHostNode();
1207         const auto& flexItem = childWrapper->GetLayoutProperty()->GetFlexItemProperty();
1208         if (!flexItem || !flexItem->HasAlignRules()) {
1209             visitedNode.push(mapItem.first);
1210             layoutQueue.push(mapItem.second.id);
1211             continue;
1212         }
1213         std::set<std::string> anchorSet;
1214         PreTopologicalLoopDetectionGetAnchorSet(mapItem.first, flexItem->GetAlignRulesValue(), anchorSet);
1215         incomingDegreeMap_[mapItem.second.id] = anchorSet.size();
1216         if (incomingDegreeMap_[mapItem.second.id] == 0) {
1217             layoutQueue.push(mapItem.second.id);
1218         }
1219     }
1220 
1221     for (const auto& barrier : barriers_) {
1222         std::set<std::string> anchorSet;
1223         for (const auto& nodeName : barrier.second.second) {
1224             if (IsBarrier(nodeName)) {
1225                 anchorSet.insert(nodeName);
1226                 continue;
1227             }
1228             if (IsGuideline(nodeName) || idNodeMap_.find(nodeName) == idNodeMap_.end()) {
1229                 continue;
1230             }
1231             anchorSet.insert(nodeName);
1232         }
1233         incomingDegreeMap_[barrier.first] = anchorSet.size();
1234         if (incomingDegreeMap_[barrier.first] == 0) {
1235             layoutQueue.push(barrier.first);
1236         }
1237     }
1238 
1239     std::map<std::string, uint32_t> incomingDegreeMapCopy;
1240     incomingDegreeMapCopy.insert(incomingDegreeMap_.begin(), incomingDegreeMap_.end());
1241     while (!layoutQueue.empty()) {
1242         auto currentNodeInspectorId = layoutQueue.front();
1243         layoutQueue.pop();
1244         auto reliedSet = reliedOnMap_[currentNodeInspectorId];
1245         for (const auto& node : reliedSet) {
1246             auto it = incomingDegreeMapCopy.find(node);
1247             if (it == incomingDegreeMapCopy.end() || IsAnchorContainer(node)) {
1248                 continue;
1249             }
1250             it->second -= 1;
1251             if (it->second == 0) {
1252                 layoutQueue.push(node);
1253             }
1254         }
1255         incomingDegreeMapCopy.erase(currentNodeInspectorId);
1256         visitedNode.push(currentNodeInspectorId);
1257     }
1258     if (!incomingDegreeMapCopy.empty()) {
1259         std::string loopDependentNodes;
1260         for (const auto& node : incomingDegreeMapCopy) {
1261             loopDependentNodes += node.first + ",";
1262         }
1263         return false;
1264     }
1265     return true;
1266 }
1267 
TopologicalSort(std::list<std::string> & renderList)1268 void RelativeContainerLayoutAlgorithm::TopologicalSort(std::list<std::string>& renderList)
1269 {
1270     std::queue<std::string> layoutQueue;
1271     for (const auto& mapItem : idNodeMap_) {
1272         auto childWrapper = mapItem.second.layoutWrapper;
1273         auto childHostNode = childWrapper->GetHostNode();
1274         const auto& flexItem = childWrapper->GetLayoutProperty()->GetFlexItemProperty();
1275         if (!flexItem || incomingDegreeMap_[mapItem.second.id] == 0) {
1276             layoutQueue.push(mapItem.second.id);
1277         }
1278     }
1279     while (!layoutQueue.empty()) {
1280         auto currentNodeInspectorId = layoutQueue.front();
1281         layoutQueue.pop();
1282         // reduce incoming degree of nodes relied on currentNode
1283         auto reliedList = reliedOnMap_[currentNodeInspectorId];
1284         for (const auto& node : reliedList) {
1285             auto it = incomingDegreeMap_.find(node);
1286             if (it == incomingDegreeMap_.end() || IsAnchorContainer(node)) {
1287                 continue;
1288             }
1289             it->second -= 1;
1290             if (it->second == 0) {
1291                 layoutQueue.push(node);
1292             }
1293         }
1294         renderList.emplace_back(currentNodeInspectorId);
1295     }
1296 }
1297 
CalcSizeParam(LayoutWrapper * layoutWrapper,const std::string & nodeName)1298 void RelativeContainerLayoutAlgorithm::CalcSizeParam(LayoutWrapper* layoutWrapper, const std::string& nodeName)
1299 {
1300     std::string chainName;
1301     auto it = idNodeMap_.find(nodeName);
1302     if (it == idNodeMap_.end()) {
1303         return;
1304     }
1305     auto childWrapper = it->second.layoutWrapper;
1306     auto childLayoutProperty = childWrapper->GetLayoutProperty();
1307     CHECK_NULL_VOID(childLayoutProperty);
1308     CHECK_NULL_VOID(childWrapper->GetLayoutProperty()->GetFlexItemProperty());
1309     CHECK_NULL_VOID(childWrapper->GetLayoutProperty()->GetFlexItemProperty()->HasAlignRules());
1310     auto relativeContainerLayoutProperty = layoutWrapper->GetLayoutProperty();
1311     CHECK_NULL_VOID(relativeContainerLayoutProperty);
1312     auto childConstraint = relativeContainerLayoutProperty->CreateChildConstraint();
1313     auto alignRules = childWrapper->GetLayoutProperty()->GetFlexItemProperty()->GetAlignRulesValue();
1314     const auto& calcConstraint = childLayoutProperty->GetCalcLayoutConstraint();
1315     bool horizontalHasIdealSize = false;
1316     bool verticalHasIdealSize = false;
1317     if (calcConstraint && calcConstraint->selfIdealSize.has_value() &&
1318         calcConstraint->selfIdealSize.value().Width().has_value()) {
1319         horizontalHasIdealSize = true;
1320     }
1321     if (calcConstraint && calcConstraint->selfIdealSize.has_value() &&
1322         calcConstraint->selfIdealSize.value().Height().has_value()) {
1323         verticalHasIdealSize = true;
1324     }
1325     horizontalHasIdealSize = horizontalHasIdealSize && versionGreatorOrEqualToEleven_;
1326     verticalHasIdealSize = verticalHasIdealSize && versionGreatorOrEqualToEleven_;
1327     const auto& childFlexItemProperty = childLayoutProperty->GetFlexItemProperty();
1328     std::optional<float> childIdealWidth;
1329     std::optional<float> childIdealHeight;
1330 
1331     for (const auto& alignRule : alignRules) {
1332         if (!IsAnchorLegal(alignRule.second.anchor)) {
1333             continue;
1334         }
1335         if (static_cast<uint32_t>(alignRule.first) < HORIZONTAL_DIRECTION_RANGE) {
1336             if (!childFlexItemProperty->GetTwoHorizontalDirectionAligned()) {
1337                 CalcHorizontalLayoutParam(alignRule.first, alignRule.second, layoutWrapper, nodeName);
1338             }
1339         } else {
1340             if (!childFlexItemProperty->GetTwoVerticalDirectionAligned()) {
1341                 CalcVerticalLayoutParam(alignRule.first, alignRule.second, layoutWrapper, nodeName);
1342             }
1343         }
1344     }
1345     if (childFlexItemProperty->GetTwoHorizontalDirectionAligned()) {
1346         auto checkAlign = AlignDirection::MIDDLE;
1347         if (childFlexItemProperty->GetAligned(checkAlign)) {
1348             auto middleValue = childFlexItemProperty->GetAlignValue(checkAlign);
1349             checkAlign = AlignDirection::LEFT;
1350             if (childFlexItemProperty->GetAligned(checkAlign)) {
1351                 childIdealWidth = 2.0f * std::max(middleValue - childFlexItemProperty->GetAlignValue(checkAlign), 0.0f);
1352             } else {
1353                 checkAlign = AlignDirection::RIGHT;
1354                 childIdealWidth = 2.0f * std::max(childFlexItemProperty->GetAlignValue(checkAlign) - middleValue, 0.0f);
1355             }
1356         } else {
1357             childIdealWidth = std::max(childFlexItemProperty->GetAlignValue(AlignDirection::RIGHT) -
1358                                            childFlexItemProperty->GetAlignValue(AlignDirection::LEFT),
1359                 0.0f);
1360         }
1361         if (childIdealWidth.has_value() && LessOrEqual(childIdealWidth.value(), 0.0f) &&
1362             !IsNodeInHorizontalChain(nodeName, chainName)) {
1363             childConstraint.selfIdealSize.SetWidth(0.0f);
1364             childConstraint.selfIdealSize.SetHeight(0.0f);
1365             childWrapper->Measure(childConstraint);
1366             RecordSizeInChain(nodeName);
1367             return;
1368         }
1369     }
1370     if (childFlexItemProperty->GetTwoVerticalDirectionAligned()) {
1371         auto checkAlign = AlignDirection::CENTER;
1372         if (childFlexItemProperty->GetAligned(checkAlign)) {
1373             auto centerValue = childFlexItemProperty->GetAlignValue(checkAlign);
1374             checkAlign = AlignDirection::TOP;
1375             if (childFlexItemProperty->GetAligned(checkAlign)) {
1376                 childIdealHeight =
1377                     2.0f * std::max(centerValue - childFlexItemProperty->GetAlignValue(checkAlign), 0.0f);
1378             } else {
1379                 checkAlign = AlignDirection::BOTTOM;
1380                 childIdealHeight =
1381                     2.0f * std::max(childFlexItemProperty->GetAlignValue(checkAlign) - centerValue, 0.0f);
1382             }
1383         } else {
1384             childIdealHeight = std::max(childFlexItemProperty->GetAlignValue(AlignDirection::BOTTOM) -
1385                                             childFlexItemProperty->GetAlignValue(AlignDirection::TOP),
1386                 0.0f);
1387         }
1388         if (childIdealHeight.has_value() && LessOrEqual(childIdealHeight.value(), 0.0f) &&
1389             !IsNodeInVerticalChain(nodeName, chainName)) {
1390             childConstraint.selfIdealSize.SetWidth(0.0f);
1391             childConstraint.selfIdealSize.SetHeight(0.0f);
1392             childWrapper->Measure(childConstraint);
1393             RecordSizeInChain(nodeName);
1394             return;
1395         }
1396     }
1397 
1398     // for api 11 or larger, alignRules will not effect child component size as described in doc
1399     if (horizontalHasIdealSize && verticalHasIdealSize) {
1400         childWrapper->Measure(childConstraint);
1401         RecordSizeInChain(nodeName);
1402         return;
1403     }
1404 
1405     if (childIdealWidth.has_value() && !horizontalHasIdealSize && !IsNodeInHorizontalChain(nodeName, chainName)) {
1406         childConstraint.selfIdealSize.SetWidth(childIdealWidth.value());
1407     }
1408     if (childIdealHeight.has_value() && !verticalHasIdealSize && !IsNodeInVerticalChain(nodeName, chainName)) {
1409         childConstraint.selfIdealSize.SetHeight(childIdealHeight.value());
1410     }
1411     childWrapper->Measure(childConstraint);
1412     RecordSizeInChain(nodeName);
1413 }
1414 
CalcOffsetParam(LayoutWrapper * layoutWrapper,const std::string & nodeName)1415 void RelativeContainerLayoutAlgorithm::CalcOffsetParam(LayoutWrapper* layoutWrapper, const std::string& nodeName)
1416 {
1417     auto childWrapper = idNodeMap_[nodeName].layoutWrapper;
1418     auto alignRules = childWrapper->GetLayoutProperty()->GetFlexItemProperty()->GetAlignRulesValue();
1419     float offsetX = 0.0f;
1420     bool offsetXCalculated = false;
1421     float offsetY = 0.0f;
1422     bool offsetYCalculated = false;
1423     std::string chainName;
1424     if (IsNodeInHorizontalChain(nodeName, chainName)) {
1425         if (CalcOffsetInChain(chainName, LineDirection::HORIZONTAL)) {
1426             offsetX = recordOffsetMap_[nodeName].GetX();
1427         }
1428         offsetXCalculated = true;
1429     }
1430     if (IsNodeInVerticalChain(nodeName, chainName)) {
1431         if (CalcOffsetInChain(chainName, LineDirection::VERTICAL)) {
1432             offsetY = recordOffsetMap_[nodeName].GetY();
1433         }
1434         offsetYCalculated = true;
1435     }
1436 
1437     for (const auto& alignRule : alignRules) {
1438         if (!IsAnchorLegal(alignRule.second.anchor)) {
1439             continue;
1440         }
1441         if (static_cast<uint32_t>(alignRule.first) < HORIZONTAL_DIRECTION_RANGE) {
1442             if (!offsetXCalculated) {
1443                 offsetX = CalcHorizontalOffset(
1444                     alignRule.first, alignRule.second, containerSizeWithoutPaddingBorder_.Width(), nodeName);
1445                 offsetXCalculated = true;
1446             }
1447         } else {
1448             if (!offsetYCalculated) {
1449                 offsetY = CalcVerticalOffset(
1450                     alignRule.first, alignRule.second, containerSizeWithoutPaddingBorder_.Height(), nodeName);
1451                 offsetYCalculated = true;
1452             }
1453         }
1454     }
1455     recordOffsetMap_[nodeName] = OffsetF(offsetX, offsetY) + CalcBias(nodeName);
1456 }
1457 
IsValidBias(float bias)1458 bool RelativeContainerLayoutAlgorithm::IsValidBias(float bias)
1459 {
1460     return GreatOrEqual(bias, 0.0f);
1461 }
1462 
CalcBiasTowDirection(std::pair<TwoAlignedValues,TwoAlignedValues> & alignedValuesOnTwoDirections,ChildIdealSize & childIdealSize,BiasPair & biasPair,float & horizontalOffset,float & verticalOffset)1463 void RelativeContainerLayoutAlgorithm::CalcBiasTowDirection(
1464     std::pair<TwoAlignedValues, TwoAlignedValues>& alignedValuesOnTwoDirections, ChildIdealSize& childIdealSize,
1465     BiasPair& biasPair, float& horizontalOffset, float& verticalOffset)
1466 {
1467     auto horizontalValues = alignedValuesOnTwoDirections.first;
1468     auto verticalValues = alignedValuesOnTwoDirections.second;
1469     auto biasX = biasPair.first;
1470     auto biasY = biasPair.second;
1471     if (horizontalValues.first.has_value() && horizontalValues.second.has_value() && childIdealSize.first.has_value()) {
1472         auto alignDiff = std::abs(horizontalValues.first.value() - horizontalValues.second.value());
1473         horizontalOffset = (alignDiff - childIdealSize.first.value()) * (IsValidBias(biasX) ? biasX : DEFAULT_BIAS);
1474     }
1475     if (verticalValues.first.has_value() && verticalValues.second.has_value() && childIdealSize.second.has_value()) {
1476         auto alignDiff = std::abs(verticalValues.first.value() - verticalValues.second.value());
1477         verticalOffset = (alignDiff - childIdealSize.second.value()) * (IsValidBias(biasY) ? biasY : DEFAULT_BIAS);
1478     }
1479 }
1480 
CalcBias(const std::string & nodeName)1481 OffsetF RelativeContainerLayoutAlgorithm::CalcBias(const std::string& nodeName)
1482 {
1483     OffsetF emptyBiasOffset;
1484     std::string chainName;
1485     CHECK_NULL_RETURN(versionGreatorOrEqualToEleven_, emptyBiasOffset);
1486     if (IsNodeInHorizontalChain(nodeName, chainName) && IsNodeInVerticalChain(nodeName, chainName)) {
1487         return emptyBiasOffset;
1488     }
1489     auto childWrapper = idNodeMap_[nodeName].layoutWrapper;
1490     auto layoutProperty = childWrapper->GetLayoutProperty();
1491     CHECK_NULL_RETURN(layoutProperty, emptyBiasOffset);
1492     const auto& flexItemProperty = layoutProperty->GetFlexItemProperty();
1493     CHECK_NULL_RETURN(flexItemProperty, emptyBiasOffset);
1494     CHECK_NULL_RETURN(flexItemProperty->HasBias(), emptyBiasOffset);
1495     auto biasPair = flexItemProperty->GetBiasValue();
1496     CHECK_NULL_RETURN(flexItemProperty->HasAlignRules(), emptyBiasOffset);
1497     auto alignRules = flexItemProperty->GetAlignRulesValue();
1498     const auto& calcLayoutConstraint = layoutProperty->GetCalcLayoutConstraint();
1499     ChildIdealSize childIdealSize;
1500     if (calcLayoutConstraint && calcLayoutConstraint->selfIdealSize.has_value() &&
1501         calcLayoutConstraint->selfIdealSize.value().Width().has_value()) {
1502         childIdealSize.first = childWrapper->GetGeometryNode()->GetMarginFrameSize().Width();
1503     }
1504 
1505     if (calcLayoutConstraint && calcLayoutConstraint->selfIdealSize.has_value() &&
1506         calcLayoutConstraint->selfIdealSize.value().Height().has_value()) {
1507         childIdealSize.second = childWrapper->GetGeometryNode()->GetMarginFrameSize().Height();
1508     }
1509     if (!childIdealSize.first.has_value() && !childIdealSize.second.has_value()) {
1510         return OffsetF(0.0f, 0.0f);
1511     }
1512     auto alignedValuesOnTwoDirections = GetFirstTwoAlignValues(childWrapper, flexItemProperty, childIdealSize);
1513     auto horizontalOffset = 0.0f;
1514     auto verticalOffset = 0.0f;
1515     CalcBiasTowDirection(alignedValuesOnTwoDirections, childIdealSize, biasPair, horizontalOffset, verticalOffset);
1516     horizontalOffset = (IsNodeInHorizontalChain(nodeName, chainName)) ? 0.0f : horizontalOffset;
1517     verticalOffset = ((IsNodeInVerticalChain(nodeName, chainName))) ? 0.0f : verticalOffset;
1518     return OffsetF(horizontalOffset, verticalOffset);
1519 }
1520 
GetFirstTwoAlignValues(const RefPtr<LayoutWrapper> & childWrapper,const std::unique_ptr<FlexItemProperty> & flexItemProperty,const ChildIdealSize & childIdealSize)1521 std::pair<TwoAlignedValues, TwoAlignedValues> RelativeContainerLayoutAlgorithm::GetFirstTwoAlignValues(
1522     const RefPtr<LayoutWrapper>& childWrapper, const std::unique_ptr<FlexItemProperty>& flexItemProperty,
1523     const ChildIdealSize& childIdealSize)
1524 {
1525     CHECK_NULL_RETURN(flexItemProperty, {});
1526     CHECK_NULL_RETURN(flexItemProperty->HasAlignRules(), {});
1527     auto alignRules = flexItemProperty->GetAlignRulesValue();
1528     TwoAlignedValues horizontalValues;
1529     TwoAlignedValues verticalValues;
1530     for (const auto& alignRule : alignRules) {
1531         bool horizontalCheckTwoSidesAligned = horizontalValues.first.has_value() && horizontalValues.second.has_value();
1532         bool verticalCheckTwoSidesAligned = verticalValues.second.has_value() && verticalValues.second.has_value();
1533         if (horizontalCheckTwoSidesAligned && verticalCheckTwoSidesAligned) {
1534             break;
1535         }
1536         if (!IsAnchorLegal(alignRule.second.anchor)) {
1537             continue;
1538         }
1539         if (static_cast<uint32_t>(alignRule.first) < HORIZONTAL_DIRECTION_RANGE && !horizontalCheckTwoSidesAligned &&
1540             childIdealSize.first.has_value()) {
1541             UpdateTwoAlignValues(horizontalValues, alignRule.second, LineDirection::HORIZONTAL);
1542         } else if (static_cast<uint32_t>(alignRule.first) >= HORIZONTAL_DIRECTION_RANGE &&
1543                    !verticalCheckTwoSidesAligned && childIdealSize.second.has_value()) {
1544             UpdateTwoAlignValues(verticalValues, alignRule.second, LineDirection::VERTICAL);
1545         }
1546     }
1547     return { horizontalValues, verticalValues };
1548 }
1549 
CalcHorizontalLayoutParam(AlignDirection alignDirection,const AlignRule & alignRule,LayoutWrapper * layoutWrapper,const std::string & nodeName)1550 void RelativeContainerLayoutAlgorithm::CalcHorizontalLayoutParam(AlignDirection alignDirection,
1551     const AlignRule& alignRule, LayoutWrapper* layoutWrapper, const std::string& nodeName)
1552 {
1553     auto it = idNodeMap_.find(nodeName);
1554     if (it == idNodeMap_.end()) {
1555         return;
1556     }
1557     auto childWrapper = it->second.layoutWrapper;
1558     auto childLayoutProperty = childWrapper->GetLayoutProperty();
1559     CHECK_NULL_VOID(childLayoutProperty);
1560     const auto& childFlexItemProperty = childLayoutProperty->GetFlexItemProperty();
1561     if (IsGuideline(alignRule.anchor)) {
1562         if (guidelines_[alignRule.anchor].first == LineDirection::VERTICAL) {
1563             childFlexItemProperty->SetAlignValue(alignDirection, recordOffsetMap_[alignRule.anchor].GetX());
1564         } else {
1565             childFlexItemProperty->SetAlignValue(alignDirection, 0.0f);
1566         }
1567         return;
1568     }
1569 
1570     if (IsBarrier(alignRule.anchor)) {
1571         if (barriers_[alignRule.anchor].first == BarrierDirection::LEFT ||
1572             barriers_[alignRule.anchor].first == BarrierDirection::RIGHT) {
1573             childFlexItemProperty->SetAlignValue(alignDirection, recordOffsetMap_[alignRule.anchor].GetX());
1574         } else {
1575             childFlexItemProperty->SetAlignValue(alignDirection, 0.0f);
1576         }
1577         return;
1578     }
1579 
1580     switch (alignRule.horizontal) {
1581         case HorizontalAlign::START:
1582             childFlexItemProperty->SetAlignValue(
1583                 alignDirection, IsAnchorContainer(alignRule.anchor) ? 0.0f : recordOffsetMap_[alignRule.anchor].GetX());
1584             break;
1585         case HorizontalAlign::CENTER:
1586             childFlexItemProperty->SetAlignValue(alignDirection,
1587                 IsAnchorContainer(alignRule.anchor)
1588                     ? containerSizeWithoutPaddingBorder_.Width() * HALF_MULTIPLY
1589                     : idNodeMap_[alignRule.anchor].layoutWrapper->GetGeometryNode()->GetMarginFrameSize().Width() *
1590                               HALF_MULTIPLY +
1591                           recordOffsetMap_[alignRule.anchor].GetX());
1592             break;
1593         case HorizontalAlign::END:
1594             childFlexItemProperty->SetAlignValue(alignDirection,
1595                 IsAnchorContainer(alignRule.anchor)
1596                     ? containerSizeWithoutPaddingBorder_.Width()
1597                     : idNodeMap_[alignRule.anchor].layoutWrapper->GetGeometryNode()->GetMarginFrameSize().Width() +
1598                           recordOffsetMap_[alignRule.anchor].GetX());
1599             break;
1600         default:
1601             break;
1602     }
1603 }
1604 
CalcVerticalLayoutParam(AlignDirection alignDirection,const AlignRule & alignRule,LayoutWrapper * layoutWrapper,const std::string & nodeName)1605 void RelativeContainerLayoutAlgorithm::CalcVerticalLayoutParam(AlignDirection alignDirection,
1606     const AlignRule& alignRule, LayoutWrapper* layoutWrapper, const std::string& nodeName)
1607 {
1608     auto it = idNodeMap_.find(nodeName);
1609     if (it == idNodeMap_.end()) {
1610         return;
1611     }
1612     auto childWrapper = it->second.layoutWrapper;
1613     auto childLayoutProperty = childWrapper->GetLayoutProperty();
1614     CHECK_NULL_VOID(childLayoutProperty);
1615     const auto& childFlexItemProperty = childLayoutProperty->GetFlexItemProperty();
1616     if (IsGuideline(alignRule.anchor)) {
1617         if (guidelines_[alignRule.anchor].first == LineDirection::HORIZONTAL) {
1618             childFlexItemProperty->SetAlignValue(alignDirection, recordOffsetMap_[alignRule.anchor].GetY());
1619         } else {
1620             childFlexItemProperty->SetAlignValue(alignDirection, 0.0f);
1621         }
1622         return;
1623     }
1624 
1625     if (IsBarrier(alignRule.anchor)) {
1626         if (barriers_[alignRule.anchor].first == BarrierDirection::TOP ||
1627             barriers_[alignRule.anchor].first == BarrierDirection::BOTTOM) {
1628             childFlexItemProperty->SetAlignValue(alignDirection, recordOffsetMap_[alignRule.anchor].GetY());
1629         } else {
1630             childFlexItemProperty->SetAlignValue(alignDirection, 0.0f);
1631         }
1632         return;
1633     }
1634 
1635     switch (alignRule.vertical) {
1636         case VerticalAlign::TOP:
1637             childFlexItemProperty->SetAlignValue(
1638                 alignDirection, IsAnchorContainer(alignRule.anchor) ? 0.0f : recordOffsetMap_[alignRule.anchor].GetY());
1639             break;
1640         case VerticalAlign::CENTER:
1641             childFlexItemProperty->SetAlignValue(alignDirection,
1642                 IsAnchorContainer(alignRule.anchor)
1643                     ? containerSizeWithoutPaddingBorder_.Height() * HALF_MULTIPLY
1644                     : idNodeMap_[alignRule.anchor].layoutWrapper->GetGeometryNode()->GetMarginFrameSize().Height() *
1645                               HALF_MULTIPLY +
1646                           recordOffsetMap_[alignRule.anchor].GetY());
1647             break;
1648         case VerticalAlign::BOTTOM:
1649             childFlexItemProperty->SetAlignValue(alignDirection,
1650                 IsAnchorContainer(alignRule.anchor)
1651                     ? containerSizeWithoutPaddingBorder_.Height()
1652                     : idNodeMap_[alignRule.anchor].layoutWrapper->GetGeometryNode()->GetMarginFrameSize().Height() +
1653                           recordOffsetMap_[alignRule.anchor].GetY());
1654             break;
1655         default:
1656             break;
1657     }
1658 }
1659 
CalcHorizontalOffsetAlignLeft(const HorizontalAlign & alignRule,float & anchorWidth)1660 float RelativeContainerLayoutAlgorithm::CalcHorizontalOffsetAlignLeft(
1661     const HorizontalAlign& alignRule, float& anchorWidth)
1662 {
1663     float offsetX = 0.0f;
1664     switch (alignRule) {
1665         case HorizontalAlign::START:
1666             offsetX = 0.0f;
1667             break;
1668         case HorizontalAlign::CENTER:
1669             offsetX = anchorWidth * HALF_MULTIPLY;
1670             break;
1671         case HorizontalAlign::END:
1672             offsetX = anchorWidth;
1673             break;
1674         default:
1675             break;
1676     }
1677     return offsetX;
1678 }
1679 
CalcHorizontalOffsetAlignMiddle(const HorizontalAlign & alignRule,float & anchorWidth,float & flexItemWidth)1680 float RelativeContainerLayoutAlgorithm::CalcHorizontalOffsetAlignMiddle(
1681     const HorizontalAlign& alignRule, float& anchorWidth, float& flexItemWidth)
1682 {
1683     float offsetX = 0.0f;
1684     switch (alignRule) {
1685         case HorizontalAlign::START:
1686             offsetX = (-1) * flexItemWidth * HALF_MULTIPLY;
1687             break;
1688         case HorizontalAlign::CENTER:
1689             offsetX = (anchorWidth - flexItemWidth) * HALF_MULTIPLY;
1690             break;
1691         case HorizontalAlign::END:
1692             offsetX = anchorWidth - flexItemWidth * HALF_MULTIPLY;
1693             break;
1694         default:
1695             break;
1696     }
1697     return offsetX;
1698 }
1699 
CalcHorizontalOffsetAlignRight(const HorizontalAlign & alignRule,float & anchorWidth,float & flexItemWidth)1700 float RelativeContainerLayoutAlgorithm::CalcHorizontalOffsetAlignRight(
1701     const HorizontalAlign& alignRule, float& anchorWidth, float& flexItemWidth)
1702 {
1703     float offsetX = 0.0f;
1704     switch (alignRule) {
1705         case HorizontalAlign::START:
1706             offsetX = (-1) * flexItemWidth;
1707             break;
1708         case HorizontalAlign::CENTER:
1709             offsetX = anchorWidth * HALF_MULTIPLY - flexItemWidth;
1710             break;
1711         case HorizontalAlign::END:
1712             offsetX = anchorWidth - flexItemWidth;
1713             break;
1714         default:
1715             break;
1716     }
1717     return offsetX;
1718 }
1719 
CalcHorizontalOffset(AlignDirection alignDirection,const AlignRule & alignRule,float containerWidth,const std::string & nodeName)1720 float RelativeContainerLayoutAlgorithm::CalcHorizontalOffset(
1721     AlignDirection alignDirection, const AlignRule& alignRule, float containerWidth, const std::string& nodeName)
1722 {
1723     float offsetX = 0.0f;
1724     CHECK_NULL_RETURN(idNodeMap_.find(nodeName) != idNodeMap_.end(), offsetX);
1725     auto childWrapper = idNodeMap_[nodeName].layoutWrapper;
1726     bool anchorIsContainer = IsAnchorContainer(alignRule.anchor);
1727     float flexItemWidth = childWrapper->GetGeometryNode()->GetMarginFrameSize().Width();
1728     float anchorWidth;
1729     if (!versionGreatorOrEqualToEleven_) {
1730         anchorWidth = anchorIsContainer
1731                           ? containerWidth
1732                           : idNodeMap_[alignRule.anchor].layoutWrapper->GetGeometryNode()->GetMarginFrameSize().Width();
1733     } else {
1734         if (IsGuideline(alignRule.anchor) || IsBarrier(alignRule.anchor)) {
1735             anchorWidth = 0;
1736         } else if (anchorIsContainer) {
1737             anchorWidth = containerWidth;
1738         } else {
1739             anchorWidth = idNodeMap_[alignRule.anchor].layoutWrapper->GetGeometryNode()->GetFrameSize().Width();
1740         }
1741     }
1742     std::optional<float> marginLeft;
1743     if (!anchorIsContainer && !IsGuideline(alignRule.anchor) && !IsBarrier(alignRule.anchor)) {
1744         auto anchorWrapper = idNodeMap_[alignRule.anchor].layoutWrapper;
1745         if (anchorWrapper->GetGeometryNode()->GetMargin()) {
1746             marginLeft = anchorWrapper->GetGeometryNode()->GetMargin()->left;
1747         }
1748     }
1749     switch (alignDirection) {
1750         case AlignDirection::LEFT:
1751             offsetX = CalcHorizontalOffsetAlignLeft(alignRule.horizontal, anchorWidth);
1752             break;
1753         case AlignDirection::MIDDLE:
1754             offsetX = CalcHorizontalOffsetAlignMiddle(alignRule.horizontal, anchorWidth, flexItemWidth);
1755             break;
1756         case AlignDirection::RIGHT:
1757             offsetX = CalcHorizontalOffsetAlignRight(alignRule.horizontal, anchorWidth, flexItemWidth);
1758             break;
1759         default:
1760             break;
1761     }
1762 
1763     if (!versionGreatorOrEqualToEleven_) {
1764         offsetX += anchorIsContainer ? 0.0f : recordOffsetMap_[alignRule.anchor].GetX();
1765     } else {
1766         offsetX += anchorIsContainer ? 0.0f : recordOffsetMap_[alignRule.anchor].GetX() + marginLeft.value_or(0);
1767     }
1768     return offsetX;
1769 }
1770 
CalcVerticalOffsetAlignTop(const VerticalAlign & alignRule,float & anchorHeight)1771 float RelativeContainerLayoutAlgorithm::CalcVerticalOffsetAlignTop(const VerticalAlign& alignRule, float& anchorHeight)
1772 {
1773     float offsetY = 0.0f;
1774     switch (alignRule) {
1775         case VerticalAlign::TOP:
1776             offsetY = 0.0f;
1777             break;
1778         case VerticalAlign::CENTER:
1779             offsetY = anchorHeight * HALF_MULTIPLY;
1780             break;
1781         case VerticalAlign::BOTTOM:
1782             offsetY = anchorHeight;
1783             break;
1784         default:
1785             break;
1786     }
1787     return offsetY;
1788 }
1789 
CalcVerticalOffsetAlignCenter(const VerticalAlign & alignRule,float & anchorHeight,float & flexItemHeight)1790 float RelativeContainerLayoutAlgorithm::CalcVerticalOffsetAlignCenter(
1791     const VerticalAlign& alignRule, float& anchorHeight, float& flexItemHeight)
1792 {
1793     float offsetY = 0.0f;
1794     switch (alignRule) {
1795         case VerticalAlign::TOP:
1796             offsetY = (-1) * flexItemHeight * HALF_MULTIPLY;
1797             break;
1798         case VerticalAlign::CENTER:
1799             offsetY = (anchorHeight - flexItemHeight) * HALF_MULTIPLY;
1800             break;
1801         case VerticalAlign::BOTTOM:
1802             offsetY = anchorHeight - flexItemHeight * HALF_MULTIPLY;
1803             break;
1804         default:
1805             break;
1806     }
1807     return offsetY;
1808 }
1809 
CalcVerticalOffsetAlignBottom(const VerticalAlign & alignRule,float & anchorHeight,float & flexItemHeight)1810 float RelativeContainerLayoutAlgorithm::CalcVerticalOffsetAlignBottom(
1811     const VerticalAlign& alignRule, float& anchorHeight, float& flexItemHeight)
1812 {
1813     float offsetY = 0.0f;
1814     switch (alignRule) {
1815         case VerticalAlign::TOP:
1816             offsetY = (-1) * flexItemHeight;
1817             break;
1818         case VerticalAlign::CENTER:
1819             offsetY = anchorHeight * HALF_MULTIPLY - flexItemHeight;
1820             break;
1821         case VerticalAlign::BOTTOM:
1822             offsetY = anchorHeight - flexItemHeight;
1823             break;
1824         default:
1825             break;
1826     }
1827     return offsetY;
1828 }
1829 
CalcVerticalOffset(AlignDirection alignDirection,const AlignRule & alignRule,float containerHeight,const std::string & nodeName)1830 float RelativeContainerLayoutAlgorithm::CalcVerticalOffset(
1831     AlignDirection alignDirection, const AlignRule& alignRule, float containerHeight, const std::string& nodeName)
1832 {
1833     float offsetY = 0.0f;
1834     CHECK_NULL_RETURN(idNodeMap_.find(nodeName) != idNodeMap_.end(), offsetY);
1835     auto childWrapper = idNodeMap_[nodeName].layoutWrapper;
1836     bool anchorIsContainer = IsAnchorContainer(alignRule.anchor);
1837     float flexItemHeight = childWrapper->GetGeometryNode()->GetMarginFrameSize().Height();
1838     float anchorHeight;
1839     if (!versionGreatorOrEqualToEleven_) {
1840         anchorHeight =
1841             anchorIsContainer
1842                 ? containerHeight
1843                 : idNodeMap_[alignRule.anchor].layoutWrapper->GetGeometryNode()->GetMarginFrameSize().Height();
1844     } else {
1845         if (IsGuideline(alignRule.anchor) || IsBarrier(alignRule.anchor)) {
1846             anchorHeight = 0;
1847         } else if (anchorIsContainer) {
1848             anchorHeight = containerHeight;
1849         } else {
1850             anchorHeight = idNodeMap_[alignRule.anchor].layoutWrapper->GetGeometryNode()->GetFrameSize().Height();
1851         }
1852     }
1853     std::optional<float> marginTop;
1854     if (!anchorIsContainer && !IsGuideline(alignRule.anchor) && !IsBarrier(alignRule.anchor)) {
1855         auto anchorWrapper = idNodeMap_[alignRule.anchor].layoutWrapper;
1856         if (anchorWrapper->GetGeometryNode()->GetMargin()) {
1857             marginTop = anchorWrapper->GetGeometryNode()->GetMargin()->top;
1858         }
1859     }
1860     switch (alignDirection) {
1861         case AlignDirection::TOP:
1862             offsetY = CalcVerticalOffsetAlignTop(alignRule.vertical, anchorHeight);
1863             break;
1864         case AlignDirection::CENTER:
1865             offsetY = CalcVerticalOffsetAlignCenter(alignRule.vertical, anchorHeight, flexItemHeight);
1866             break;
1867         case AlignDirection::BOTTOM:
1868             offsetY = CalcVerticalOffsetAlignBottom(alignRule.vertical, anchorHeight, flexItemHeight);
1869             break;
1870         default:
1871             break;
1872     }
1873     if (!versionGreatorOrEqualToEleven_) {
1874         offsetY += anchorIsContainer ? 0.0f : recordOffsetMap_[alignRule.anchor].GetY();
1875     } else {
1876         offsetY += anchorIsContainer ? 0.0f : recordOffsetMap_[alignRule.anchor].GetY() + marginTop.value_or(0);
1877     }
1878     return offsetY;
1879 }
1880 
IsAnchorLegal(const std::string & anchorName)1881 bool RelativeContainerLayoutAlgorithm::IsAnchorLegal(const std::string& anchorName)
1882 {
1883     if (!IsAnchorContainer(anchorName) && !IsGuideline(anchorName) && !IsBarrier(anchorName) &&
1884         idNodeMap_.find(anchorName) == idNodeMap_.end()) {
1885         return false;
1886     }
1887     return true;
1888 }
1889 
BarrierDirectionRtl(BarrierDirection barrierDirection)1890 BarrierDirection RelativeContainerLayoutAlgorithm::BarrierDirectionRtl(BarrierDirection barrierDirection)
1891 {
1892     auto barrierDirectionRtl = barrierDirection;
1893     if (barrierDirection == BarrierDirection::START) {
1894         barrierDirectionRtl = BarrierDirection::LEFT;
1895     } else if (barrierDirection == BarrierDirection::END) {
1896         barrierDirectionRtl = BarrierDirection::RIGHT;
1897     }
1898     return barrierDirectionRtl;
1899 }
1900 
AdjustOffsetRtl(LayoutWrapper * layoutWrapper)1901 void RelativeContainerLayoutAlgorithm::AdjustOffsetRtl(LayoutWrapper* layoutWrapper)
1902 {
1903     auto textDirection = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection();
1904     if (textDirection != TextDirection::RTL) {
1905         return;
1906     }
1907     auto containerWidth = layoutWrapper->GetGeometryNode()->GetFrameSize().Width();
1908     for (const auto& nodeName : renderList_) {
1909         auto it = idNodeMap_.find(nodeName);
1910         if (it == idNodeMap_.end()) {
1911             continue;
1912         }
1913         auto childWrapper = idNodeMap_[nodeName].layoutWrapper;
1914         if (!childWrapper) {
1915             continue;
1916         }
1917         auto oldNodeX = recordOffsetMap_[nodeName].GetX();
1918         auto oldNodeY = recordOffsetMap_[nodeName].GetY();
1919         auto nodeWidth = childWrapper->GetGeometryNode()->GetMarginFrameSize().Width();
1920         auto newNodeX = containerWidth - nodeWidth - oldNodeX - padding_.Width();
1921         recordOffsetMap_[nodeName] = OffsetF(newNodeX, oldNodeY);
1922     }
1923 }
1924 } // namespace OHOS::Ace::NG
1925