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