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