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