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