• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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/tab_bar/render_tab_bar.h"
17 
18 #include <algorithm>
19 
20 #include "base/log/event_report.h"
21 #include "base/utils/system_properties.h"
22 #include "core/common/container.h"
23 #include "core/components/common/properties/alignment.h"
24 
25 namespace OHOS::Ace {
26 namespace {
27 
28 constexpr int32_t GRADIENT_POINT_SIZE = 4;
29 constexpr Dimension FOCUS_ANIMATION_WIDTH = 2.0_vp;
30 constexpr Dimension OFFSET_FOR_FOCUS = 4.0_vp;
31 constexpr double DOUBLE_FACTOR = 2.0;
32 
33 } // namespace
34 
RenderTabBar()35 RenderTabBar::RenderTabBar() : RenderNode(true) {}
36 
Update(const RefPtr<Component> & component)37 void RenderTabBar::Update(const RefPtr<Component>& component)
38 {
39     RefPtr<TabBarComponent> tabBar = AceType::DynamicCast<TabBarComponent>(component);
40     if (!tabBar) {
41         LOGE("TabBarComponent is nullptr");
42         EventReport::SendRenderException(RenderExcepType::RENDER_COMPONENT_ERR);
43         return;
44     }
45     tabsSize_ = static_cast<int32_t>(tabBar->GetChildren().size());
46     auto tabController = tabBar->GetController();
47     FlushIndex(tabController);
48     if (initialUpdate_) {
49         auto barIndicator = tabBar->GetIndicator();
50         if (barIndicator) {
51             indicator_ = barIndicator->CreateRenderNode();
52             if (indicator_) {
53                 AddChild(indicator_);
54                 indicator_->Attach(GetContext());
55                 indicator_->Update(barIndicator);
56                 indicatorPadding_ = barIndicator->GetPadding();
57                 indicatorStyle_ = GetIndicatorStyle(barIndicator);
58             }
59         }
60         initialUpdate_ = false;
61     }
62     InitScrollableOffset(tabBar->GetMode());
63     mode_ = tabBar->GetMode();
64     isVertical_ = tabBar->IsVertical();
65     indicatorSize_ = tabBar->GetIndicatorSize();
66     padding_ = tabBar->GetPadding();
67     activeIndicatorMinWidth_ = tabBar->GetActiveIndicatorMinWidth();
68     focusAnimationColor_ = tabBar->GetFocusAnimationColor();
69     focusRadiusDimension_ = tabBar->GetFocusRadiusDimension();
70     gradientWidth_ = tabBar->GetGradientWidth();
71     barPosition_ = tabBar->GetBarPosition();
72     SetTextDirection(tabBar->GetTextDirection());
73     Initialize();
74     MarkNeedLayout();
75 }
76 
FlushIndex(const RefPtr<TabController> & controller)77 void RenderTabBar::FlushIndex(const RefPtr<TabController>& controller)
78 {
79     if (!controller) {
80         return;
81     }
82     int32_t index = 0;
83     if (controller->IsIndexDefined()) {
84         index = controller->GetIndex();
85     } else {
86         auto initialIndex = controller->GetInitialIndex();
87         auto pendingIndex = controller->GetPendingIndex();
88         if (initialUpdate_ && pendingIndex < 0) {
89             index = initialIndex < 0 ? 0 : initialIndex;
90         } else {
91             index = pendingIndex < 0 ? 0 : pendingIndex;
92         }
93     }
94     if (!Container::IsCurrentUsePartialUpdate()) {
95         index_ = index < tabsSize_ ? index : tabsSize_ - 1;
96     } else {
97         // In partial update we have zero tabs yet here, so just keep the index
98         index_ = (index < tabsSize_ || tabsSize_ == 0) ? index : tabsSize_ - 1;
99     }
100 
101     ApplyRestoreInfo(controller);
102 
103     LOGD("focus index_ %{public}d tabsSize_ %{public}d, index %{public}d, initial %{public}d, pending %{public}d",
104         index_, tabsSize_, index, controller->GetInitialIndex(), controller->GetIndex());
105     controller->SetIndexWithoutChangeContent(index_);
106 }
107 
PerformLayout()108 void RenderTabBar::PerformLayout()
109 {
110     tabsSize_ = 0;
111     const std::list<RefPtr<RenderNode>>& children = GetChildren();
112     if (indicator_) {
113         // At least one child(include indicator)
114         if (children.size() <= 1) {
115             return;
116         }
117         tabsSize_ = static_cast<int32_t>(children.size()) - 1;
118     } else {
119         if (children.empty()) {
120             return;
121         }
122         tabsSize_ = static_cast<int32_t>(children.size());
123     }
124 
125     index_ = std::clamp(index_, 0, std::max(0, tabsSize_ - 1));
126     if (!IsRightToLeft()) {
127         if (tabBarWidth_ > 0 && !NearEqual(tabBarWidth_, GetLayoutParam().GetMaxSize().Width())) {
128             if (!isVertical_ && actualWidth_ > GetLayoutSize().Width() &&
129                 GreatNotEqual(
130                     GetLayoutParam().GetMaxSize().Width(), actualWidth_ - std::abs(scrollableOffset_.GetX()))) {
131                 scrollableOffset_.SetX(scrollableOffset_.GetX() + GetLayoutParam().GetMaxSize().Width() - tabBarWidth_);
132             }
133         }
134     }
135     tabBarWidth_ = GetLayoutParam().GetMaxSize().Width();
136     // Layout children and indicator
137     LayoutChildren();
138     UpdatePosition();
139     Size layoutSize = GetLayoutParam().Constrain(GetLayoutParam().GetMaxSize());
140     LOGD("RenderTabBar layoutSize: (%{public}s)", layoutSize.ToString().c_str());
141     SetLayoutSize(layoutSize);
142     ApplyGradientColor();
143     if (isFirstLayout_) {
144         if (index_ != 0) {
145             SetIndex(index_, true);
146         }
147         isFirstLayout_ = false;
148     }
149 }
150 
PerformLayoutChildren(const LayoutParam & innerLayoutParam)151 void RenderTabBar::PerformLayoutChildren(const LayoutParam& innerLayoutParam)
152 {
153     actualWidth_ = NormalizeToPx(padding_.Left());
154     actualHeight_ = NormalizeToPx(padding_.Top());
155 
156     tabsWidth_.clear();
157     tabsHeight_.clear();
158     tabItemOffsets_.clear();
159     if (!isVertical_ && scrollableOffset_.GetX() > tabBarWidth_) {
160         scrollableOffset_.Reset();
161     }
162     auto context = context_.Upgrade();
163     if (!context) {
164         LOGE("context is null");
165         return;
166     }
167 
168     if (isVertical_) {
169         tabItemOffsets_.emplace_back(0.0, 0.0);
170     } else {
171         if (!IsRightToLeft()) {
172             tabItemOffsets_.emplace_back(padding_.GetOffsetInPx(context->GetDipScale()));
173         }
174     }
175 
176     auto children = GetChildren();
177     auto item = children.begin();
178     // skip indicator
179     if (indicator_) {
180         ++item;
181     }
182     // First time layout all children
183     for (; item != children.end(); ++item) {
184         (*item)->Layout(innerLayoutParam);
185         if (isVertical_) {
186             tabsHeight_.push_back((*item)->GetLayoutSize().Height());
187             actualHeight_ += tabsHeight_.back();
188             tabItemOffsets_.emplace_back(0.0, actualHeight_);
189         } else {
190             tabsWidth_.push_back((*item)->GetLayoutSize().Width());
191             actualWidth_ += tabsWidth_.back();
192             if (IsRightToLeft()) {
193                 tabItemOffsets_.emplace_back(tabBarWidth_ - actualWidth_, 0.0);
194             } else {
195                 tabItemOffsets_.emplace_back(actualWidth_, 0.0);
196             }
197         }
198     }
199 
200     actualWidth_ += NormalizeToPx(padding_.Right());
201     actualHeight_ += NormalizeToPx(padding_.Bottom());
202 }
203 
LayoutChildren()204 void RenderTabBar::LayoutChildren()
205 {
206     // First time layout all children and update relative position.
207     LayoutParam innerLayoutParam = MakeInnerLayoutParam();
208 
209     // First time layout all children
210     PerformLayoutChildren(innerLayoutParam);
211 
212     if (mode_ == TabBarMode::FIXED_START && !isVertical_ && needUpdateOffset_) {
213         double padding;
214         if (index_ == 0) {
215             padding = NormalizeToPx(Dimension(16, DimensionUnit::VP));
216         } else {
217             padding = NormalizeToPx(Dimension(24, DimensionUnit::VP));
218         }
219         if (!IsRightToLeft()) {
220             scrollableOffset_.SetX(std::clamp(padding - tabItemOffsets_[index_].GetX(), -MaxScrollableWidth(), 0.0));
221         } else {
222             scrollableOffset_.SetX(std::clamp(padding - tabItemOffsets_[index_].GetX(), 0.0, MaxScrollableWidth()));
223         }
224     } else if (mode_ == TabBarMode::SCROLLABLE && actualWidth_ < GetLayoutParam().GetMaxSize().Width() &&
225                !isVertical_) {
226         // In scrollable mod: the sum of Tab's width can less then TabBar width
227         double halfWidth = GetLayoutParam().GetMaxSize().Width() / DOUBLE_FACTOR;
228         if (actualWidth_ < halfWidth) {
229             // when items total width less than half of tab bar, make items average harf width
230             double averageWidth = halfWidth / tabsSize_;
231             innerLayoutParam.SetMinSize(Size(averageWidth, innerLayoutParam.GetMinSize().Height()));
232             // relayout all children
233             PerformLayoutChildren(innerLayoutParam);
234         }
235         if (IsRightToLeft()) {
236             scrollableOffset_ = Offset((actualWidth_ - GetLayoutParam().GetMaxSize().Width()) / DOUBLE_FACTOR, 0.0);
237         } else {
238             scrollableOffset_ = Offset((GetLayoutParam().GetMaxSize().Width() - actualWidth_) / DOUBLE_FACTOR, 0.0);
239         }
240     } else if (mode_ == TabBarMode::SCROLLABLE && actualHeight_ < GetLayoutParam().GetMaxSize().Height() &&
241                isVertical_) {
242         // In scrollable mod: the sum of Tab's width can less then TabBar width
243         scrollableOffset_ = Offset(0.0, (GetLayoutParam().GetMaxSize().Height() - actualHeight_) / DOUBLE_FACTOR);
244     }
245 }
246 
UpdatePosition()247 void RenderTabBar::UpdatePosition()
248 {
249     const std::list<RefPtr<RenderNode>>& children = GetChildren();
250 
251     // At least one child
252     if (children.size() <= 1) {
253         return;
254     }
255     auto item = children.begin();
256     // skip indicator
257     if (indicator_) {
258         ++item;
259     }
260     // Second update relative position.
261     for (int32_t i = 0; item != children.end(); ++item) {
262         (*item)->SetPosition(scrollableOffset_ + tabItemOffsets_[i]);
263         if (i == index_ && indicator_) {
264             // update indicator layout and position
265             indicator_->Layout(MakeIndicatorLayoutParam(*item));
266             Offset offset = MakeIndicatorOffset(*item);
267             Offset indiOffset = Offset(indicatorPlusX_, 0.0);
268             Offset posOffset = scrollableOffset_ + tabItemOffsets_[index_] + offset - indiOffset;
269             indicator_->SetPosition(posOffset);
270         }
271         i++;
272     }
273 }
274 
SetScrollIndicator(double percent,int32_t newIndex,bool needChange)275 void RenderTabBar::SetScrollIndicator(double percent, int32_t newIndex, bool needChange)
276 {
277     indicatorPlusX_ = 0.0;
278     indicatorPlusWidth_ = 0.0;
279     if (newIndex < 0 || newIndex >= tabsSize_) {
280         LOGW("boundary index = %{public}d", newIndex);
281         return;
282     }
283 
284     if (isVertical_) {
285         return;
286     }
287     if (tabsWidth_.empty()) {
288         return;
289     }
290     auto maxIndex = static_cast<int32_t>(tabsWidth_.size()) - 1;
291     if (newIndex > maxIndex || newIndex < 0 || index_ > maxIndex || index_ < 0) {
292         return;
293     }
294     double newItemsWidth = tabsWidth_[newIndex];
295     double curItemsWidth = tabsWidth_[index_];
296     indicatorPlusX_ = curItemsWidth * percent;
297     if (needChange) {
298         if (percent<0) {
299             indicatorPlusX_ = curItemsWidth * (1.0 - std::abs(percent));
300         } else {
301             indicatorPlusX_ = curItemsWidth * (1.0 - std::abs(percent)) * -1.0;
302         }
303     }
304     indicatorPlusWidth_ = (curItemsWidth - newItemsWidth) * std::abs(percent);
305     MarkNeedLayout();
306 }
307 
SetIndex(int32_t index,bool force)308 void RenderTabBar::SetIndex(int32_t index, bool force)
309 {
310     indicatorPlusX_ = 0.0;
311     indicatorPlusWidth_ = 0.0;
312 
313     if (Container::IsCurrentUsePartialUpdate()) {
314         tabsSize_ = static_cast<int32_t>(indicator_? GetChildren().size() - 1 : GetChildren().size());
315     }
316 
317     if (index < 0 || index >= tabsSize_) {
318         LOGW("illegal index = %{public}d", index);
319         return;
320     }
321 
322     if (index_ != index || force) {
323         if (mode_ == TabBarMode::FIXED_START && tabBarSizeAnimation_) {
324             needUpdateOffset_ = true;
325             auto tabBar = AceType::WeakClaim(this);
326             tabBarSizeAnimation_->Start(tabBar, index_, index);
327         }
328         index_ = index;
329         if (mode_ == TabBarMode::SCROLLABLE) {
330             if (actualWidth_ > GetLayoutParam().GetMaxSize().Width() && !isVertical_) {
331                 // In scrollable mod: the select tab must in middle of tabBar
332                 Offset centerViewPort(GetLayoutParam().GetMaxSize().Width() / DOUBLE_FACTOR, 0.0);
333                 Offset centerTabItem = tabItemOffsets_[index_] + Offset(tabsWidth_[index_] / DOUBLE_FACTOR, 0.0);
334                 scrollableOffset_ = centerViewPort - centerTabItem;
335                 if (!IsRightToLeft()) {
336                     scrollableOffset_.SetX(std::clamp(scrollableOffset_.GetX(), -MaxScrollableWidth(), 0.0));
337                 } else {
338                     scrollableOffset_.SetX(std::clamp(scrollableOffset_.GetX(), 0.0, MaxScrollableWidth()));
339                 }
340             }
341             if (actualHeight_ > GetLayoutParam().GetMaxSize().Height() && isVertical_) {
342                 // In scrollable mod: the select tab must in middle of tabBar
343                 Offset centerViewPort(0.0, GetLayoutParam().GetMaxSize().Height() / DOUBLE_FACTOR);
344                 Offset centerTabItem = tabItemOffsets_[index_] + Offset(0.0, tabsHeight_[index_] / DOUBLE_FACTOR);
345                 scrollableOffset_ = centerViewPort - centerTabItem;
346                 scrollableOffset_.SetY(std::clamp(scrollableOffset_.GetY(), -MaxScrollableHeight(), 0.0));
347             }
348         }
349     }
350 }
351 
Initialize()352 void RenderTabBar::Initialize()
353 {
354     if (!clickRecognizer_) {
355         clickRecognizer_ = AceType::MakeRefPtr<ClickRecognizer>();
356         clickRecognizer_->SetOnClick([weakBar = AceType::WeakClaim(this)](const ClickInfo& info) {
357             auto tabBar = weakBar.Upgrade();
358             if (tabBar) {
359                 tabBar->HandleClickedEvent(info);
360             }
361         });
362     }
363     if (!scrollable_) {
364         scrollable_ = AceType::MakeRefPtr<Scrollable>(
365             [weak = AceType::WeakClaim(this)](double offset, int32_t source) {
366                 auto tabBar = weak.Upgrade();
367                 if (tabBar && (source != SCROLL_FROM_START)) {
368                     return tabBar->HandleScrollablePosition(offset);
369                 } else {
370                     return false;
371                 }
372             },
373             isVertical_ ? Axis::VERTICAL : Axis::HORIZONTAL);
374         scrollable_->Initialize(GetContext());
375         scrollable_->SetNodeId(GetAccessibilityNodeId());
376         scrollable_->SetScrollableNode(AceType::WeakClaim(this));
377     }
378 
379     if (!tabBarSizeAnimation_ && !indicator_) {
380         tabBarSizeAnimation_ = AceType::MakeRefPtr<TabBarSizeAnimation>();
381         tabBarSizeAnimation_->Initialize(GetContext());
382     }
383     InitAccessibilityEventListener();
384 }
385 
InitAccessibilityEventListener()386 void RenderTabBar::InitAccessibilityEventListener()
387 {
388     auto refNode = accessibilityNode_.Upgrade();
389     if (!refNode) {
390         return;
391     }
392 
393     refNode->AddSupportAction(AceAction::ACTION_SCROLL_FORWARD);
394     refNode->AddSupportAction(AceAction::ACTION_SCROLL_BACKWARD);
395     refNode->AddSupportAction(AceAction::ACTION_CLICK);
396 
397     auto weakPtr = AceType::WeakClaim(this);
398     refNode->SetActionClickImpl([weakPtr]() {
399         auto tabBar = weakPtr.Upgrade();
400         if (tabBar) {
401             tabBar->AccessibilityClick();
402             return true;
403         }
404         return false;
405     });
406 
407     refNode->SetActionScrollForward([weakPtr]() {
408         auto tabBar = weakPtr.Upgrade();
409         if (tabBar) {
410             tabBar->AccessibilityScroll(true);
411             return true;
412         }
413         return false;
414     });
415 
416     refNode->SetActionScrollBackward([weakPtr]() {
417         auto tabBar = weakPtr.Upgrade();
418         if (tabBar) {
419             tabBar->AccessibilityScroll(false);
420             return true;
421         }
422         return false;
423     });
424 }
425 
AccessibilityScroll(bool isAdd)426 void RenderTabBar::AccessibilityScroll(bool isAdd)
427 {
428     if (tabItemOffsets_.empty()) {
429         return;
430     }
431     if (isAdd) {
432         accessibilityIndex_++;
433     } else {
434         accessibilityIndex_--;
435     }
436 }
437 
AccessibilityClick()438 void RenderTabBar::AccessibilityClick()
439 {
440     if (callback_) {
441         callback_(accessibilityIndex_);
442     }
443 }
444 
HandleClickedEvent(const ClickInfo & info)445 void RenderTabBar::HandleClickedEvent(const ClickInfo& info)
446 {
447     LOGI("Click event x is %{public}lf", info.GetLocalLocation().GetX());
448     if (tabItemOffsets_.empty()) {
449         LOGW("tabItemOffsets is empty");
450         return;
451     }
452     Offset local = info.GetLocalLocation() - scrollableOffset_;
453     if (isVertical_) {
454         auto clickRange = std::make_pair(tabItemOffsets_[0].GetY(), tabItemOffsets_[tabItemOffsets_.size() - 1].GetY());
455         if (local.GetY() < clickRange.first || local.GetY() > clickRange.second) {
456             LOGW("clicked (%{public}lf) position out of range [%{public}lf, %{public}lf]", local.GetY(),
457                 clickRange.first, clickRange.second);
458             return;
459         }
460     } else {
461         auto clickRange = std::make_pair(tabItemOffsets_[0].GetX(), tabItemOffsets_[tabItemOffsets_.size() - 1].GetX());
462         if (!IsRightToLeft()) {
463             if (local.GetX() < clickRange.first || local.GetX() > clickRange.second) {
464                 LOGW("clicked (%{public}lf) position out of range [%{public}lf, %{public}lf]", local.GetX(),
465                     clickRange.first, clickRange.second);
466                 return;
467             }
468         } else {
469             if (local.GetX() > tabBarWidth_ || local.GetX() < clickRange.second) {
470                 LOGW("clicked (%{public}lf) position out of range [%{public}lf, %{public}lf]", local.GetX(),
471                     clickRange.first, clickRange.second);
472                 return;
473             }
474         }
475     }
476     auto pos = std::lower_bound(tabItemOffsets_.begin(), tabItemOffsets_.end(), local,
477         [weakBar = AceType::WeakClaim(this)](const Offset& a, const Offset& b) {
478             auto tabBar = weakBar.Upgrade();
479             if (tabBar) {
480                 return tabBar->IsRightToLeft() ? a.GetX() > b.GetX()
481                                                : (tabBar->isVertical_ ? a.GetY() < b.GetY() : a.GetX() < b.GetX());
482             } else {
483                 return false;
484             }
485         });
486 
487     if (pos != tabItemOffsets_.end()) {
488         int32_t index = IsRightToLeft() ? std::distance(tabItemOffsets_.begin(), pos)
489                                         : std::distance(tabItemOffsets_.begin(), pos) - 1;
490         if (index >= 0 && index < tabsSize_ && index != index_) {
491             LOGD("selected tab bar index :%{public}d, current index :%{public}d", index, index_);
492             if (callback_) {
493                 callback_(index);
494             }
495         }
496     }
497 }
498 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)499 void RenderTabBar::OnTouchTestHit(
500     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
501 {
502     if (!clickRecognizer_ || !scrollable_) {
503         return;
504     }
505     clickRecognizer_->SetCoordinateOffset(coordinateOffset);
506     result.emplace_back(clickRecognizer_);
507     scrollable_->SetCoordinateOffset(coordinateOffset);
508     result.emplace_back(scrollable_);
509 }
510 
HandleScrollablePosition(double value)511 bool RenderTabBar::HandleScrollablePosition(double value)
512 {
513     if (IsScrollable()) {
514         if (!isVertical_ && actualWidth_ > GetLayoutSize().Width()) {
515             Offset delta(value, 0.0);
516             scrollableOffset_ += delta;
517             if (!IsRightToLeft()) {
518                 scrollableOffset_.SetX(std::clamp(scrollableOffset_.GetX(), -MaxScrollableWidth(), 0.0));
519             } else {
520                 scrollableOffset_.SetX(std::clamp(scrollableOffset_.GetX(), 0.0, MaxScrollableWidth()));
521             }
522             needUpdateOffset_ = false;
523         }
524         if (isVertical_ && actualHeight_ > GetLayoutSize().Height()) {
525             Offset delta(0.0, value);
526             scrollableOffset_ += delta;
527             scrollableOffset_.SetY(std::clamp(scrollableOffset_.GetY(), -MaxScrollableHeight(), 0.0));
528         }
529 
530         if (!NearZero(value)) {
531             MarkNeedLayout();
532             return true;
533         }
534     }
535     return false;
536 }
537 
MakeInnerLayoutParam() const538 LayoutParam RenderTabBar::MakeInnerLayoutParam() const
539 {
540     LayoutParam innerLayout = GetLayoutParam();
541     if (mode_ == TabBarMode::FIXED) {
542         double paddingHorizontal = NormalizeToPx(padding_.Left()) + NormalizeToPx(padding_.Right());
543         double paddingVertical = NormalizeToPx(padding_.Top()) + NormalizeToPx(padding_.Bottom());
544         double tabMinWidth = std::max(0.0, innerLayout.GetMinSize().Width() - paddingHorizontal);
545         double tabMaxWidth = std::max(0.0, innerLayout.GetMaxSize().Width() - paddingHorizontal);
546         double tabMinHeight = std::max(0.0, innerLayout.GetMinSize().Height() - paddingVertical);
547         double tabMaxHeight = std::max(0.0, innerLayout.GetMaxSize().Height() - paddingVertical);
548         if (isVertical_) {
549             if (tabsSize_ > 1) {
550                 tabMinHeight = tabMinHeight / tabsSize_;
551                 tabMaxHeight = tabMaxHeight / tabsSize_;
552             }
553             innerLayout.SetMinSize(Size(std::max(innerLayout.GetMinSize().Width(), 0.0), std::max(tabMinHeight, 0.0)));
554             innerLayout.SetMaxSize(
555                 Size(std::max(GetLayoutParam().GetMaxSize().Width(), 0.0), std::max(tabMaxHeight, 0.0)));
556         } else {
557             if (tabsSize_ > 1) {
558                 tabMinWidth = tabMinWidth / tabsSize_;
559                 tabMaxWidth = tabMaxWidth / tabsSize_;
560             }
561             innerLayout.SetMinSize(Size(std::max(tabMinWidth, 0.0), std::max(innerLayout.GetMinSize().Height(), 0.0)));
562             innerLayout.SetMaxSize(
563                 Size(std::max(tabMaxWidth, 0.0), std::max(GetLayoutParam().GetMaxSize().Height(), 0.0)));
564         }
565     } else {
566         if (isVertical_) {
567             innerLayout.SetMinSize(
568                 Size(std::max(innerLayout.GetMinSize().Width(), 0.0), innerLayout.GetMinSize().Height()));
569             innerLayout.SetMaxSize(Size(std::max(innerLayout.GetMaxSize().Width(), 0.0), Size::INFINITE_SIZE));
570         } else {
571             innerLayout.SetMinSize(
572                 Size(innerLayout.GetMinSize().Width(), std::max(innerLayout.GetMinSize().Height(), 0.0)));
573             innerLayout.SetMaxSize(Size(Size::INFINITE_SIZE, std::max(innerLayout.GetMaxSize().Height(), 0.0)));
574         }
575     }
576     return innerLayout;
577 }
578 
MakeIndicatorLayoutParam(const RefPtr<RenderNode> & item) const579 LayoutParam RenderTabBar::MakeIndicatorLayoutParam(const RefPtr<RenderNode>& item) const
580 {
581     LayoutParam innerLayout;
582     auto context = context_.Upgrade();
583     if (!context) {
584         LOGE("context is null");
585         return innerLayout;
586     }
587     innerLayout.SetMinSize(Size(NormalizeToPx(activeIndicatorMinWidth_), 0.0));
588     Size maxLayoutParam = GetLayoutParam().GetMaxSize();
589     if (isVertical_) {
590         innerLayout.SetMaxSize(Size(
591             std::max(0.0, std::max(0.0, maxLayoutParam.Width()) - NormalizeToPx(FOCUS_ANIMATION_WIDTH) * DOUBLE_FACTOR),
592             std::max(0.0, maxLayoutParam.Height()) - NormalizeToPx(FOCUS_ANIMATION_WIDTH) * DOUBLE_FACTOR));
593     } else {
594         innerLayout.SetMaxSize(
595             Size(std::max(
596                 0.0, tabsWidth_[index_] - indicatorPlusWidth_ - NormalizeToPx(FOCUS_ANIMATION_WIDTH) * DOUBLE_FACTOR),
597                 std::max(0.0, maxLayoutParam.Height()) - NormalizeToPx(FOCUS_ANIMATION_WIDTH) * DOUBLE_FACTOR));
598     }
599 
600     if (indicatorSize_ == TabBarIndicatorType::LABEL) {
601         auto childSize = GetTabItemChildLayoutSize(item);
602         childSize += indicatorPadding_.GetLayoutSizeInPx(context->GetDipScale());
603 
604         innerLayout.SetMaxSize(
605             Size(std::min(
606                 innerLayout.GetMaxSize().Width() - indicatorPlusWidth_, childSize.Width() - indicatorPlusWidth_),
607                 std::min(innerLayout.GetMaxSize().Height(), childSize.Height())));
608     }
609     return innerLayout;
610 }
611 
MakeIndicatorOffset(const RefPtr<RenderNode> & item) const612 Offset RenderTabBar::MakeIndicatorOffset(const RefPtr<RenderNode>& item) const
613 {
614     auto context = context_.Upgrade();
615     if (!context) {
616         LOGE("context is null");
617         return Offset(0.0, 0.0);
618     }
619     Offset offset;
620     if (isVertical_) {
621         offset = Alignment::GetAlignPosition(Size(GetLayoutParam().GetMaxSize().Width(), tabsHeight_[index_]),
622             indicator_->GetLayoutSize(), Alignment::CENTER);
623     } else {
624         offset = Alignment::GetAlignPosition(Size(tabsWidth_[index_], GetLayoutParam().GetMaxSize().Height()),
625             indicator_->GetLayoutSize(), Alignment::CENTER);
626     }
627     if (indicatorStyle_ == TabBarIndicatorStyle::DEFAULT &&
628         (!onFocused_ || SystemProperties::GetDeviceType() == DeviceType::PHONE ||
629         SystemProperties::GetDeviceType() == DeviceType::TABLET ||
630         SystemProperties::GetDeviceType() == DeviceType::TWO_IN_ONE)) {
631         Size childSize = GetTabItemChildLayoutSize(item);
632         offset +=
633             Offset(0.0, childSize.Height() / DOUBLE_FACTOR) + indicatorPadding_.GetOffsetInPx(context->GetDipScale());
634     }
635     return offset;
636 }
637 
MaxScrollableWidth() const638 double RenderTabBar::MaxScrollableWidth() const
639 {
640     if (IsScrollable() && actualWidth_ > GetLayoutParam().GetMaxSize().Width() && !isVertical_) {
641         return actualWidth_ - GetLayoutParam().GetMaxSize().Width();
642     }
643     return 0.0;
644 }
645 
MaxScrollableHeight() const646 double RenderTabBar::MaxScrollableHeight() const
647 {
648     if (IsScrollable() && actualHeight_ > GetLayoutParam().GetMaxSize().Height() && isVertical_) {
649         return actualHeight_ - GetLayoutParam().GetMaxSize().Height();
650     }
651     return 0.0;
652 }
653 
GetTabItemChildLayoutSize(const RefPtr<RenderNode> & item) const654 Size RenderTabBar::GetTabItemChildLayoutSize(const RefPtr<RenderNode>& item) const
655 {
656     auto children = item->GetChildren();
657     if (children.empty()) {
658         return Size(0.0, 0.0);
659     }
660     return children.front()->GetLayoutSize();
661 }
662 
UpdateIndicatorStyle(const RefPtr<Component> & component)663 void RenderTabBar::UpdateIndicatorStyle(const RefPtr<Component>& component)
664 {
665     if (indicator_) {
666         indicator_->Update(component);
667         RefPtr<BoxComponent> boxComponent = AceType::DynamicCast<BoxComponent>(component);
668         if (boxComponent) {
669             indicatorStyle_ = GetIndicatorStyle(boxComponent);
670             indicatorPadding_ = boxComponent->GetPadding();
671         }
672     }
673 }
674 
HandleFocusEvent(bool focus)675 void RenderTabBar::HandleFocusEvent(bool focus)
676 {
677     LOGD("RenderTabBar HandleFocusEvent, focus is %{public}d", focus);
678     onFocused_ = focus;
679     if (!onFocused_) {
680         auto context = context_.Upgrade();
681         if (context) {
682             context->CancelFocusAnimation();
683         }
684     }
685 }
686 
OnPaintFinish()687 void RenderTabBar::OnPaintFinish()
688 {
689     auto context = context_.Upgrade();
690     if (!context || !onFocused_) {
691         return;
692     }
693 
694     auto deviceType = SystemProperties::GetDeviceType();
695     if (deviceType == DeviceType::TV) {
696         if (indicator_) {
697             context->ShowFocusAnimation(RRect::MakeRRect(Rect(Offset(0, 0), indicator_->GetLayoutSize()),
698                                             Radius(NormalizeToPx(focusRadiusDimension_))),
699                 focusAnimationColor_, indicator_->GetGlobalOffset());
700         }
701     } else {
702         int32_t focusedChildIndex = indicator_ ? index_ + 1 : index_;
703         auto focusedItem = GetChildByIndex(focusedChildIndex);
704         if (!focusedItem) {
705             return;
706         }
707 
708         auto layoutSize = focusedItem->GetLayoutSize();
709         auto position = focusedItem->GetGlobalOffset();
710 
711         double offsetForFocus = NormalizeToPx(OFFSET_FOR_FOCUS);
712         Offset offset = Offset(offsetForFocus, offsetForFocus);
713         layoutSize -= Size(offsetForFocus * 2.0, offsetForFocus * 2.0);
714         context->ShowFocusAnimation(
715             RRect::MakeRRect(Rect(offset, layoutSize), Radius(NormalizeToPx(focusRadiusDimension_))),
716             focusAnimationColor_, position + offset);
717     }
718 }
719 
ApplyGradientColor()720 void RenderTabBar::ApplyGradientColor()
721 {
722     auto parent = GetParent().Upgrade();
723     RefPtr<RenderBox> box = AceType::DynamicCast<RenderBox>(parent);
724     if (box) {
725         Color colorA = box->GetColor();
726         Color colorB = colorA.ChangeAlpha(0);
727 
728         double viewWidth = GetLayoutSize().Width();
729         double gradientWidthPx = NormalizeToPx(gradientWidth_);
730 
731         // only box's width is big enough will add gradient
732         if (viewWidth > DOUBLE_FACTOR * gradientWidthPx && !isVertical_) {
733             auto frontDecoration = box->GetFrontDecoration();
734             if (!frontDecoration) {
735                 frontDecoration = AceType::MakeRefPtr<Decoration>();
736             }
737             auto backDecoration = box->GetBackDecoration();
738             if (backDecoration) {
739                 frontDecoration->SetBorder(backDecoration->GetBorder());
740             }
741             Gradient gradient = Gradient();
742             gradient.SetDirection(GradientDirection::RIGHT);
743             std::vector<GradientColor> gradientColors = std::vector<GradientColor>(GRADIENT_POINT_SIZE);
744 
745             gradientColors[0].SetColor(colorA);
746             gradientColors[0].SetDimension(Dimension(0.0, DimensionUnit::PX));
747 
748             gradientColors[1].SetColor(colorB);
749             gradientColors[1].SetDimension(Dimension(gradientWidthPx, DimensionUnit::PX));
750 
751             gradientColors[2].SetColor(colorB);
752             gradientColors[2].SetDimension(Dimension(viewWidth - gradientWidthPx, DimensionUnit::PX));
753 
754             gradientColors[3].SetColor(colorA);
755             gradientColors[3].SetDimension(Dimension(viewWidth, DimensionUnit::PX));
756 
757             for (const auto& gradientColor : gradientColors) {
758                 gradient.AddColor(gradientColor);
759             }
760 
761             frontDecoration->SetGradient(gradient);
762 
763             box->SetFrontDecoration(frontDecoration);
764         }
765     }
766 }
767 
GetIndicatorStyle(const RefPtr<BoxComponent> & component) const768 TabBarIndicatorStyle RenderTabBar::GetIndicatorStyle(const RefPtr<BoxComponent>& component) const
769 {
770     RefPtr<TabBarIndicatorComponent> indicatorComponent = AceType::DynamicCast<TabBarIndicatorComponent>(component);
771     if (indicatorComponent) {
772         return indicatorComponent->GetIndicatorStyle();
773     }
774     return TabBarIndicatorStyle::CUSTOM;
775 }
776 
InitScrollableOffset(TabBarMode mode)777 void RenderTabBar::InitScrollableOffset(TabBarMode mode)
778 {
779     if (mode != mode_) {
780         scrollableOffset_.Reset();
781     }
782 }
783 
GetChildByIndex(int32_t index) const784 RefPtr<RenderNode> RenderTabBar::GetChildByIndex(int32_t index) const
785 {
786     int32_t size = static_cast<int32_t>(GetChildren().size());
787     if (index < 0 || index >= size) {
788         return nullptr;
789     }
790     auto pos = GetChildren().begin();
791     std::advance(pos, index);
792     return *pos;
793 }
794 
ProvideRestoreInfo()795 std::string RenderTabBar::ProvideRestoreInfo()
796 {
797     auto jsonObj = JsonUtil::Create(true);
798     jsonObj->Put("index", index_);
799     jsonObj->Put("OffsetX", scrollableOffset_.GetX());
800     jsonObj->Put("OffsetY", scrollableOffset_.GetY());
801     return jsonObj->ToString();
802 }
803 
ApplyRestoreInfo(const RefPtr<TabController> & controller)804 void RenderTabBar::ApplyRestoreInfo(const RefPtr<TabController>& controller)
805 {
806     auto parent = GetParent().Upgrade();
807     if (!parent) {
808         LOGE("parent is nullptr");
809         return;
810     }
811     auto grandParent = parent->GetParent().Upgrade();
812     if (!grandParent) {
813         LOGE("grandParent is nullptr");
814         return;
815     }
816     std::string restoreInfo = grandParent->GetRestoreInfo();
817     if (restoreInfo.empty()) {
818         return;
819     }
820     auto info = JsonUtil::ParseJsonString(restoreInfo);
821     if (!info->IsValid() || !info->IsObject()) {
822         LOGW("RenderTabBar:: restore info is invalid");
823         return;
824     }
825 
826     auto jsonIndex = info->GetValue("index");
827     auto jsonOffsetX = info->GetValue("OffsetX");
828     auto jsonOffsetY = info->GetValue("OffsetY");
829 
830     index_ = jsonIndex->GetInt();
831     SetIndex(index_, true);
832     controller->SetIndexByController(index_, false);
833     controller->SetPendingIndex(index_);
834     controller->SetInitialIndex(index_);
835     scrollableOffset_.SetX(jsonOffsetX->GetDouble());
836     scrollableOffset_.SetY(jsonOffsetY->GetDouble());
837     grandParent->SetRestoreInfo("");
838 }
839 
840 } // namespace OHOS::Ace
841