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