• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/tabs/tab_bar_layout_algorithm.h"
17 
18 #include "base/geometry/axis.h"
19 #include "base/geometry/dimension.h"
20 #include "base/geometry/ng/offset_t.h"
21 #include "base/geometry/ng/size_t.h"
22 #include "base/log/ace_trace.h"
23 #include "base/utils/utils.h"
24 #include "core/components/common/layout/grid_layout_info.h"
25 #include "core/components/common/layout/grid_system_manager.h"
26 #include "core/components/tab_bar/tab_theme.h"
27 #include "core/components_ng/base/frame_node.h"
28 #include "core/components_ng/layout/layout_algorithm.h"
29 #include "core/components_ng/pattern/image/image_layout_property.h"
30 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
31 #include "core/components_ng/pattern/tabs/tab_bar_paint_property.h"
32 #include "core/components_ng/pattern/tabs/tab_bar_pattern.h"
33 #include "core/components_ng/pattern/tabs/tabs_layout_property.h"
34 #include "core/components_ng/pattern/tabs/tabs_node.h"
35 #include "core/components_ng/pattern/text/text_layout_property.h"
36 #include "core/components_ng/property/layout_constraint.h"
37 #include "core/components_ng/property/measure_property.h"
38 #include "core/components_ng/property/measure_utils.h"
39 #include "core/pipeline_ng/pipeline_context.h"
40 
41 namespace OHOS::Ace::NG {
42 namespace {
43 constexpr int8_t MASK_COUNT = 2;
44 constexpr int8_t SM_COLUMN_NUM = 4;
45 constexpr int8_t MD_COLUMN_NUM = 8;
46 constexpr int8_t LG_COLUMN_NUM = 12;
47 constexpr int8_t TWO = 2;
48 constexpr int8_t FOCUS_BOARD = 2;
49 } // namespace
50 
Measure(LayoutWrapper * layoutWrapper)51 void TabBarLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
52 {
53     CHECK_NULL_VOID(layoutWrapper);
54     auto host = layoutWrapper->GetHostNode();
55     CHECK_NULL_VOID(host);
56     auto pipelineContext = host->GetContext();
57     CHECK_NULL_VOID(pipelineContext);
58     auto tabTheme = pipelineContext->GetTheme<TabTheme>();
59     CHECK_NULL_VOID(tabTheme);
60     auto geometryNode = layoutWrapper->GetGeometryNode();
61     CHECK_NULL_VOID(geometryNode);
62     auto layoutProperty = AceType::DynamicCast<TabBarLayoutProperty>(layoutWrapper->GetLayoutProperty());
63     CHECK_NULL_VOID(layoutProperty);
64     axis_ = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
65     auto tabsNode = AceType::DynamicCast<TabsNode>(host->GetParent());
66     CHECK_NULL_VOID(tabsNode);
67     auto tabsLayoutProperty = AceType::DynamicCast<TabsLayoutProperty>(tabsNode->GetLayoutProperty());
68     CHECK_NULL_VOID(tabsLayoutProperty);
69     auto tabsDirection = tabsLayoutProperty->GetNonAutoLayoutDirection();
70     auto tabBarDirection = layoutProperty->GetLayoutDirection();
71     isRTL_ = tabBarDirection == TextDirection::RTL ||
72              (tabBarDirection == TextDirection::AUTO && tabsDirection == TextDirection::RTL);
73     auto constraint = layoutProperty->GetLayoutConstraint();
74     auto idealSize =
75         CreateIdealSize(constraint.value(), axis_, layoutProperty->GetMeasureType(MeasureType::MATCH_PARENT));
76 
77     childCount_ = layoutWrapper->GetTotalChildCount() - MASK_COUNT;
78     if (childCount_ <= 0) {
79         return;
80     }
81 
82     if (axis_ == Axis::VERTICAL && constraint->selfIdealSize.Width().has_value() &&
83         constraint->selfIdealSize.Width().value() < constraint->parentIdealSize.Width().value_or(0.0f) &&
84         constraint->selfIdealSize.Width().value() > tabTheme->GetHorizontalBottomTabMinWidth().ConvertToPx()) {
85         // Vertical tab bar may apply LayoutMode.AUTO
86         ApplyLayoutMode(layoutWrapper, constraint->selfIdealSize.Width().value());
87     }
88     if (constraint->selfIdealSize.Width().has_value() &&
89         constraint->selfIdealSize.Width().value() > constraint->parentIdealSize.Width().value_or(0.0f)) {
90         idealSize.SetWidth(static_cast<float>(
91             axis_ == Axis::HORIZONTAL                         ? constraint->parentIdealSize.Width().value_or(0.0f)
92             : tabBarStyle_ == TabBarStyle::BOTTOMTABBATSTYLE ? tabTheme->GetTabBarDefaultWidth().ConvertToPx()
93                                                              : tabTheme->GetTabBarDefaultHeight().ConvertToPx()));
94     }
95     if (constraint->selfIdealSize.Height().has_value() &&
96         constraint->selfIdealSize.Height().value() > constraint->parentIdealSize.Height().value_or(0.0f)) {
97         float height = axis_ == Axis::HORIZONTAL
98                            ? (tabBarStyle_ == TabBarStyle::BOTTOMTABBATSTYLE &&
99                                          Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)
100                                      ? tabTheme->GetBottomTabBarDefaultWidth().ConvertToPx()
101                                      : tabTheme->GetTabBarDefaultHeight().ConvertToPx())
102                            : constraint->parentIdealSize.Height().value_or(0.0f);
103 
104         idealSize.SetHeight(static_cast<float>(height));
105     }
106     if (!constraint->selfIdealSize.Width().has_value()) {
107         auto defaultWidth = idealSize.Width().value_or(0.f);
108         if (axis_ == Axis::VERTICAL) {
109             defaultWidth = static_cast<float>(tabBarStyle_ == TabBarStyle::BOTTOMTABBATSTYLE
110                                                   ? tabTheme->GetTabBarDefaultWidth().ConvertToPx()
111                                                   : tabTheme->GetTabBarDefaultHeight().ConvertToPx());
112         }
113         idealSize.SetWidth(std::clamp(defaultWidth, constraint->minSize.Width(), constraint->maxSize.Width()));
114     }
115     if (!constraint->selfIdealSize.Height().has_value()) {
116         if (axis_ == Axis::HORIZONTAL) {
117             defaultHeight_ = (tabBarStyle_ == TabBarStyle::BOTTOMTABBATSTYLE &&
118                                  Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE))
119                                  ? static_cast<float>(tabTheme->GetBottomTabBarDefaultWidth().ConvertToPx())
120                                  : static_cast<float>(tabTheme->GetTabBarDefaultHeight().ConvertToPx());
121         }
122         auto idealHeight = idealSize.Height().value_or(0.f);
123         idealSize.SetHeight(std::clamp(idealHeight, constraint->minSize.Height(), constraint->maxSize.Height()));
124     }
125 
126     auto tabBarFocusNode = host->GetFocusHub();
127     if ((axis_ == Axis::VERTICAL && NearZero(idealSize.ConvertToSizeT().Width())) ||
128         (axis_ == Axis::HORIZONTAL && NearZero(idealSize.ConvertToSizeT().Height()))) {
129         layoutWrapper->SetActive(false);
130         geometryNode->SetFrameSize(SizeF());
131         if (tabBarFocusNode) {
132             tabBarFocusNode->SetFocusable(false, false);
133         }
134         return;
135     } else {
136         layoutWrapper->SetActive(true);
137         if (tabBarFocusNode) {
138             tabBarFocusNode->SetFocusable(true, false);
139         }
140     }
141 
142     auto frameSize = idealSize.ConvertToSizeT();
143     auto padding = layoutProperty->CreatePaddingAndBorder();
144     verticalPadding_ = padding.Height();
145     auto contentSize = frameSize;
146     MinusPaddingToNonNegativeSize(padding, contentSize);
147     contentMainSize_ = GetContentMainSize(layoutWrapper, contentSize);
148     if (layoutProperty->GetTabBarMode().value_or(TabBarMode::FIXED) == TabBarMode::FIXED) {
149         MeasureFixedMode(layoutWrapper, contentSize);
150     } else {
151         MeasureScrollableMode(layoutWrapper, contentSize);
152     }
153     if (visibleItemPosition_.empty()) {
154         layoutWrapper->SetActiveChildRange(-1, -1);
155     } else {
156         layoutWrapper->SetActiveChildRange(visibleItemPosition_.begin()->first, visibleItemPosition_.rbegin()->first);
157     }
158     if (defaultHeight_ || maxHeight_) {
159         auto frameHeight = 0.0f;
160         if (isBarAdaptiveHeight_ && isNoMinHeightLimit_) {
161             frameHeight = maxHeight_.value_or(0.0f) + verticalPadding_;
162         } else {
163             frameHeight = std::max(defaultHeight_.value_or(0.0f), maxHeight_.value_or(0.0f) + verticalPadding_);
164         }
165         frameSize.SetHeight(std::clamp(frameHeight, constraint->minSize.Height(), constraint->maxSize.Height()));
166     }
167     CheckBorderAndPadding(frameSize, padding);
168     geometryNode->SetFrameSize(frameSize);
169     MeasureMask(layoutWrapper);
170 }
171 
GetContentMainSize(LayoutWrapper * layoutWrapper,const SizeF & contentSize)172 float TabBarLayoutAlgorithm::GetContentMainSize(LayoutWrapper* layoutWrapper, const SizeF& contentSize)
173 {
174     auto layoutProperty = AceType::DynamicCast<TabBarLayoutProperty>(layoutWrapper->GetLayoutProperty());
175     CHECK_NULL_RETURN(layoutProperty, 0.0f);
176     if (axis_ == Axis::HORIZONTAL) {
177         // Apply grid column options to the tab bar
178         barGridMargin_ = ApplyBarGridAlign(layoutProperty, contentSize);
179         return Positive(contentSize.Width() - barGridMargin_ * TWO) ? contentSize.Width() - barGridMargin_ * TWO : 0.0f;
180     } else {
181         barGridMargin_ = 0.0f;
182         return contentSize.Height();
183     }
184 }
185 
MeasureFixedMode(LayoutWrapper * layoutWrapper,SizeF frameSize)186 void TabBarLayoutAlgorithm::MeasureFixedMode(LayoutWrapper* layoutWrapper, SizeF frameSize)
187 {
188     auto childLayoutConstraint = GetChildConstraint(layoutWrapper, frameSize);
189     visibleItemLength_.clear();
190     visibleChildrenMainSize_ = 0.0f;
191     if (axis_ == Axis::HORIZONTAL) {
192         auto allocatedWidth = contentMainSize_ / childCount_;
193         ApplyLayoutMode(layoutWrapper, allocatedWidth);
194 
195         auto host = layoutWrapper->GetHostNode();
196         CHECK_NULL_VOID(host);
197         auto tabBarPattern = host->GetPattern<TabBarPattern>();
198         CHECK_NULL_VOID(tabBarPattern);
199         auto isApplySymmetricExtensible = false;
200         for (int32_t index = 0; index < childCount_ && childCount_ > TWO; index++) {
201             if (tabBarPattern->GetTabBarStyle(index) == TabBarStyle::BOTTOMTABBATSTYLE &&
202                 tabBarPattern->GetBottomTabBarStyle(index).symmetricExtensible) {
203                 isApplySymmetricExtensible = true;
204                 break;
205             }
206         }
207         if (!isApplySymmetricExtensible) {
208             childLayoutConstraint.selfIdealSize.SetWidth(allocatedWidth);
209         }
210         for (int32_t index = 0; index < childCount_; index++) {
211             MeasureItem(layoutWrapper, childLayoutConstraint, index);
212             visibleItemPosition_[index] = { allocatedWidth * index, allocatedWidth * (index + 1) };
213         }
214         if (isApplySymmetricExtensible) {
215             ApplySymmetricExtensible(layoutWrapper, allocatedWidth);
216             if (isBarAdaptiveHeight_) {
217                 MeasureMaxHeight(layoutWrapper, childLayoutConstraint);
218             }
219         }
220         if (isApplySymmetricExtensible || isBarAdaptiveHeight_) {
221             MeasureItemSecond(layoutWrapper, childLayoutConstraint, frameSize);
222         }
223     } else {
224         for (int32_t index = 0; index < childCount_; index++) {
225             MeasureItem(layoutWrapper, childLayoutConstraint, index);
226         }
227     }
228 
229     visibleItemPosition_.clear();
230     auto currentOffset =
231         (axis_ == Axis::VERTICAL && tabBarStyle_ == TabBarStyle::BOTTOMTABBATSTYLE) ? contentMainSize_ / 4 : 0.0f;
232     for (int32_t index = 0; index < childCount_; index++) {
233         visibleItemPosition_[index] = { currentOffset, currentOffset + visibleItemLength_[index] };
234         currentOffset += visibleItemLength_[index];
235     }
236 }
237 
UpdateMaxLines(LayoutWrapper * layoutWrapper,int32_t index)238 void TabBarLayoutAlgorithm::UpdateMaxLines(LayoutWrapper* layoutWrapper, int32_t index)
239 {
240     auto host = layoutWrapper->GetHostNode();
241     CHECK_NULL_VOID(host);
242     CHECK_NULL_VOID((tabBarStyle_ == TabBarStyle::SUBTABBATSTYLE) && (NeedAdaptForAging(host)
243         && (axis_ == Axis::VERTICAL)));
244     CHECK_NULL_VOID(layoutWrapper);
245     auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
246     CHECK_NULL_VOID(childWrapper);
247     auto textWrapper = childWrapper->GetOrCreateChildByIndex(1);
248     CHECK_NULL_VOID(textWrapper);
249     auto textLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(textWrapper->GetLayoutProperty());
250     CHECK_NULL_VOID(textLayoutProperty);
251     textLayoutProperty->UpdateMaxLines(TWO);
252 }
253 
MeasureScrollableMode(LayoutWrapper * layoutWrapper,SizeF frameSize)254 void TabBarLayoutAlgorithm::MeasureScrollableMode(LayoutWrapper* layoutWrapper, SizeF frameSize)
255 {
256     auto childLayoutConstraint = GetChildConstraint(layoutWrapper, frameSize);
257     auto layoutProperty = AceType::DynamicCast<TabBarLayoutProperty>(layoutWrapper->GetLayoutProperty());
258     CHECK_NULL_VOID(layoutProperty);
259     ScrollableBarModeOptions layoutStyle;
260     if (axis_ == Axis::HORIZONTAL) {
261         auto host = layoutWrapper->GetHostNode();
262         CHECK_NULL_VOID(host);
263         auto pipelineContext = host->GetContext();
264         CHECK_NULL_VOID(pipelineContext);
265         auto tabTheme = pipelineContext->GetTheme<TabTheme>();
266         CHECK_NULL_VOID(tabTheme);
267         ScrollableBarModeOptions defaultOptions;
268         defaultOptions.margin = tabTheme->GetTabBarDefaultMargin();
269         layoutStyle = layoutProperty->GetScrollableBarModeOptions().value_or(defaultOptions);
270         scrollMargin_ = layoutStyle.margin.ConvertToPx();
271         MeasureVisibleItems(layoutWrapper, childLayoutConstraint);
272 
273         useItemWidth_ = true;
274         if (GreatNotEqual(visibleChildrenMainSize_, contentMainSize_) ||
275             childCount_ > static_cast<int32_t>(visibleItemPosition_.size())) {
276             useItemWidth_ = false;
277         } else {
278             visibleChildrenMainSize_ -= scrollMargin_ * TWO;
279             if (layoutStyle.nonScrollableLayoutStyle.value_or(LayoutStyle::ALWAYS_CENTER) ==
280                 LayoutStyle::ALWAYS_CENTER) {
281                 useItemWidth_ = false;
282             } else if (layoutStyle.nonScrollableLayoutStyle.value() == LayoutStyle::ALWAYS_AVERAGE_SPLIT) {
283                 HandleAlwaysAverageSplitLayoutStyle(layoutWrapper);
284             } else if (layoutStyle.nonScrollableLayoutStyle.value() == LayoutStyle::SPACE_BETWEEN_OR_CENTER) {
285                 HandleSpaceBetweenOrCenterLayoutStyle(layoutWrapper);
286             }
287             scrollMargin_ = 0.0f;
288         }
289 
290         if (isBarAdaptiveHeight_ || useItemWidth_) {
291             MeasureItemSecond(layoutWrapper, childLayoutConstraint, frameSize);
292         }
293     } else {
294         MeasureVisibleItems(layoutWrapper, childLayoutConstraint);
295     }
296 
297     if (LessOrEqual(visibleChildrenMainSize_, contentMainSize_) &&
298         childCount_ == static_cast<int32_t>(visibleItemPosition_.size())) {
299         visibleItemPosition_.clear();
300         float currentOffset = GetCurrentOffset(layoutProperty, layoutStyle);
301         for (int32_t index = 0; index < childCount_; index++) {
302             visibleItemPosition_[index] = { currentOffset, currentOffset + visibleItemLength_[index] };
303             currentOffset += visibleItemLength_[index];
304         }
305     }
306 }
307 
GetCurrentOffset(RefPtr<TabBarLayoutProperty> & layoutProperty,ScrollableBarModeOptions & layoutStyle)308 float TabBarLayoutAlgorithm::GetCurrentOffset(
309     RefPtr<TabBarLayoutProperty>& layoutProperty, ScrollableBarModeOptions& layoutStyle)
310 {
311     float currentOffset = (contentMainSize_ - visibleChildrenMainSize_) / TWO;
312     if (layoutStyle.nonScrollableLayoutStyle.has_value()) {
313         return currentOffset;
314     }
315     Alignment alignment = Alignment::CENTER;
316     if (layoutProperty->GetPositionProperty()) {
317         alignment = layoutProperty->GetPositionProperty()->GetAlignment().value_or(Alignment::CENTER);
318     }
319     if (axis_ == Axis::HORIZONTAL) {
320         float margin = layoutStyle.margin.ConvertToPx();
321         currentOffset = (1.0 + alignment.GetHorizontal()) * (contentMainSize_ - visibleChildrenMainSize_) / TWO;
322         currentOffset -= alignment.GetHorizontal() * margin;
323     } else {
324         currentOffset = (1.0 + alignment.GetVertical()) * (contentMainSize_ - visibleChildrenMainSize_) / TWO;
325     }
326     return currentOffset;
327 }
328 
CheckBorderAndPadding(SizeF & frameSize,const PaddingPropertyF & padding)329 void TabBarLayoutAlgorithm::CheckBorderAndPadding(SizeF& frameSize, const PaddingPropertyF& padding)
330 {
331     if (GreatNotEqual(padding.Width(), frameSize.Width())) {
332         frameSize.SetWidth(padding.Width());
333     }
334     if (GreatNotEqual(padding.Height(), frameSize.Height())) {
335         frameSize.SetHeight(padding.Height());
336     }
337 }
338 
NeedAdaptForAging(RefPtr<FrameNode> host)339 bool TabBarLayoutAlgorithm::NeedAdaptForAging(RefPtr<FrameNode> host)
340 {
341     CHECK_NULL_RETURN(host, false);
342     auto pipeline = host->GetContext();
343     CHECK_NULL_RETURN(pipeline, false);
344     auto tabTheme = pipeline->GetTheme<TabTheme>();
345     CHECK_NULL_RETURN(tabTheme, false);
346 
347     if (GreatOrEqual(pipeline->GetFontScale(), tabTheme->GetSubTabBarBigFontSizeScale())) {
348         return true;
349     }
350     return false;
351 }
352 
GetBarAdaptiveHeight(LayoutWrapper * layoutWrapper)353 bool TabBarLayoutAlgorithm::GetBarAdaptiveHeight(LayoutWrapper* layoutWrapper)
354 {
355     CHECK_NULL_RETURN(defaultHeight_, false);
356     auto layoutProperty = AceType::DynamicCast<TabBarLayoutProperty>(layoutWrapper->GetLayoutProperty());
357     CHECK_NULL_RETURN(layoutProperty, false);
358     auto isBarAdaptiveHeight = layoutProperty->GetBarAdaptiveHeight().value_or(false);
359 
360     auto host = layoutWrapper->GetHostNode();
361     CHECK_NULL_RETURN(host, isBarAdaptiveHeight);
362     auto pipeline = host->GetContext();
363     CHECK_NULL_RETURN(pipeline, isBarAdaptiveHeight);
364     auto tabTheme = pipeline->GetTheme<TabTheme>();
365     CHECK_NULL_RETURN(tabTheme, isBarAdaptiveHeight);
366     if (tabBarStyle_ == TabBarStyle::SUBTABBATSTYLE &&
367         GreatOrEqual(pipeline->GetFontScale(), tabTheme->GetsubTabBarThirdLargeFontSizeScale())) {
368         isBarAdaptiveHeight = true;
369     }
370     return isBarAdaptiveHeight;
371 }
372 
GetNoMinHeightLimit(LayoutWrapper * layoutWrapper)373 bool TabBarLayoutAlgorithm::GetNoMinHeightLimit(LayoutWrapper* layoutWrapper)
374 {
375     auto layoutProperty = AceType::DynamicCast<TabBarLayoutProperty>(layoutWrapper->GetLayoutProperty());
376     CHECK_NULL_RETURN(layoutProperty, false);
377     auto isNoMinHeightLimit = layoutProperty->GetNoMinHeightLimit().value_or(false);
378     auto host = layoutWrapper->GetHostNode();
379     CHECK_NULL_RETURN(host, isNoMinHeightLimit);
380     auto pipeline = host->GetContext();
381     CHECK_NULL_RETURN(pipeline, isNoMinHeightLimit);
382     auto tabTheme = pipeline->GetTheme<TabTheme>();
383     CHECK_NULL_RETURN(tabTheme, isNoMinHeightLimit);
384     if (tabBarStyle_ == TabBarStyle::SUBTABBATSTYLE &&
385         GreatOrEqual(pipeline->GetFontScale(), tabTheme->GetsubTabBarThirdLargeFontSizeScale())) {
386         isNoMinHeightLimit = true;
387     }
388     return isNoMinHeightLimit;
389 }
390 
GetChildConstraint(LayoutWrapper * layoutWrapper,SizeF & frameSize)391 LayoutConstraintF TabBarLayoutAlgorithm::GetChildConstraint(LayoutWrapper* layoutWrapper, SizeF& frameSize)
392 {
393     auto layoutProperty = AceType::DynamicCast<TabBarLayoutProperty>(layoutWrapper->GetLayoutProperty());
394     CHECK_NULL_RETURN(layoutProperty, {});
395     auto host = layoutWrapper->GetHostNode();
396     CHECK_NULL_RETURN(host, {});
397     auto pipelineContext = host->GetContext();
398     CHECK_NULL_RETURN(pipelineContext, {});
399     auto tabTheme = pipelineContext->GetTheme<TabTheme>();
400     CHECK_NULL_RETURN(tabTheme, {});
401     auto focusBoardPadding = tabTheme->GetBoardFocusPadding().ConvertToPx();
402     auto childLayoutConstraint = layoutProperty->CreateChildConstraint();
403     if (axis_ == Axis::HORIZONTAL) {
404         isBarAdaptiveHeight_ = GetBarAdaptiveHeight(layoutWrapper);
405         isNoMinHeightLimit_ = GetNoMinHeightLimit(layoutWrapper);
406         childLayoutConstraint.maxSize.SetWidth(Infinity<float>());
407         if (tabBarStyle_ == TabBarStyle::SUBTABBATSTYLE) {
408             childLayoutConstraint.minSize.SetWidth(tabTheme->GetSubTabBarMinWidth().ConvertToPx());
409         }
410         if (!defaultHeight_.has_value()) {
411             childLayoutConstraint.parentIdealSize = OptionalSizeF(frameSize);
412             childLayoutConstraint.selfIdealSize.SetHeight(frameSize.Height());
413         } else if (!isBarAdaptiveHeight_) {
414             frameSize.SetHeight(defaultHeight_.value() - verticalPadding_);
415             frameSize.MinusHeight(focusBoardPadding * FOCUS_BOARD);
416             childLayoutConstraint.parentIdealSize = OptionalSizeF(frameSize);
417             childLayoutConstraint.selfIdealSize.SetHeight(frameSize.Height());
418         }
419     } else {
420         childLayoutConstraint.parentIdealSize = OptionalSizeF(frameSize);
421         if (layoutProperty->GetTabBarMode().value_or(TabBarMode::FIXED) == TabBarMode::FIXED) {
422             frameSize.SetHeight(tabBarStyle_ == TabBarStyle::BOTTOMTABBATSTYLE
423                                     ? frameSize.Height() / TWO / childCount_
424                                     : frameSize.Height() / childCount_);
425             childLayoutConstraint.selfIdealSize = OptionalSizeF(frameSize);
426         } else {
427             frameSize.MinusWidth(focusBoardPadding * FOCUS_BOARD);
428             childLayoutConstraint.maxSize.SetHeight(Infinity<float>());
429             childLayoutConstraint.selfIdealSize.SetWidth(frameSize.Width());
430         }
431     }
432     return childLayoutConstraint;
433 }
434 
MeasureVisibleItems(LayoutWrapper * layoutWrapper,LayoutConstraintF & childLayoutConstraint)435 void TabBarLayoutAlgorithm::MeasureVisibleItems(LayoutWrapper* layoutWrapper, LayoutConstraintF& childLayoutConstraint)
436 {
437     visibleItemLength_.clear();
438     visibleChildrenMainSize_ = scrollMargin_ * TWO;
439     startMainPos_ = 0.0f;
440     endMainPos_ = contentMainSize_;
441 
442     if (targetIndex_) {
443         targetIndex_ = targetIndex_.value() % childCount_;
444         MeasureTargetIndex(layoutWrapper, childLayoutConstraint);
445     } else if (jumpIndex_) {
446         if (jumpIndex_.value() >= childCount_) {
447             jumpIndex_ = 0;
448         }
449         MeasureJumpIndex(layoutWrapper, childLayoutConstraint);
450         if (GreatNotEqual(visibleChildrenMainSize_, scrollMargin_ * TWO)) {
451             jumpIndex_.reset();
452         }
453     } else if (focusIndex_) {
454         MeasureFocusIndex(layoutWrapper, childLayoutConstraint);
455     } else {
456         MeasureWithOffset(layoutWrapper, childLayoutConstraint);
457     }
458 }
459 
MeasureTargetIndex(LayoutWrapper * layoutWrapper,LayoutConstraintF & childLayoutConstraint)460 void TabBarLayoutAlgorithm::MeasureTargetIndex(LayoutWrapper* layoutWrapper, LayoutConstraintF& childLayoutConstraint)
461 {
462     MeasureWithOffset(layoutWrapper, childLayoutConstraint);
463     if (GreatOrEqual(visibleItemLength_[targetIndex_.value()], endMainPos_ - startMainPos_)) {
464         return;
465     }
466 
467     if (visibleItemPosition_.empty()) {
468         return;
469     }
470     auto iter = visibleItemPosition_.find(targetIndex_.value());
471     if (iter == visibleItemPosition_.end()) {
472         return;
473     }
474     auto space = ((endMainPos_ - startMainPos_) - visibleItemLength_[targetIndex_.value()]) / TWO;
475     startMainPos_ = std::min(startMainPos_, iter->second.startPos - space);
476     endMainPos_ = std::max(endMainPos_, iter->second.endPos + space);
477     auto startIndex = visibleItemPosition_.begin()->first - 1;
478     auto startPos = visibleItemPosition_.begin()->second.startPos;
479     auto endIndex = visibleItemPosition_.rbegin()->first + 1;
480     auto endPos = visibleItemPosition_.rbegin()->second.endPos;
481     LayoutForward(layoutWrapper, childLayoutConstraint, endIndex, endPos);
482     LayoutBackward(layoutWrapper, childLayoutConstraint, startIndex, startPos);
483 
484     startMainPos_ = 0.0f;
485     endMainPos_ = contentMainSize_;
486     AdjustPosition(layoutWrapper, childLayoutConstraint, startIndex, endIndex, startPos, endPos);
487 }
488 
MeasureJumpIndex(LayoutWrapper * layoutWrapper,LayoutConstraintF & childLayoutConstraint)489 void TabBarLayoutAlgorithm::MeasureJumpIndex(LayoutWrapper* layoutWrapper, LayoutConstraintF& childLayoutConstraint)
490 {
491     visibleItemPosition_.clear();
492     MeasureItem(layoutWrapper, childLayoutConstraint, jumpIndex_.value());
493     if (GreatOrEqual(visibleItemLength_[jumpIndex_.value()], endMainPos_ - startMainPos_)) {
494         visibleItemPosition_[jumpIndex_.value()] = { 0.0f, visibleItemLength_[jumpIndex_.value()] };
495         return;
496     }
497 
498     auto startIndex = jumpIndex_.value() - 1;
499     auto startPos = ((endMainPos_ - startMainPos_) - visibleItemLength_[jumpIndex_.value()]) / TWO;
500     auto endIndex = jumpIndex_.value() + 1;
501     auto endPos = ((endMainPos_ - startMainPos_) + visibleItemLength_[jumpIndex_.value()]) / TWO;
502     visibleItemPosition_[jumpIndex_.value()] = { startPos, endPos };
503     LayoutForward(layoutWrapper, childLayoutConstraint, endIndex, endPos);
504     LayoutBackward(layoutWrapper, childLayoutConstraint, startIndex, startPos);
505 
506     AdjustPosition(layoutWrapper, childLayoutConstraint, startIndex, endIndex, startPos, endPos);
507 }
508 
MeasureFocusIndex(LayoutWrapper * layoutWrapper,LayoutConstraintF & childLayoutConstraint)509 void TabBarLayoutAlgorithm::MeasureFocusIndex(LayoutWrapper* layoutWrapper, LayoutConstraintF& childLayoutConstraint)
510 {
511     if (visibleItemPosition_.empty()) {
512         return;
513     }
514     auto startIndex = focusIndex_.value();
515     auto startPos = endMainPos_;
516     auto endIndex = focusIndex_.value();
517     auto endPos = 0.0f;
518 
519     auto iter = visibleItemPosition_.find(focusIndex_.value());
520     if ((iter != visibleItemPosition_.end() && LessNotEqual(iter->second.startPos, 0.0f)) ||
521         focusIndex_.value() < visibleItemPosition_.begin()->first) {
522         if (focusIndex_.value() == 0) {
523             endPos += scrollMargin_;
524         }
525         startIndex = endIndex - 1;
526         startPos = endPos;
527     } else if ((iter != visibleItemPosition_.end() && GreatNotEqual(iter->second.endPos, contentMainSize_)) ||
528                focusIndex_.value() > visibleItemPosition_.rbegin()->first) {
529         if (focusIndex_.value() == childCount_ - 1) {
530             startPos -= scrollMargin_;
531         }
532         endIndex = startIndex + 1;
533         endPos = startPos;
534     } else {
535         return;
536     }
537     visibleItemPosition_.clear();
538     LayoutForward(layoutWrapper, childLayoutConstraint, endIndex, endPos);
539     LayoutBackward(layoutWrapper, childLayoutConstraint, startIndex, startPos);
540     if (!canOverScroll_) {
541         AdjustPosition(layoutWrapper, childLayoutConstraint, startIndex, endIndex, startPos, endPos);
542     }
543 }
544 
MeasureWithOffset(LayoutWrapper * layoutWrapper,LayoutConstraintF & childLayoutConstraint)545 void TabBarLayoutAlgorithm::MeasureWithOffset(LayoutWrapper* layoutWrapper, LayoutConstraintF& childLayoutConstraint)
546 {
547     auto startIndex = -1;
548     auto startPos = scrollMargin_;
549     auto endIndex = 0;
550     auto endPos = scrollMargin_;
551     if (isRTL_ && axis_ == Axis::HORIZONTAL) {
552         currentDelta_ = -currentDelta_;
553     }
554     if (NonNegative(currentDelta_)) {
555         if (!visibleItemPosition_.empty()) {
556             endIndex = visibleItemPosition_.begin()->first;
557             endPos = visibleItemPosition_.begin()->second.startPos;
558         }
559         startIndex = endIndex - 1;
560         startPos = endPos;
561     } else {
562         if (!visibleItemPosition_.empty()) {
563             startIndex = visibleItemPosition_.rbegin()->first;
564             startPos = visibleItemPosition_.rbegin()->second.endPos;
565         }
566         endIndex = startIndex + 1;
567         endPos = startPos;
568     }
569 
570     startPos += currentDelta_;
571     endPos += currentDelta_;
572     visibleItemPosition_.clear();
573     LayoutForward(layoutWrapper, childLayoutConstraint, endIndex, endPos);
574     LayoutBackward(layoutWrapper, childLayoutConstraint, startIndex, startPos);
575 
576     if (!canOverScroll_) {
577         AdjustPosition(layoutWrapper, childLayoutConstraint, startIndex, endIndex, startPos, endPos);
578     }
579 }
580 
AdjustPosition(LayoutWrapper * layoutWrapper,LayoutConstraintF & childLayoutConstraint,int32_t startIndex,int32_t endIndex,float startPos,float endPos)581 void TabBarLayoutAlgorithm::AdjustPosition(LayoutWrapper* layoutWrapper, LayoutConstraintF& childLayoutConstraint,
582     int32_t startIndex, int32_t endIndex, float startPos, float endPos)
583 {
584     if (GreatNotEqual(startPos, startMainPos_ + scrollMargin_)) {
585         auto offset = startPos - startMainPos_ - scrollMargin_;
586         for (auto& pos : visibleItemPosition_) {
587             pos.second.startPos -= offset;
588             pos.second.endPos -= offset;
589         }
590         endPos -= offset;
591         LayoutForward(layoutWrapper, childLayoutConstraint, endIndex, endPos);
592     } else if (LessNotEqual(endPos, endMainPos_ - scrollMargin_)) {
593         auto offset = endMainPos_ - scrollMargin_ - endPos;
594         for (auto& pos : visibleItemPosition_) {
595             pos.second.startPos += offset;
596             pos.second.endPos += offset;
597         }
598         startPos += offset;
599         LayoutBackward(layoutWrapper, childLayoutConstraint, startIndex, startPos);
600     }
601 }
602 
LayoutForward(LayoutWrapper * layoutWrapper,LayoutConstraintF & childLayoutConstraint,int32_t & endIndex,float & endPos)603 void TabBarLayoutAlgorithm::LayoutForward(LayoutWrapper* layoutWrapper, LayoutConstraintF& childLayoutConstraint,
604     int32_t& endIndex, float& endPos)
605 {
606     // 1.When first item is invisible and located to the right or below the tab bar, measure at least one item.
607     // 2.When set the height of tab bar to auto, measure all items to find max height of items.
608     // 3.If target index exists, measure items from the end index to target index.
609     while (endIndex < childCount_ && (endIndex == 0 || LessNotEqual(endPos, endMainPos_) || isBarAdaptiveHeight_ ||
610         (targetIndex_ && endIndex <= targetIndex_.value()))) {
611         if (endIndex < 0) {
612             endIndex = 0;
613             continue;
614         }
615         MeasureItem(layoutWrapper, childLayoutConstraint, endIndex);
616         visibleItemPosition_[endIndex] = { endPos, endPos + visibleItemLength_[endIndex] };
617         endPos += visibleItemLength_[endIndex];
618         if (endIndex < childCount_ - 1 && LessOrEqual(endPos, startMainPos_) && !isBarAdaptiveHeight_ &&
619             !targetIndex_.has_value()) {
620             visibleChildrenMainSize_ -= visibleItemLength_[endIndex];
621             visibleItemLength_.erase(endIndex);
622             visibleItemPosition_.erase(endIndex);
623         }
624         endIndex++;
625     }
626 }
627 
LayoutBackward(LayoutWrapper * layoutWrapper,LayoutConstraintF & childLayoutConstraint,int32_t & startIndex,float & startPos)628 void TabBarLayoutAlgorithm::LayoutBackward(LayoutWrapper* layoutWrapper, LayoutConstraintF& childLayoutConstraint,
629     int32_t& startIndex, float& startPos)
630 {
631     // 1.When last item is invisible and located to the left or above the tab bar, measure at least one item.
632     // 2.When set the height of tab bar to auto, measure all items to find max height of items.
633     // 3.If target index exists, measure items from the start index to target index.
634     while (startIndex >= 0 && (startIndex == childCount_ - 1 || GreatNotEqual(startPos, startMainPos_) ||
635         isBarAdaptiveHeight_ || (targetIndex_ && startIndex >= targetIndex_.value()))) {
636         if (startIndex >= childCount_) {
637             startIndex = childCount_ - 1;
638             continue;
639         }
640         MeasureItem(layoutWrapper, childLayoutConstraint, startIndex);
641         visibleItemPosition_[startIndex] = { startPos - visibleItemLength_[startIndex], startPos };
642         startPos -= visibleItemLength_[startIndex];
643         if (startIndex > 0 && GreatOrEqual(startPos, endMainPos_) && !isBarAdaptiveHeight_ &&
644             !targetIndex_.has_value()) {
645             visibleChildrenMainSize_ -= visibleItemLength_[startIndex];
646             visibleItemLength_.erase(startIndex);
647             visibleItemPosition_.erase(startIndex);
648         }
649         startIndex--;
650     }
651 }
652 
MeasureItem(LayoutWrapper * layoutWrapper,LayoutConstraintF & childLayoutConstraint,int32_t index)653 void TabBarLayoutAlgorithm::MeasureItem(LayoutWrapper* layoutWrapper, LayoutConstraintF& childLayoutConstraint,
654     int32_t index)
655 {
656     auto host = layoutWrapper->GetHostNode();
657     CHECK_NULL_VOID(host);
658     auto tabBarPattern = host->GetPattern<TabBarPattern>();
659     CHECK_NULL_VOID(tabBarPattern);
660     auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
661     CHECK_NULL_VOID (childWrapper);
662     auto layoutProperty = AceType::DynamicCast<TabBarLayoutProperty>(layoutWrapper->GetLayoutProperty());
663     CHECK_NULL_VOID(layoutProperty);
664     if (tabBarPattern->GetTabBarStyle(index) == TabBarStyle::BOTTOMTABBATSTYLE && axis_ == Axis::HORIZONTAL) {
665         auto iconWrapper = childWrapper->GetOrCreateChildByIndex(0);
666         CHECK_NULL_VOID(iconWrapper);
667         if (iconWrapper->GetHostNode()->GetTag() == V2::SYMBOL_ETS_TAG) {
668             auto symbolLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(iconWrapper->GetLayoutProperty());
669             CHECK_NULL_VOID(symbolLayoutProperty);
670             symbolLayoutProperty->UpdateMargin({ CalcLength(0.0_vp), CalcLength(0.0_vp), {}, {}, {}, {} });
671         } else {
672             auto imageLayoutProperty = AceType::DynamicCast<ImageLayoutProperty>(iconWrapper->GetLayoutProperty());
673             CHECK_NULL_VOID(imageLayoutProperty);
674             imageLayoutProperty->UpdateMargin({ CalcLength(0.0_vp), CalcLength(0.0_vp), {}, {}, {}, {} });
675         }
676         auto textWrapper = childWrapper->GetOrCreateChildByIndex(1);
677         CHECK_NULL_VOID(textWrapper);
678         auto textLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(textWrapper->GetLayoutProperty());
679         CHECK_NULL_VOID(textLayoutProperty);
680         textLayoutProperty->UpdateMargin({ CalcLength(0.0_vp), CalcLength(0.0_vp), {}, {}, {}, {} });
681     }
682     if (layoutProperty->GetTabBarMode().value_or(TabBarMode::FIXED) == TabBarMode::SCROLLABLE &&
683         axis_ == Axis::HORIZONTAL) {
684         auto textWrapper = childWrapper->GetOrCreateChildByIndex(1);
685         if (textWrapper) {
686             auto textLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(textWrapper->GetLayoutProperty());
687             if (textLayoutProperty &&
688                 textLayoutProperty->GetTextOverflow().value_or(TextOverflow::NONE) == TextOverflow::MARQUEE) {
689                 textLayoutProperty->UpdateTextOverflow(TextOverflow::NONE);
690             }
691         }
692     }
693     UpdateMaxLines(layoutWrapper, index);
694     SetTabBarMargin(childWrapper, index);
695     childWrapper->Measure(childLayoutConstraint);
696     auto geometryNode = childWrapper->GetGeometryNode();
697     CHECK_NULL_VOID(geometryNode);
698     visibleItemLength_[index] = geometryNode->GetMarginFrameSize().MainSize(axis_);
699     visibleChildrenMainSize_ += visibleItemLength_[index];
700     if (isBarAdaptiveHeight_) {
701         maxHeight_ = std::max(maxHeight_.value_or(0.0f), geometryNode->GetMarginFrameSize().MainSize(Axis::VERTICAL));
702     }
703 }
704 
SetTabBarMargin(RefPtr<LayoutWrapper> layoutWrapper,int32_t index)705 void TabBarLayoutAlgorithm::SetTabBarMargin(RefPtr<LayoutWrapper> layoutWrapper, int32_t index)
706 {
707     auto host = layoutWrapper->GetHostNode();
708     CHECK_NULL_VOID(host);
709     auto tabBarPattern = host->GetPattern<TabBarPattern>();
710     CHECK_NULL_VOID(tabBarPattern);
711     CHECK_NULL_VOID(tabBarPattern->GetTabBarStyle(index) == TabBarStyle::SUBTABBATSTYLE);
712     auto pipelineContext = host->GetContext();
713     CHECK_NULL_VOID(pipelineContext);
714     auto tabTheme = pipelineContext->GetTheme<TabTheme>();
715     CHECK_NULL_VOID(tabTheme);
716     auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
717     CHECK_NULL_VOID (childWrapper);
718 
719     auto textWrapper = childWrapper->GetOrCreateChildByIndex(1);
720     CHECK_NULL_VOID(textWrapper);
721     auto textLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(textWrapper->GetLayoutProperty());
722     CHECK_NULL_VOID(textLayoutProperty);
723     if (NeedAdaptForAging(host)) {
724         textLayoutProperty->UpdateMargin({ CalcLength(tabTheme->GetSubTabBarLeftRightMargin()),
725             CalcLength(tabTheme->GetSubTabBarLeftRightMargin()), {}, {}, {}, {} });
726     } else {
727         textLayoutProperty->UpdateMargin({ CalcLength(0.0_vp), CalcLength(0.0_vp), {}, {}, {}, {} });
728     }
729 }
730 
MeasureItemSecond(LayoutWrapper * layoutWrapper,LayoutConstraintF & childLayoutConstraint,SizeF & frameSize)731 void TabBarLayoutAlgorithm::MeasureItemSecond(LayoutWrapper* layoutWrapper, LayoutConstraintF& childLayoutConstraint,
732     SizeF& frameSize)
733 {
734     auto host = layoutWrapper->GetHostNode();
735     CHECK_NULL_VOID(host);
736     auto tabBarPattern = host->GetPattern<TabBarPattern>();
737     CHECK_NULL_VOID(tabBarPattern);
738 
739     visibleChildrenMainSize_ = scrollMargin_ * TWO;
740     if (isBarAdaptiveHeight_) {
741         if (isNoMinHeightLimit_) {
742             frameSize.SetHeight(maxHeight_.value_or(0.0f));
743         } else {
744             frameSize.SetHeight(std::max(defaultHeight_.value_or(0.0f) - verticalPadding_, maxHeight_.value_or(0.0f)));
745         }
746         childLayoutConstraint.parentIdealSize = OptionalSizeF(frameSize);
747         childLayoutConstraint.selfIdealSize.SetHeight(frameSize.Height());
748     }
749     for (auto& iter : visibleItemPosition_) {
750         childLayoutConstraint.selfIdealSize.SetWidth(visibleItemLength_[iter.first]);
751         auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(iter.first);
752         CHECK_NULL_VOID(childWrapper);
753         auto iconWrapper = childWrapper->GetOrCreateChildByIndex(0);
754         if (iconWrapper && iconWrapper->GetHostNode() && iconWrapper->GetHostNode()->GetTag() == V2::SYMBOL_ETS_TAG) {
755             childWrapper->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
756         }
757         childWrapper->Measure(childLayoutConstraint);
758         auto geometryNode = childWrapper->GetGeometryNode();
759         CHECK_NULL_VOID(geometryNode);
760         visibleChildrenMainSize_ += geometryNode->GetMarginFrameSize().MainSize(axis_);
761         tabBarPattern->UpdateSymbolEffect(iter.first);
762     }
763 }
764 
MeasureMask(LayoutWrapper * layoutWrapper) const765 void TabBarLayoutAlgorithm::MeasureMask(LayoutWrapper* layoutWrapper) const
766 {
767     auto layoutProperty = AceType::DynamicCast<TabBarLayoutProperty>(layoutWrapper->GetLayoutProperty());
768     CHECK_NULL_VOID(layoutProperty);
769     auto maskLayoutConstraint = layoutProperty->CreateChildConstraint();
770     auto selectedMaskWrapper = layoutWrapper->GetOrCreateChildByIndex(childCount_);
771     CHECK_NULL_VOID(selectedMaskWrapper);
772     maskLayoutConstraint.selfIdealSize = OptionalSizeF(selectedMaskWrapper->GetGeometryNode()->GetFrameSize());
773     selectedMaskWrapper->Measure(maskLayoutConstraint);
774 
775     auto unselectedMaskWrapper = layoutWrapper->GetOrCreateChildByIndex(childCount_ + 1);
776     CHECK_NULL_VOID(unselectedMaskWrapper);
777     maskLayoutConstraint.selfIdealSize = OptionalSizeF(unselectedMaskWrapper->GetGeometryNode()->GetFrameSize());
778     unselectedMaskWrapper->Measure(maskLayoutConstraint);
779 }
780 
MeasureMaxHeight(LayoutWrapper * layoutWrapper,LayoutConstraintF & childLayoutConstraint)781 void TabBarLayoutAlgorithm::MeasureMaxHeight(LayoutWrapper* layoutWrapper, LayoutConstraintF& childLayoutConstraint)
782 {
783     for (int32_t index = 0; index < childCount_; ++index) {
784         auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
785         CHECK_NULL_VOID(childWrapper);
786         if (static_cast<int32_t>(visibleItemLength_.size()) == childCount_) {
787             childLayoutConstraint.selfIdealSize.SetWidth(visibleItemLength_[index]);
788         }
789         childWrapper->Measure(childLayoutConstraint);
790         auto geometryNode = childWrapper->GetGeometryNode();
791         CHECK_NULL_VOID(geometryNode);
792         maxHeight_ = std::max(maxHeight_.value_or(0.0f), geometryNode->GetMarginFrameSize().MainSize(Axis::VERTICAL));
793     }
794 }
795 
HandleAlwaysAverageSplitLayoutStyle(LayoutWrapper * layoutWrapper)796 void TabBarLayoutAlgorithm::HandleAlwaysAverageSplitLayoutStyle(LayoutWrapper* layoutWrapper)
797 {
798     std::map<int32_t, float> originalVisibleItemLength;
799     for (int32_t index = 0; index < childCount_; index++) {
800         originalVisibleItemLength[index] = visibleItemLength_[index];
801         visibleItemLength_[index] = 0.0f;
802     }
803 
804     bool hasLongItem = false;
805     int32_t remainingChildCount = childCount_;
806     auto totalWidth = contentMainSize_ - scrollMargin_ * TWO;
807     auto allocatedItemWidth = 0.0f;
808 
809     /* Calculate the widths of long items. A long item refers to an item whose length is above the average,
810         so remainingChildCount can't be zero */
811     do {
812         allocatedItemWidth = totalWidth / remainingChildCount;
813         hasLongItem = false;
814         for (int32_t index = 0; index < childCount_; index++) {
815             if (NearZero(visibleItemLength_[index]) &&
816                 GreatNotEqual(originalVisibleItemLength[index], allocatedItemWidth)) {
817                 visibleItemLength_[index] = originalVisibleItemLength[index];
818                 hasLongItem = true;
819                 remainingChildCount--;
820                 totalWidth -= originalVisibleItemLength[index];
821             }
822         }
823     } while (hasLongItem && remainingChildCount > 0 && Positive(totalWidth));
824 
825     // Calculate the widths of other items
826     for (int32_t index = 0; index < childCount_; index++) {
827         if (NearZero(visibleItemLength_[index])) {
828             visibleItemLength_[index] = allocatedItemWidth;
829         }
830     }
831 }
832 
HandleSpaceBetweenOrCenterLayoutStyle(LayoutWrapper * layoutWrapper)833 void TabBarLayoutAlgorithm::HandleSpaceBetweenOrCenterLayoutStyle(LayoutWrapper* layoutWrapper)
834 {
835     if (GreatNotEqual(visibleChildrenMainSize_, contentMainSize_ / TWO)) {
836         useItemWidth_ = false;
837         return;
838     }
839     auto additionalWidth = (contentMainSize_ / TWO - visibleChildrenMainSize_) / childCount_;
840 
841     for (int32_t index = 0; index < childCount_; ++index) {
842         visibleItemLength_[index] += additionalWidth;
843     }
844 }
845 
ApplyLayoutMode(LayoutWrapper * layoutWrapper,float allocatedWidth)846 void TabBarLayoutAlgorithm::ApplyLayoutMode(LayoutWrapper* layoutWrapper, float allocatedWidth)
847 {
848     auto host = layoutWrapper->GetHostNode();
849     CHECK_NULL_VOID(host);
850     auto pipelineContext = host->GetContext();
851     CHECK_NULL_VOID(pipelineContext);
852     auto tabTheme = pipelineContext->GetTheme<TabTheme>();
853     CHECK_NULL_VOID(tabTheme);
854     auto tabBarPattern = host->GetPattern<TabBarPattern>();
855     CHECK_NULL_VOID(tabBarPattern);
856 
857     bool isVertical = LessOrEqual(allocatedWidth, tabTheme->GetHorizontalBottomTabMinWidth().ConvertToPx());
858 
859     // Calculate the initial buffer and initial space request of each item.
860     for (int32_t index = 0; index < childCount_; ++index) {
861         auto bottomTabBarStyle = tabBarPattern->GetBottomTabBarStyle(index);
862         if (tabBarPattern->GetTabBarStyle(index) != TabBarStyle::BOTTOMTABBATSTYLE ||
863             bottomTabBarStyle.layoutMode != LayoutMode::AUTO) {
864             continue;
865         }
866         auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
867         CHECK_NULL_VOID(childWrapper);
868         auto linearLayoutProperty = AceType::DynamicCast<LinearLayoutProperty>(childWrapper->GetLayoutProperty());
869         CHECK_NULL_VOID(linearLayoutProperty);
870         auto textWrapper = childWrapper->GetOrCreateChildByIndex(1);
871         CHECK_NULL_VOID(textWrapper);
872         auto textLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(textWrapper->GetLayoutProperty());
873         CHECK_NULL_VOID(textLayoutProperty);
874         if (isVertical) {
875             linearLayoutProperty->UpdateFlexDirection(FlexDirection::COLUMN);
876             linearLayoutProperty->UpdateSpace(tabTheme->GetBottomTabBarSpace());
877             linearLayoutProperty->UpdateMainAxisAlign(bottomTabBarStyle.verticalAlign);
878             linearLayoutProperty->UpdateCrossAxisAlign(FlexAlign::CENTER);
879             linearLayoutProperty->SetIsVertical(true);
880             textLayoutProperty->UpdateTextAlign(TextAlign::CENTER);
881         } else {
882             linearLayoutProperty->UpdateFlexDirection(FlexDirection::ROW);
883             linearLayoutProperty->UpdateSpace(tabTheme->GetHorizontalBottomTabBarSpace());
884             linearLayoutProperty->UpdateMainAxisAlign(FlexAlign::CENTER);
885             linearLayoutProperty->UpdateCrossAxisAlign(bottomTabBarStyle.verticalAlign);
886             linearLayoutProperty->SetIsVertical(false);
887             textLayoutProperty->UpdateTextAlign(TextAlign::LEFT);
888         }
889         auto childNode = childWrapper->GetHostNode();
890         CHECK_NULL_VOID(childNode);
891         if (!tabBarPattern->GetBottomTabLabelStyle(childNode->GetId()).fontSize.has_value() &&
892             Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
893             textLayoutProperty->UpdateFontSize(
894                 isVertical ? tabTheme->GetBottomTabTextSize() : tabTheme->GetBottomTabHorizontalTextSize());
895         }
896     }
897 }
898 
ApplySymmetricExtensible(LayoutWrapper * layoutWrapper,float allocatedWidth)899 void TabBarLayoutAlgorithm::ApplySymmetricExtensible(LayoutWrapper* layoutWrapper, float allocatedWidth)
900 {
901     auto host = layoutWrapper->GetHostNode();
902     CHECK_NULL_VOID(host);
903     auto tabBarPattern = host->GetPattern<TabBarPattern>();
904     CHECK_NULL_VOID(tabBarPattern);
905 
906     if (childCount_ <= TWO || childCount_ > static_cast<int32_t>(visibleItemLength_.size())) {
907         for (int32_t index = 0; index < static_cast<int32_t>(visibleItemLength_.size()); ++index) {
908             visibleItemLength_[index] = allocatedWidth;
909         }
910         return;
911     }
912 
913     std::vector<float> leftBuffers(childCount_);
914     std::vector<float> rightBuffers(childCount_);
915     std::vector<float> spaceRequests(childCount_);
916 
917     // Calculate the initial buffer and initial space request of each item.
918     for (int32_t index = 0; index < childCount_; ++index) {
919         auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
920         CHECK_NULL_VOID(childWrapper);
921         auto linearLayoutProperty = AceType::DynamicCast<LinearLayoutProperty>(childWrapper->GetLayoutProperty());
922         CHECK_NULL_VOID(linearLayoutProperty);
923         if (GreatNotEqual(visibleItemLength_[index], allocatedWidth)) {
924             if (tabBarPattern->GetTabBarStyle(index) == TabBarStyle::BOTTOMTABBATSTYLE &&
925                 tabBarPattern->GetBottomTabBarStyle(index).symmetricExtensible && index > 0 &&
926                 index < childCount_ - 1) {
927                 spaceRequests[index] = (visibleItemLength_[index] - allocatedWidth) / TWO;
928             }
929         } else {
930             if (tabBarPattern->GetTabBarStyle(index) == TabBarStyle::BOTTOMTABBATSTYLE) {
931                 leftBuffers[index] = index == 0 ? 0.0f : (allocatedWidth - visibleItemLength_[index]) / TWO;
932                 rightBuffers[index] =
933                     index == childCount_ - 1 ? 0.0f : (allocatedWidth - visibleItemLength_[index]) / TWO;
934             }
935         }
936     }
937 
938     // Decide the used buffer and used space request of each item.
939     for (int32_t index = 1; index < childCount_ - 1; ++index) {
940         auto actualRequest = std::min(std::min(rightBuffers[index - 1], leftBuffers[index + 1]), spaceRequests[index]);
941         spaceRequests[index] = actualRequest;
942         rightBuffers[index - 1] = actualRequest;
943         leftBuffers[index + 1] = actualRequest;
944     }
945 
946     spaceRequests[0] = 0.0f;
947     spaceRequests[childCount_ - 1] = 0.0f;
948 
949     leftBuffers[1] = 0.0f;
950     rightBuffers[childCount_ - TWO] = 0.0f;
951 
952     CalculateItemWidthsForSymmetricExtensible(layoutWrapper, spaceRequests, leftBuffers, rightBuffers, allocatedWidth);
953 }
954 
CalculateItemWidthsForSymmetricExtensible(LayoutWrapper * layoutWrapper,const std::vector<float> & spaceRequests,const std::vector<float> & leftBuffers,const std::vector<float> & rightBuffers,float allocatedWidth)955 void TabBarLayoutAlgorithm::CalculateItemWidthsForSymmetricExtensible(LayoutWrapper* layoutWrapper,
956     const std::vector<float>& spaceRequests, const std::vector<float>& leftBuffers,
957     const std::vector<float>& rightBuffers, float allocatedWidth)
958 {
959     auto host = layoutWrapper->GetHostNode();
960     CHECK_NULL_VOID(host);
961     auto tabBarPattern = host->GetPattern<TabBarPattern>();
962     CHECK_NULL_VOID(tabBarPattern);
963 
964     if ((static_cast<int32_t>(spaceRequests.size()) != childCount_) ||
965         (static_cast<int32_t>(leftBuffers.size()) != childCount_) ||
966         (static_cast<int32_t>(rightBuffers.size()) != childCount_) ||
967         (static_cast<int32_t>(visibleItemLength_.size()) != childCount_)) {
968         return;
969     }
970 
971     for (int32_t index = 0; index < childCount_; ++index) {
972         if (tabBarPattern->GetTabBarStyle(index) != TabBarStyle::BOTTOMTABBATSTYLE) {
973             visibleItemLength_[index] = allocatedWidth;
974             continue;
975         }
976         if (!NearZero(spaceRequests[index])) {
977             visibleItemLength_[index] = allocatedWidth + spaceRequests[index] * TWO;
978         } else if (!NearZero(leftBuffers[index]) || !NearZero(rightBuffers[index])) {
979             visibleItemLength_[index] = allocatedWidth - leftBuffers[index] - rightBuffers[index];
980             auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
981             CHECK_NULL_VOID(childWrapper);
982             // Adjust margin to keep the position of current item.
983             auto leftMargin = rightBuffers[index];
984             auto rightMargin = leftBuffers[index];
985             if (GreatNotEqual(leftMargin, rightMargin)) {
986                 leftMargin -= rightMargin;
987                 rightMargin = 0.0f;
988             } else {
989                 rightMargin -= leftMargin;
990                 leftMargin = 0.0f;
991             }
992             UpdateChildMarginProperty(rightMargin, leftMargin, childWrapper);
993         } else {
994             visibleItemLength_[index] = allocatedWidth;
995         }
996     }
997 }
998 
UpdateChildMarginProperty(float rightMargin,float leftMargin,const RefPtr<LayoutWrapper> & childWrapper)999 void TabBarLayoutAlgorithm::UpdateChildMarginProperty(
1000     float rightMargin, float leftMargin, const RefPtr<LayoutWrapper>& childWrapper)
1001 {
1002     auto linearLayoutProperty = AceType::DynamicCast<LinearLayoutProperty>(childWrapper->GetLayoutProperty());
1003     CHECK_NULL_VOID(linearLayoutProperty);
1004     auto textWrapper = childWrapper->GetOrCreateChildByIndex(1);
1005     CHECK_NULL_VOID(textWrapper);
1006     auto textLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(textWrapper->GetLayoutProperty());
1007     CHECK_NULL_VOID(textLayoutProperty);
1008     textLayoutProperty->UpdateMargin(
1009         { CalcLength(Dimension(leftMargin)), CalcLength(Dimension(rightMargin)), {}, {}, {}, {} });
1010     auto iconWrapper = childWrapper->GetOrCreateChildByIndex(0);
1011     CHECK_NULL_VOID(iconWrapper);
1012     if (iconWrapper->GetHostNode()->GetTag() == V2::SYMBOL_ETS_TAG) {
1013         auto symbolLayoutProperty = AceType::DynamicCast<TextLayoutProperty>(iconWrapper->GetLayoutProperty());
1014         CHECK_NULL_VOID(symbolLayoutProperty);
1015         symbolLayoutProperty->UpdateMargin(
1016             { CalcLength(Dimension(leftMargin)), CalcLength(Dimension(rightMargin)), {}, {}, {}, {} });
1017         if (linearLayoutProperty->GetFlexDirection().value_or(FlexDirection::COLUMN) == FlexDirection::ROW) {
1018             symbolLayoutProperty->UpdateMargin({ CalcLength(Dimension(leftMargin)), {}, {}, {}, {}, {} });
1019             textLayoutProperty->UpdateMargin({ {}, CalcLength(Dimension(rightMargin)), {}, {}, {}, {} });
1020         }
1021     } else {
1022         auto imageLayoutProperty = AceType::DynamicCast<ImageLayoutProperty>(iconWrapper->GetLayoutProperty());
1023         CHECK_NULL_VOID(imageLayoutProperty);
1024         imageLayoutProperty->UpdateMargin(
1025             { CalcLength(Dimension(leftMargin)), CalcLength(Dimension(rightMargin)), {}, {}, {}, {} });
1026         if (linearLayoutProperty->GetFlexDirection().value_or(FlexDirection::COLUMN) == FlexDirection::ROW) {
1027             imageLayoutProperty->UpdateMargin({ CalcLength(Dimension(leftMargin)), {}, {}, {}, {}, {} });
1028             textLayoutProperty->UpdateMargin({ {}, CalcLength(Dimension(rightMargin)), {}, {}, {}, {} });
1029         }
1030     }
1031 }
1032 
ApplyBarGridAlign(const RefPtr<TabBarLayoutProperty> & layoutProperty,const SizeF & contentSize) const1033 float TabBarLayoutAlgorithm::ApplyBarGridAlign(
1034     const RefPtr<TabBarLayoutProperty>& layoutProperty, const SizeF& contentSize) const
1035 {
1036     if (!layoutProperty->GetBarGridAlign()) {
1037         return 0.0f;
1038     }
1039     auto option = layoutProperty->GetBarGridAlign().value();
1040     auto gridSizeType = GetGridSizeType(contentSize);
1041     int32_t columnNum = -1;
1042     if (gridSizeType == GridSizeType::SM) {
1043         columnNum = option.sm;
1044         if (columnNum > SM_COLUMN_NUM) {
1045             return 0.0f;
1046         }
1047     } else if (gridSizeType == GridSizeType::MD) {
1048         columnNum = option.md;
1049         if (columnNum > MD_COLUMN_NUM) {
1050             return 0.0f;
1051         }
1052     } else if (gridSizeType == GridSizeType::LG) {
1053         columnNum = option.lg;
1054         if (columnNum > LG_COLUMN_NUM) {
1055             return 0.0f;
1056         }
1057     } else {
1058         return 0.0f;
1059     }
1060     if (columnNum < 0 || columnNum % 2) {
1061         return 0.0f;
1062     }
1063     auto gridWidth = GetGridWidth(option, contentSize, columnNum);
1064     return (contentSize.Width() - gridWidth) / TWO;
1065 }
1066 
Layout(LayoutWrapper * layoutWrapper)1067 void TabBarLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
1068 {
1069     CHECK_NULL_VOID(layoutWrapper);
1070     auto geometryNode = layoutWrapper->GetGeometryNode();
1071     CHECK_NULL_VOID(geometryNode);
1072     auto layoutProperty = AceType::DynamicCast<TabBarLayoutProperty>(layoutWrapper->GetLayoutProperty());
1073     CHECK_NULL_VOID(layoutProperty);
1074     axis_ = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
1075     if ((axis_ == Axis::VERTICAL && NearZero(geometryNode->GetFrameSize().Width())) ||
1076         (axis_ == Axis::HORIZONTAL && NearZero(geometryNode->GetFrameSize().Height()))) {
1077         return;
1078     }
1079     childCount_ = layoutWrapper->GetTotalChildCount() - MASK_COUNT;
1080     if (childCount_ <= 0) {
1081         return;
1082     }
1083     if (visibleItemPosition_.empty()) {
1084         return;
1085     }
1086 
1087     auto contentSize = geometryNode->GetPaddingSize();
1088     auto childOffset = OffsetF(0.0f, 0.0f);
1089     if (geometryNode->GetPadding()) {
1090         auto left = geometryNode->GetPadding()->left.value_or(0.0f);
1091         auto top = geometryNode->GetPadding()->top.value_or(0.0f);
1092         childOffset += OffsetF(left, top);
1093     }
1094     if (isRTL_ && axis_ == Axis::HORIZONTAL) {
1095         childOffset +=
1096             OffsetF(0.0f, contentSize.Width() - visibleItemPosition_.begin()->second.startPos - barGridMargin_, axis_);
1097     } else {
1098         childOffset += OffsetF(barGridMargin_, 0.0f);
1099         childOffset += OffsetF(0.0f, visibleItemPosition_.begin()->second.startPos, axis_);
1100     }
1101     LayoutChildren(layoutWrapper, contentSize, childOffset);
1102 }
1103 
LayoutChildren(LayoutWrapper * layoutWrapper,const SizeF & contentSize,OffsetF & childOffset)1104 void TabBarLayoutAlgorithm::LayoutChildren(LayoutWrapper* layoutWrapper, const SizeF& contentSize, OffsetF& childOffset)
1105 {
1106     std::map<int32_t, OffsetF> childOffsetDelta;
1107     for (auto& iter : visibleItemPosition_) {
1108         auto pos = iter.first;
1109         auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(pos);
1110         if (!childWrapper) {
1111             continue;
1112         }
1113         auto childGeometryNode = childWrapper->GetGeometryNode();
1114         auto childFrameSize = childGeometryNode->GetMarginFrameSize();
1115         if (isRTL_ && axis_ == Axis::HORIZONTAL) {
1116             childOffset -= OffsetF(0.0f, childFrameSize.MainSize(axis_), axis_);
1117         }
1118         OffsetF centerOffset =
1119             OffsetF((contentSize.CrossSize(axis_) - childFrameSize.CrossSize(axis_)) / 2.0, 0.0f, axis_);
1120         childOffsetDelta[pos] = childOffset + centerOffset - childGeometryNode->GetMarginFrameOffset();
1121         childGeometryNode->SetMarginFrameOffset(childOffset + centerOffset);
1122         childWrapper->Layout();
1123         if (!isRTL_ || axis_ != Axis::HORIZONTAL) {
1124             childOffset += OffsetF(0.0f, childFrameSize.MainSize(axis_), axis_);
1125         }
1126     }
1127     LayoutMask(layoutWrapper, childOffsetDelta);
1128 }
1129 
LayoutMask(LayoutWrapper * layoutWrapper,const std::map<int32_t,OffsetF> & childOffsetDelta)1130 void TabBarLayoutAlgorithm::LayoutMask(LayoutWrapper* layoutWrapper,
1131     const std::map<int32_t, OffsetF>& childOffsetDelta)
1132 {
1133     auto layoutProperty = AceType::DynamicCast<TabBarLayoutProperty>(layoutWrapper->GetLayoutProperty());
1134     CHECK_NULL_VOID(layoutProperty);
1135     auto selectedMaskWrapper = layoutWrapper->GetOrCreateChildByIndex(childCount_);
1136     CHECK_NULL_VOID(selectedMaskWrapper);
1137     auto unselectedMaskWrapper = layoutWrapper->GetOrCreateChildByIndex(childCount_ + 1);
1138     CHECK_NULL_VOID(unselectedMaskWrapper);
1139     for (int32_t i = 0; i < MASK_COUNT; i++) {
1140         auto currentWrapper = (i == 0 ? selectedMaskWrapper : unselectedMaskWrapper);
1141         auto currentMask = (i == 0 ? layoutProperty->GetSelectedMask().value_or(-1)
1142                                    : layoutProperty->GetUnselectedMask().value_or(-1));
1143         if (currentMask < 0) {
1144             currentWrapper->GetGeometryNode()->SetFrameSize(SizeF());
1145             currentWrapper->Layout();
1146             currentWrapper->SetActive(false);
1147         } else {
1148             auto offset = currentWrapper->GetGeometryNode()->GetMarginFrameOffset();
1149             auto iter = childOffsetDelta.find(currentMask);
1150             if (iter != childOffsetDelta.end()) {
1151                 offset += iter->second;
1152             }
1153             currentWrapper->GetGeometryNode()->SetMarginFrameOffset(offset);
1154             auto imageWrapper = currentWrapper->GetOrCreateChildByIndex(0);
1155             CHECK_NULL_VOID(imageWrapper);
1156             auto imageNode = imageWrapper->GetHostNode();
1157             CHECK_NULL_VOID(imageNode);
1158             auto imageRenderContext = imageNode->GetRenderContext();
1159             CHECK_NULL_VOID(imageRenderContext);
1160             imageRenderContext->SetVisible(true);
1161             currentWrapper->Layout();
1162             currentWrapper->SetActive(true);
1163         }
1164     }
1165 }
1166 
GetGridSizeType(const SizeF & frameSize) const1167 GridSizeType TabBarLayoutAlgorithm::GetGridSizeType(const SizeF& frameSize) const
1168 {
1169     auto gridColumnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::TAB_BAR);
1170     CHECK_NULL_RETURN(gridColumnInfo, GridSizeType::UNDEFINED);
1171     auto parent = gridColumnInfo->GetParent();
1172     CHECK_NULL_RETURN(parent, GridSizeType::UNDEFINED);
1173     parent->BuildColumnWidth(frameSize.Width());
1174     return parent->GetSizeType();
1175 }
1176 
GetGridWidth(const BarGridColumnOptions & option,const SizeF & frameSize,int32_t columns) const1177 float TabBarLayoutAlgorithm::GetGridWidth(
1178     const BarGridColumnOptions& option, const SizeF& frameSize, int32_t columns) const
1179 {
1180     auto gridColumnInfo = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::TAB_BAR);
1181     CHECK_NULL_RETURN(gridColumnInfo, 0.0f);
1182     auto parent = gridColumnInfo->GetParent();
1183     CHECK_NULL_RETURN(parent, 0.0f);
1184     parent->SetGutterWidth(option.gutter);
1185     parent->SetMarginLeft(option.margin);
1186     parent->SetMarginRight(option.margin);
1187     parent->BuildColumnWidth(frameSize.Width());
1188     if (columns < 0) {
1189         return gridColumnInfo->GetMaxWidth();
1190     }
1191     return gridColumnInfo->GetWidth(columns);
1192 }
1193 } // namespace OHOS::Ace::NG
1194