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