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