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