1 /*
2 * Copyright (c) 2022-2023 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_pattern.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/memory/ace_type.h"
23 #include "base/utils/utils.h"
24 #include "core/components/common/layout/constants.h"
25 #include "core/components/scroll/scrollable.h"
26 #include "core/components/tab_bar/tab_theme.h"
27 #include "core/components_ng/base/frame_node.h"
28 #include "core/components_ng/pattern/image/image_layout_property.h"
29 #include "core/components_ng/pattern/image/image_pattern.h"
30 #include "core/components_ng/pattern/scroll/scroll_spring_effect.h"
31 #include "core/components_ng/pattern/swiper/swiper_pattern.h"
32 #include "core/components_ng/pattern/tabs/tabs_layout_property.h"
33 #include "core/components_ng/pattern/tabs/tabs_node.h"
34 #include "core/components_ng/pattern/tabs/tabs_pattern.h"
35 #include "core/components_ng/pattern/text/text_layout_property.h"
36 #include "core/components_ng/property/property.h"
37 #include "core/components_v2/inspector/inspector_constants.h"
38 #include "core/pipeline_ng/pipeline_context.h"
39
40 namespace OHOS::Ace::NG {
41 namespace {
42 constexpr int8_t LEFT_GRADIENT = 0;
43 constexpr int8_t RIGHT_GRADIENT = 1;
44 constexpr int8_t TOP_GRADIENT = 2;
45 constexpr int8_t BOTTOM_GRADIENT = 3;
46 constexpr float HALF_PROGRESS = 0.5f;
47 constexpr float FULL_PROGRESS = 1.0f;
48 constexpr float HALF_MASK_RADIUS_RATIO = 0.717f;
49 constexpr float FULL_MASK_RADIUS_RATIO = 1.414f;
50 constexpr float INVALID_RATIO = -1.0f;
51 constexpr uint16_t MASK_ANIMATION_DURATION = 200;
52 constexpr int8_t MASK_COUNT = 2;
53 constexpr float FULL_OPACITY = 1.0f;
54 constexpr float NEAR_FULL_OPACITY = 0.99f;
55 constexpr float NO_OPACITY = 0.0f;
56 } // namespace
57
OnAttachToFrameNode()58 void TabBarPattern::OnAttachToFrameNode()
59 {
60 auto host = GetHost();
61 CHECK_NULL_VOID(host);
62 auto renderContext = host->GetRenderContext();
63 CHECK_NULL_VOID(renderContext);
64 renderContext->SetClipToFrame(true);
65 }
66
InitClick(const RefPtr<GestureEventHub> & gestureHub)67 void TabBarPattern::InitClick(const RefPtr<GestureEventHub>& gestureHub)
68 {
69 if (clickEvent_) {
70 return;
71 }
72 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
73 auto tabBar = weak.Upgrade();
74 if (tabBar) {
75 tabBar->HandleClick(info);
76 }
77 };
78 clickEvent_ = AceType::MakeRefPtr<ClickEvent>(std::move(clickCallback));
79 gestureHub->AddClickEvent(clickEvent_);
80 }
81
InitScrollable(const RefPtr<GestureEventHub> & gestureHub)82 void TabBarPattern::InitScrollable(const RefPtr<GestureEventHub>& gestureHub)
83 {
84 auto host = GetHost();
85 CHECK_NULL_VOID(host);
86 auto layoutProperty = host->GetLayoutProperty<TabBarLayoutProperty>();
87 CHECK_NULL_VOID(layoutProperty);
88 auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
89 if (axis_ == axis && scrollableEvent_) {
90 LOGD("Direction not changed, need't resister scroll event again.");
91 return;
92 }
93
94 axis_ = axis;
95 auto task = [weak = WeakClaim(this)](double offset, int32_t source) {
96 if (source == SCROLL_FROM_START) {
97 return true;
98 }
99 auto pattern = weak.Upgrade();
100 if (!pattern) {
101 return false;
102 }
103 if (pattern->controller_ && pattern->controller_->IsRunning()) {
104 auto scrollable = pattern->scrollableEvent_->GetScrollable();
105 if (scrollable) {
106 scrollable->StopScrollable();
107 }
108 return true;
109 }
110 if (pattern->tabBarStyle_ == TabBarStyle::SUBTABBATSTYLE && pattern->axis_ == Axis::HORIZONTAL &&
111 pattern->IsOutOfBoundary()) {
112 // over scroll in drag update from normal to over scroll.
113 float overScroll = 0.0f;
114 // over scroll in drag update during over scroll.
115 if (pattern->tabItemOffsets_.empty()) {
116 return false;
117 }
118 auto startPos =
119 pattern->tabItemOffsets_.begin()->GetX() - pattern->scrollMargin_ - pattern->GetLeftPadding();
120 auto host = pattern->GetHost();
121 CHECK_NULL_RETURN(host, false);
122 auto mainSize = host->GetGeometryNode()->GetPaddingSize().Width();
123 if (Positive(startPos)) {
124 overScroll = startPos;
125 } else {
126 overScroll = mainSize + pattern->GetLeftPadding() - pattern->tabItemOffsets_.back().GetX() -
127 pattern->scrollMargin_;
128 }
129
130 if (source == SCROLL_FROM_UPDATE) {
131 // adjust offset.
132 if (mainSize != 0.0f) {
133 auto friction = CalculateFriction(std::abs(overScroll) / mainSize);
134 pattern->UpdateCurrentOffset(static_cast<float>(offset * friction));
135 }
136 return true;
137 }
138 }
139 if (source == SCROLL_FROM_AXIS) {
140 pattern->AdjustOffset(offset);
141 }
142 pattern->UpdateCurrentOffset(static_cast<float>(offset));
143 return true;
144 };
145
146 if (scrollableEvent_) {
147 gestureHub->RemoveScrollableEvent(scrollableEvent_);
148 }
149
150 auto callback = [weak = WeakClaim(this)]() {
151 auto tabBarPattern = weak.Upgrade();
152 CHECK_NULL_VOID(tabBarPattern);
153 auto scrollable = tabBarPattern->scrollableEvent_->GetScrollable();
154 if (scrollable) {
155 scrollable->StopScrollable();
156 }
157 tabBarPattern->SetSwiperCurve(TabBarPhysicalCurve);
158 };
159
160 swiperController_->SetTabBarFinishCallback(std::move(callback));
161
162 scrollableEvent_ = MakeRefPtr<ScrollableEvent>(axis);
163 auto scrollable = MakeRefPtr<Scrollable>(task, axis);
164 scrollable->SetNodeId(host->GetAccessibilityId());
165 scrollable->Initialize(host->GetContext());
166 scrollableEvent_->SetScrollable(scrollable);
167 gestureHub->AddScrollableEvent(scrollableEvent_);
168 scrollableEvent_->GetScrollable()->SetEdgeEffect(EdgeEffect::SPRING);
169 }
170
InitTouch(const RefPtr<GestureEventHub> & gestureHub)171 void TabBarPattern::InitTouch(const RefPtr<GestureEventHub>& gestureHub)
172 {
173 if (touchEvent_) {
174 return;
175 }
176 auto touchCallback = [weak = WeakClaim(this)](const TouchEventInfo& info) {
177 auto pattern = weak.Upgrade();
178 CHECK_NULL_VOID(pattern);
179 pattern->HandleTouchEvent(info.GetTouches().front());
180 };
181 touchEvent_ = MakeRefPtr<TouchEventImpl>(std::move(touchCallback));
182 gestureHub->AddTouchEvent(touchEvent_);
183 }
184
InitHoverEvent()185 void TabBarPattern::InitHoverEvent()
186 {
187 if (hoverEvent_) {
188 return;
189 }
190 auto host = GetHost();
191 CHECK_NULL_VOID(host);
192 auto eventHub = GetHost()->GetEventHub<EventHub>();
193 auto inputHub = eventHub->GetOrCreateInputEventHub();
194
195 auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
196 auto pattern = weak.Upgrade();
197 if (pattern) {
198 pattern->HandleHoverEvent(isHover);
199 }
200 };
201 hoverEvent_ = MakeRefPtr<InputEvent>(std::move(hoverTask));
202 inputHub->AddOnHoverEvent(hoverEvent_);
203 }
204
InitMouseEvent()205 void TabBarPattern::InitMouseEvent()
206 {
207 if (mouseEvent_) {
208 return;
209 }
210 auto host = GetHost();
211 CHECK_NULL_VOID(host);
212 auto eventHub = GetHost()->GetEventHub<EventHub>();
213 auto inputHub = eventHub->GetOrCreateInputEventHub();
214 auto mouseTask = [weak = WeakClaim(this)](const MouseInfo& info) {
215 auto pattern = weak.Upgrade();
216 if (pattern) {
217 pattern->HandleMouseEvent(info);
218 }
219 };
220 mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
221 inputHub->AddOnMouseEvent(mouseEvent_);
222 }
223
HandleMouseEvent(const MouseInfo & info)224 void TabBarPattern::HandleMouseEvent(const MouseInfo& info)
225 {
226 if (IsContainsBuilder()) {
227 return;
228 }
229 auto host = GetHost();
230 CHECK_NULL_VOID(host);
231 auto totalCount = host->TotalChildCount() - MASK_COUNT;
232 if (totalCount < 0) {
233 return;
234 }
235 auto index = CalculateSelectedIndex(info.GetLocalLocation());
236 if (index < 0 || index >= totalCount) {
237 if (hoverIndex_.has_value() && !touchingIndex_.has_value()) {
238 HandleMoveAway(hoverIndex_.value());
239 }
240 hoverIndex_.reset();
241 return;
242 }
243 auto mouseAction = info.GetAction();
244 if (mouseAction == MouseAction::MOVE || mouseAction == MouseAction::WINDOW_ENTER) {
245 if (touchingIndex_.has_value()) {
246 hoverIndex_ = index;
247 return;
248 }
249 if (!hoverIndex_.has_value()) {
250 HandleHoverOnEvent(index);
251 hoverIndex_ = index;
252 return;
253 }
254 if (hoverIndex_.value() != index) {
255 HandleMoveAway(hoverIndex_.value());
256 HandleHoverOnEvent(index);
257 hoverIndex_ = index;
258 return;
259 }
260 return;
261 }
262 if (mouseAction == MouseAction::WINDOW_LEAVE) {
263 if (hoverIndex_.has_value()) {
264 HandleMoveAway(hoverIndex_.value());
265 }
266 }
267 }
268
HandleHoverEvent(bool isHover)269 void TabBarPattern::HandleHoverEvent(bool isHover)
270 {
271 if (IsContainsBuilder()) {
272 return;
273 }
274 isHover_ = isHover;
275 if (!isHover_ && hoverIndex_.has_value()) {
276 if (!touchingIndex_.has_value()) {
277 HandleMoveAway(hoverIndex_.value());
278 }
279 hoverIndex_.reset();
280 }
281 }
282
HandleHoverOnEvent(int32_t index)283 void TabBarPattern::HandleHoverOnEvent(int32_t index)
284 {
285 auto pipelineContext = PipelineContext::GetCurrentContext();
286 CHECK_NULL_VOID(pipelineContext);
287 auto tabTheme = pipelineContext->GetTheme<TabTheme>();
288 CHECK_NULL_VOID(tabTheme);
289 PlayPressAnimation(index, tabTheme->GetSubTabBarHoverColor(), AnimationType::HOVER);
290 }
291
HandleMoveAway(int32_t index)292 void TabBarPattern::HandleMoveAway(int32_t index)
293 {
294 PlayPressAnimation(index, Color::TRANSPARENT, AnimationType::HOVER);
295 }
296
InitOnKeyEvent(const RefPtr<FocusHub> & focusHub)297 void TabBarPattern::InitOnKeyEvent(const RefPtr<FocusHub>& focusHub)
298 {
299 auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
300 auto pattern = wp.Upgrade();
301 if (pattern) {
302 return pattern->OnKeyEvent(event);
303 }
304 return false;
305 };
306 focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
307
308 auto getInnerPaintRectCallback = [wp = WeakClaim(this)](RoundRect& paintRect) {
309 auto pattern = wp.Upgrade();
310 if (pattern) {
311 pattern->GetInnerFocusPaintRect(paintRect);
312 }
313 };
314 focusHub->SetInnerFocusPaintRectCallback(getInnerPaintRectCallback);
315 }
316
OnKeyEvent(const KeyEvent & event)317 bool TabBarPattern::OnKeyEvent(const KeyEvent& event)
318 {
319 auto pipeline = PipelineContext::GetCurrentContext();
320 CHECK_NULL_RETURN(pipeline, false);
321 if (!pipeline->GetIsFocusActive()) {
322 return false;
323 }
324 isFirstFocus_ = false;
325 if (event.action != KeyAction::DOWN) {
326 return false;
327 }
328 if (tabBarStyle_ == TabBarStyle::BOTTOMTABBATSTYLE || tabBarStyle_ == TabBarStyle::SUBTABBATSTYLE) {
329 return OnKeyEventWithoutClick(event);
330 }
331 auto host = GetHost();
332 CHECK_NULL_RETURN(host, false);
333 auto tabBarLayoutProperty = GetLayoutProperty<TabBarLayoutProperty>();
334 auto indicator = tabBarLayoutProperty->GetIndicatorValue(0);
335
336 if (event.code == (tabBarLayoutProperty->GetAxisValue(Axis::HORIZONTAL) == Axis::HORIZONTAL
337 ? KeyCode::KEY_DPAD_LEFT
338 : KeyCode::KEY_DPAD_UP) ||
339 event.IsShiftWith(KeyCode::KEY_TAB)) {
340 if (indicator <= 0) {
341 return false;
342 }
343 indicator -= 1;
344 FocusIndexChange(indicator);
345 return true;
346 }
347 if (event.code == (tabBarLayoutProperty->GetAxisValue(Axis::HORIZONTAL) == Axis::HORIZONTAL
348 ? KeyCode::KEY_DPAD_RIGHT
349 : KeyCode::KEY_DPAD_DOWN) ||
350 event.code == KeyCode::KEY_TAB) {
351 if (indicator >= host->TotalChildCount() - MASK_COUNT - 1) {
352 return false;
353 }
354 indicator += 1;
355 FocusIndexChange(indicator);
356 return true;
357 }
358 if (event.code == KeyCode::KEY_MOVE_HOME) {
359 indicator = 0;
360 FocusIndexChange(indicator);
361 return true;
362 }
363 if (event.code == KeyCode::KEY_MOVE_END) {
364 indicator = host->TotalChildCount() - MASK_COUNT - 1;
365 FocusIndexChange(indicator);
366 return true;
367 }
368 return false;
369 }
370
OnKeyEventWithoutClick(const KeyEvent & event)371 bool TabBarPattern::OnKeyEventWithoutClick(const KeyEvent& event)
372 {
373 auto host = GetHost();
374 CHECK_NULL_RETURN(host, false);
375 auto tabBarLayoutProperty = GetLayoutProperty<TabBarLayoutProperty>();
376
377 if (event.code == (tabBarLayoutProperty->GetAxisValue(Axis::HORIZONTAL) == Axis::HORIZONTAL
378 ? KeyCode::KEY_DPAD_LEFT
379 : KeyCode::KEY_DPAD_UP) ||
380 event.IsShiftWith(KeyCode::KEY_TAB)) {
381 if (focusIndicator_ <= 0) {
382 return false;
383 }
384 focusIndicator_ -= 1;
385 PaintFocusState();
386 return true;
387 }
388 if (event.code == (tabBarLayoutProperty->GetAxisValue(Axis::HORIZONTAL) == Axis::HORIZONTAL
389 ? KeyCode::KEY_DPAD_RIGHT
390 : KeyCode::KEY_DPAD_DOWN) ||
391 event.code == KeyCode::KEY_TAB) {
392 if (focusIndicator_ >= host->TotalChildCount() - MASK_COUNT - 1) {
393 return false;
394 }
395 focusIndicator_ += 1;
396 PaintFocusState();
397 return true;
398 }
399 if (event.code == KeyCode::KEY_MOVE_HOME) {
400 focusIndicator_ = 0;
401 PaintFocusState();
402 return true;
403 }
404 if (event.code == KeyCode::KEY_MOVE_END) {
405 focusIndicator_ = host->TotalChildCount() - MASK_COUNT - 1;
406 PaintFocusState();
407 return true;
408 }
409 if (event.code == KeyCode::KEY_SPACE || event.code == KeyCode::KEY_ENTER) {
410 TabBarClickEvent(focusIndicator_);
411 FocusIndexChange(focusIndicator_);
412 return true;
413 }
414 return false;
415 }
416
FocusIndexChange(int32_t index)417 void TabBarPattern::FocusIndexChange(int32_t index)
418 {
419 auto tabBarLayoutProperty = GetLayoutProperty<TabBarLayoutProperty>();
420 if (animationDuration_.has_value()) {
421 swiperController_->SwipeTo(index);
422 } else {
423 swiperController_->SwipeToWithoutAnimation(index);
424 }
425 tabBarLayoutProperty->UpdateIndicator(index);
426 PaintFocusState();
427 }
428
GetInnerFocusPaintRect(RoundRect & paintRect)429 void TabBarPattern::GetInnerFocusPaintRect(RoundRect& paintRect)
430 {
431 auto host = GetHost();
432 CHECK_NULL_VOID(host);
433 auto tabBarLayoutProperty = GetLayoutProperty<TabBarLayoutProperty>();
434 CHECK_NULL_VOID(tabBarLayoutProperty);
435 auto indicator = tabBarLayoutProperty->GetIndicatorValue(0);
436 if (tabBarStyle_ == TabBarStyle::BOTTOMTABBATSTYLE || tabBarStyle_ == TabBarStyle::SUBTABBATSTYLE) {
437 if (isFirstFocus_) {
438 focusIndicator_ = indicator;
439 } else {
440 indicator = focusIndicator_;
441 }
442 AdjustFocusPosition();
443 }
444 auto childNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(indicator));
445 CHECK_NULL_VOID(childNode);
446 auto renderContext = childNode->GetRenderContext();
447 CHECK_NULL_VOID(renderContext);
448 auto columnPaintRect = renderContext->GetPaintRectWithoutTransform();
449 auto pipeline = PipelineContext::GetCurrentContext();
450 CHECK_NULL_VOID(pipeline);
451 auto tabTheme = pipeline->GetTheme<TabTheme>();
452 CHECK_NULL_VOID(tabTheme);
453 auto radius = tabTheme->GetFocusIndicatorRadius();
454 auto outLineWidth = tabTheme->GetActiveIndicatorWidth();
455 columnPaintRect.SetOffset(OffsetF((columnPaintRect.GetOffset().GetX() + outLineWidth.ConvertToPx() / 2),
456 (columnPaintRect.GetOffset().GetY() + outLineWidth.ConvertToPx() / 2)));
457 columnPaintRect.SetSize(SizeF((columnPaintRect.GetSize().Width() - outLineWidth.ConvertToPx()),
458 (columnPaintRect.GetSize().Height() - outLineWidth.ConvertToPx())));
459
460 paintRect.SetRect(columnPaintRect);
461 paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_LEFT_POS, static_cast<RSScalar>(radius.ConvertToPx()),
462 static_cast<RSScalar>(radius.ConvertToPx()));
463 paintRect.SetCornerRadius(RoundRect::CornerPos::TOP_RIGHT_POS, static_cast<RSScalar>(radius.ConvertToPx()),
464 static_cast<RSScalar>(radius.ConvertToPx()));
465 paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_LEFT_POS, static_cast<RSScalar>(radius.ConvertToPx()),
466 static_cast<RSScalar>(radius.ConvertToPx()));
467 paintRect.SetCornerRadius(RoundRect::CornerPos::BOTTOM_RIGHT_POS, static_cast<RSScalar>(radius.ConvertToPx()),
468 static_cast<RSScalar>(radius.ConvertToPx()));
469 }
470
PaintFocusState()471 void TabBarPattern::PaintFocusState()
472 {
473 auto host = GetHost();
474 CHECK_NULL_VOID(host);
475
476 RoundRect focusRect;
477 GetInnerFocusPaintRect(focusRect);
478
479 auto focusHub = host->GetFocusHub();
480 CHECK_NULL_VOID(focusHub);
481 focusHub->PaintInnerFocusState(focusRect);
482
483 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
484 }
485
OnModifyDone()486 void TabBarPattern::OnModifyDone()
487 {
488 Pattern::OnModifyDone();
489 auto host = GetHost();
490 CHECK_NULL_VOID(host);
491 auto hub = host->GetEventHub<EventHub>();
492 CHECK_NULL_VOID(hub);
493 auto gestureHub = hub->GetOrCreateGestureEventHub();
494 CHECK_NULL_VOID(gestureHub);
495
496 InitClick(gestureHub);
497 InitTurnPageRateEvent();
498 auto layoutProperty = host->GetLayoutProperty<TabBarLayoutProperty>();
499 CHECK_NULL_VOID(layoutProperty);
500 if (layoutProperty->GetTabBarModeValue(TabBarMode::FIXED) == TabBarMode::SCROLLABLE) {
501 InitScrollable(gestureHub);
502 if (layoutProperty->GetAxisValue(Axis::HORIZONTAL) == Axis::HORIZONTAL) {
503 SetEdgeEffect(gestureHub);
504 }
505 }
506 InitTouch(gestureHub);
507 InitHoverEvent();
508 InitMouseEvent();
509 auto focusHub = host->GetFocusHub();
510 CHECK_NULL_VOID(focusHub);
511 InitOnKeyEvent(focusHub);
512 SetAccessibilityAction();
513 UpdateSubTabBoard();
514 needSetCentered_ = true;
515
516 CHECK_NULL_VOID(swiperController_);
517 auto removeEventCallback = [weak = WeakClaim(this)]() {
518 auto tabBarPattern = weak.Upgrade();
519 CHECK_NULL_VOID(tabBarPattern);
520 auto host = tabBarPattern->GetHost();
521 CHECK_NULL_VOID(host);
522 auto hub = host->GetEventHub<EventHub>();
523 CHECK_NULL_VOID(hub);
524 auto gestureHub = hub->GetOrCreateGestureEventHub();
525 CHECK_NULL_VOID(gestureHub);
526 auto layoutProperty = host->GetLayoutProperty<TabBarLayoutProperty>();
527 CHECK_NULL_VOID(layoutProperty);
528 gestureHub->RemoveClickEvent(tabBarPattern->clickEvent_);
529 if (layoutProperty->GetTabBarModeValue(TabBarMode::FIXED) == TabBarMode::SCROLLABLE) {
530 gestureHub->RemoveScrollableEvent(tabBarPattern->scrollableEvent_);
531 }
532 gestureHub->RemoveTouchEvent(tabBarPattern->touchEvent_);
533 tabBarPattern->isTouchingSwiper_ = true;
534 };
535 swiperController_->SetRemoveTabBarEventCallback(std::move(removeEventCallback));
536
537 auto addEventCallback = [weak = WeakClaim(this)]() {
538 auto tabBarPattern = weak.Upgrade();
539 CHECK_NULL_VOID(tabBarPattern);
540 auto host = tabBarPattern->GetHost();
541 CHECK_NULL_VOID(host);
542 auto hub = host->GetEventHub<EventHub>();
543 CHECK_NULL_VOID(hub);
544 auto gestureHub = hub->GetOrCreateGestureEventHub();
545 CHECK_NULL_VOID(gestureHub);
546 auto layoutProperty = host->GetLayoutProperty<TabBarLayoutProperty>();
547 CHECK_NULL_VOID(layoutProperty);
548 gestureHub->AddClickEvent(tabBarPattern->clickEvent_);
549 if (layoutProperty->GetTabBarModeValue(TabBarMode::FIXED) == TabBarMode::SCROLLABLE) {
550 gestureHub->AddScrollableEvent(tabBarPattern->scrollableEvent_);
551 }
552 gestureHub->AddTouchEvent(tabBarPattern->touchEvent_);
553 };
554 swiperController_->SetAddTabBarEventCallback(std::move(addEventCallback));
555 }
556
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)557 bool TabBarPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
558 {
559 if (config.skipMeasure && config.skipLayout) {
560 return false;
561 }
562 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
563 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
564 auto tabBarLayoutAlgorithm = DynamicCast<TabBarLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
565 CHECK_NULL_RETURN(tabBarLayoutAlgorithm, false);
566 tabItemOffsets_ = tabBarLayoutAlgorithm->GetTabItemOffset();
567 currentOffset_ = tabBarLayoutAlgorithm->GetCurrentOffset();
568 childrenMainSize_ = tabBarLayoutAlgorithm->GetChildrenMainSize();
569 indicator_ = tabBarLayoutAlgorithm->GetIndicator();
570 scrollMargin_ = tabBarLayoutAlgorithm->GetScrollMargin();
571 auto layoutProperty = DynamicCast<TabBarLayoutProperty>(dirty->GetLayoutProperty());
572 auto host = GetHost();
573 CHECK_NULL_RETURN(host, false);
574 auto tabsFrameNode = AceType::DynamicCast<TabsNode>(host->GetParent());
575 CHECK_NULL_RETURN(tabsFrameNode, false);
576 auto swiperFrameNode = AceType::DynamicCast<FrameNode>(tabsFrameNode->GetTabs());
577 CHECK_NULL_RETURN(swiperFrameNode, false);
578 auto swiperPattern = swiperFrameNode->GetPattern<SwiperPattern>();
579 CHECK_NULL_RETURN(swiperPattern, false);
580 int32_t indicator = swiperPattern->GetCurrentIndex();
581 int32_t totalCount = swiperPattern->TotalCount();
582 if (indicator > totalCount - 1 || indicator < 0) {
583 indicator = 0;
584 }
585 if (!isAnimating_) {
586 UpdateIndicator(indicator);
587 }
588 UpdateGradientRegions();
589 return false;
590 }
591
HandleClick(const GestureEvent & info)592 void TabBarPattern::HandleClick(const GestureEvent& info)
593 {
594 if (info.GetSourceDevice() == SourceType::KEYBOARD) {
595 return;
596 }
597 auto host = GetHost();
598 CHECK_NULL_VOID(host);
599 auto layoutProperty = host->GetLayoutProperty<TabBarLayoutProperty>();
600 CHECK_NULL_VOID(layoutProperty);
601 if (layoutProperty->GetTabBarModeValue(TabBarMode::FIXED) == TabBarMode::SCROLLABLE &&
602 layoutProperty->GetAxis() == Axis::HORIZONTAL) {
603 auto scrollable = scrollableEvent_->GetScrollable();
604 if (scrollable && !scrollable->IsSpringStopped()) {
605 if (IsOutOfBoundary()) {
606 LOGD("Tabbar is scrolling");
607 return;
608 }
609 scrollable->StopScrollable();
610 }
611 }
612 LOGI("Click event x is %{public}lf", info.GetLocalLocation().GetX());
613 if (tabItemOffsets_.empty()) {
614 LOGW("tabItemOffsets is empty");
615 return;
616 }
617
618 auto totalCount = host->TotalChildCount() - MASK_COUNT;
619 if (totalCount < 0) {
620 return;
621 }
622
623 auto index = CalculateSelectedIndex(info.GetLocalLocation());
624 if (index < 0 || index >= totalCount || !swiperController_ ||
625 indicator_ >= static_cast<int32_t>(tabBarStyles_.size())) {
626 return;
627 }
628 auto curve = MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f);
629 SetSwiperCurve(curve);
630 TabBarClickEvent(index);
631 if (tabBarStyles_[indicator_] == TabBarStyle::SUBTABBATSTYLE &&
632 tabBarStyles_[index] == TabBarStyle::SUBTABBATSTYLE && layoutProperty->GetAxis() == Axis::HORIZONTAL) {
633 HandleSubTabBarClick(layoutProperty, index);
634 return;
635 }
636 if (animationDuration_.has_value()) {
637 swiperController_->SwipeTo(index);
638 } else {
639 swiperController_->SwipeToWithoutAnimation(index);
640 }
641 layoutProperty->UpdateIndicator(index);
642 }
643
HandleBottomTabBarChange(int32_t index)644 void TabBarPattern::HandleBottomTabBarChange(int32_t index)
645 {
646 AnimationUtils::CloseImplicitAnimation();
647 UpdateImageColor(index);
648 if (indicator_ != index && (tabBarStyles_[indicator_] == TabBarStyle::BOTTOMTABBATSTYLE ||
649 tabBarStyles_[index] == TabBarStyle::BOTTOMTABBATSTYLE)) {
650 int32_t selectedIndex = -1;
651 int32_t unselectedIndex = -1;
652 if (tabBarStyles_[indicator_] == TabBarStyle::BOTTOMTABBATSTYLE && CheckSvg(indicator_)) {
653 unselectedIndex = indicator_;
654 }
655 if (tabBarStyles_[index] == TabBarStyle::BOTTOMTABBATSTYLE && CheckSvg(index)) {
656 selectedIndex = index;
657 }
658 HandleBottomTabBarClick(selectedIndex, unselectedIndex);
659 }
660 }
661
CheckSvg(int32_t index) const662 bool TabBarPattern::CheckSvg(int32_t index) const
663 {
664 auto host = GetHost();
665 CHECK_NULL_RETURN(host, false);
666 auto columnNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(index));
667 CHECK_NULL_RETURN(columnNode, false);
668 auto imageNode = AceType::DynamicCast<FrameNode>(columnNode->GetChildren().front());
669 CHECK_NULL_RETURN(imageNode, false);
670 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
671 CHECK_NULL_RETURN(imageLayoutProperty, false);
672 ImageSourceInfo info;
673 auto imageSourceInfo = imageLayoutProperty->GetImageSourceInfo().value_or(info);
674 return imageSourceInfo.IsSvg();
675 }
676
HandleBottomTabBarClick(int32_t selectedIndex,int32_t unselectedIndex)677 void TabBarPattern::HandleBottomTabBarClick(int32_t selectedIndex, int32_t unselectedIndex)
678 {
679 auto host = GetHost();
680 CHECK_NULL_VOID(host);
681 auto layoutProperty = host->GetLayoutProperty<TabBarLayoutProperty>();
682 CHECK_NULL_VOID(layoutProperty);
683
684 std::vector<int32_t> selectedIndexes = {selectedIndex, unselectedIndex};
685 OffsetF originalSelectedMaskOffset, originalUnselectedMaskOffset;
686 float selectedImageSize = 0.0f, unselectedImageSize = 0.0f;
687 for (int32_t maskIndex = 0; maskIndex < MASK_COUNT; maskIndex++) {
688 if (selectedIndexes[maskIndex] < 0) {
689 continue;
690 }
691 if (maskIndex == 0) {
692 layoutProperty->UpdateSelectedMask(selectedIndex);
693 } else {
694 layoutProperty->UpdateUnselectedMask(unselectedIndex);
695 }
696 GetBottomTabBarImageSizeAndOffset(selectedIndexes, maskIndex, selectedImageSize, unselectedImageSize,
697 originalSelectedMaskOffset, originalUnselectedMaskOffset);
698 }
699 if (selectedIndex >= 0) {
700 ChangeMask(host, selectedImageSize, originalSelectedMaskOffset, NO_OPACITY, HALF_MASK_RADIUS_RATIO, true);
701 }
702 if (unselectedIndex >= 0) {
703 ChangeMask(host, unselectedImageSize, originalUnselectedMaskOffset, FULL_OPACITY, FULL_MASK_RADIUS_RATIO,
704 false);
705 }
706 host->MarkDirtyNode();
707 PlayMaskAnimation(selectedImageSize, originalSelectedMaskOffset, selectedIndex, unselectedImageSize,
708 originalUnselectedMaskOffset, unselectedIndex);
709 }
710
GetBottomTabBarImageSizeAndOffset(const std::vector<int32_t> & selectedIndexes,int32_t maskIndex,float & selectedImageSize,float & unselectedImageSize,OffsetF & originalSelectedMaskOffset,OffsetF & originalUnselectedMaskOffset)711 void TabBarPattern::GetBottomTabBarImageSizeAndOffset(const std::vector<int32_t>& selectedIndexes, int32_t maskIndex,
712 float& selectedImageSize, float& unselectedImageSize, OffsetF& originalSelectedMaskOffset,
713 OffsetF& originalUnselectedMaskOffset)
714 {
715 auto pipelineContext = PipelineContext::GetCurrentContext();
716 CHECK_NULL_VOID(pipelineContext);
717 auto tabTheme = pipelineContext->GetTheme<TabTheme>();
718 CHECK_NULL_VOID(tabTheme);
719
720 auto host = GetHost();
721 CHECK_NULL_VOID(host);
722
723 auto columnNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(selectedIndexes[maskIndex]));
724 CHECK_NULL_VOID(columnNode);
725 auto imageNode = AceType::DynamicCast<FrameNode>(columnNode->GetChildren().front());
726 CHECK_NULL_VOID(imageNode);
727 auto imageGeometryNode = imageNode->GetGeometryNode();
728 CHECK_NULL_VOID(imageGeometryNode);
729 auto imageOffset = imageGeometryNode->GetFrameOffset();
730 auto imageSize = imageGeometryNode->GetFrameSize().Width();
731 if (maskIndex == 0) {
732 selectedImageSize = imageSize;
733 } else {
734 unselectedImageSize = imageSize;
735 }
736 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
737 CHECK_NULL_VOID(imageLayoutProperty);
738 ImageSourceInfo info;
739 auto imageSourceInfo = imageLayoutProperty->GetImageSourceInfo().value_or(info);
740
741 auto maskPosition = host->GetChildren().size() - MASK_COUNT;
742 if (maskPosition < 0) {
743 return;
744 }
745 auto selectedMaskNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(maskPosition + maskIndex));
746 CHECK_NULL_VOID(selectedMaskNode);
747 if (maskIndex == 0) {
748 originalSelectedMaskOffset = imageOffset;
749 } else {
750 originalUnselectedMaskOffset = imageOffset;
751 }
752 auto selectedImageNode = AceType::DynamicCast<FrameNode>(selectedMaskNode->GetChildren().front());
753 CHECK_NULL_VOID(selectedImageNode);
754
755 auto selectedImageLayoutProperty = selectedImageNode->GetLayoutProperty<ImageLayoutProperty>();
756 CHECK_NULL_VOID(selectedImageLayoutProperty);
757 imageSourceInfo.SetFillColor(tabTheme->GetBottomTabIconOn());
758 selectedImageLayoutProperty->UpdateImageSourceInfo(imageSourceInfo);
759
760 imageSourceInfo.SetFillColor(tabTheme->GetBottomTabIconOff());
761 imageLayoutProperty->UpdateImageSourceInfo(imageSourceInfo);
762
763 selectedImageNode->MarkModifyDone();
764 selectedImageNode->MarkDirtyNode();
765 imageNode->MarkModifyDone();
766 imageNode->MarkDirtyNode();
767 }
768
PlayMaskAnimation(float selectedImageSize,const OffsetF & originalSelectedMaskOffset,int32_t selectedIndex,float unselectedImageSize,const OffsetF & originalUnselectedMaskOffset,int32_t unselectedIndex)769 void TabBarPattern::PlayMaskAnimation(float selectedImageSize,
770 const OffsetF& originalSelectedMaskOffset, int32_t selectedIndex, float unselectedImageSize,
771 const OffsetF& originalUnselectedMaskOffset, int32_t unselectedIndex)
772 {
773 auto curve = AceType::MakeRefPtr<CubicCurve>(0.4f, 0.0f, 0.2f, 1.0f);
774 AnimationOption option;
775 option.SetDuration(MASK_ANIMATION_DURATION);
776 option.SetCurve(curve);
777
778 AnimationUtils::OpenImplicitAnimation(option, option.GetCurve(), [weak = AceType::WeakClaim(this),
779 selectedIndex = selectedIndex, unselectedIndex = unselectedIndex]() {
780 auto tabBar = weak.Upgrade();
781 if (tabBar) {
782 auto host = tabBar->GetHost();
783 CHECK_NULL_VOID(host);
784 MaskAnimationFinish(host, selectedIndex, true);
785 MaskAnimationFinish(host, unselectedIndex, false);
786 tabBar->UpdateImageColor(selectedIndex);
787 }
788 });
789
790 AnimationUtils::AddKeyFrame(HALF_PROGRESS, [weak = AceType::WeakClaim(this),
791 selectedImageSize = selectedImageSize, originalSelectedMaskOffset = originalSelectedMaskOffset,
792 unselectedImageSize = unselectedImageSize, originalUnselectedMaskOffset = originalUnselectedMaskOffset]() {
793 auto tabBar = weak.Upgrade();
794 if (tabBar) {
795 auto host = tabBar->GetHost();
796 CHECK_NULL_VOID(host);
797 ChangeMask(host, selectedImageSize, originalSelectedMaskOffset, FULL_OPACITY, INVALID_RATIO, true);
798 ChangeMask(host, unselectedImageSize, originalUnselectedMaskOffset, NEAR_FULL_OPACITY, INVALID_RATIO,
799 false);
800 }
801 });
802
803 AnimationUtils::AddKeyFrame(FULL_PROGRESS, [weak = AceType::WeakClaim(this),
804 selectedImageSize = selectedImageSize, originalSelectedMaskOffset = originalSelectedMaskOffset,
805 unselectedImageSize = unselectedImageSize, originalUnselectedMaskOffset = originalUnselectedMaskOffset]() {
806 auto tabBar = weak.Upgrade();
807 if (tabBar) {
808 auto host = tabBar->GetHost();
809 CHECK_NULL_VOID(host);
810 ChangeMask(host, selectedImageSize, originalSelectedMaskOffset, FULL_OPACITY, FULL_MASK_RADIUS_RATIO, true);
811 ChangeMask(host, unselectedImageSize, originalUnselectedMaskOffset, NO_OPACITY, HALF_MASK_RADIUS_RATIO,
812 false);
813 }
814 });
815
816 AnimationUtils::CloseImplicitAnimation();
817 }
818
MaskAnimationFinish(const RefPtr<FrameNode> & host,int32_t selectedIndex,bool isSelected)819 void TabBarPattern::MaskAnimationFinish(const RefPtr<FrameNode>& host, int32_t selectedIndex,
820 bool isSelected)
821 {
822 if (selectedIndex < 0) {
823 return;
824 }
825 auto tabBarLayoutProperty = host->GetLayoutProperty<TabBarLayoutProperty>();
826 CHECK_NULL_VOID(tabBarLayoutProperty);
827 if (isSelected) {
828 tabBarLayoutProperty->UpdateSelectedMask(-1);
829 } else {
830 tabBarLayoutProperty->UpdateUnselectedMask(-1);
831 }
832
833 auto columnNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(selectedIndex));
834 CHECK_NULL_VOID(columnNode);
835 auto imageNode = AceType::DynamicCast<FrameNode>(columnNode->GetChildren().front());
836 CHECK_NULL_VOID(imageNode);
837
838 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
839 CHECK_NULL_VOID(imageLayoutProperty);
840 ImageSourceInfo info;
841 auto imageSourceInfo = imageLayoutProperty->GetImageSourceInfo().value_or(info);
842
843 auto pipelineContext = PipelineContext::GetCurrentContext();
844 CHECK_NULL_VOID(pipelineContext);
845 auto tabTheme = pipelineContext->GetTheme<TabTheme>();
846 CHECK_NULL_VOID(tabTheme);
847 imageSourceInfo.SetFillColor(isSelected ? tabTheme->GetBottomTabIconOn() :
848 tabTheme->GetBottomTabIconOff());
849 imageLayoutProperty->UpdateImageSourceInfo(imageSourceInfo);
850
851 host->MarkDirtyNode();
852 imageNode->MarkModifyDone();
853 imageNode->MarkDirtyNode();
854 }
855
ChangeMask(const RefPtr<FrameNode> & host,float imageSize,const OffsetF & originalSelectedMaskOffset,float opacity,float radiusRatio,bool isSelected)856 void TabBarPattern::ChangeMask(const RefPtr<FrameNode>& host, float imageSize,
857 const OffsetF& originalSelectedMaskOffset, float opacity, float radiusRatio, bool isSelected)
858 {
859 if (NearZero(imageSize)) {
860 return;
861 }
862 auto maskPosition = host->GetChildren().size() - MASK_COUNT;
863 if (maskPosition < 0) {
864 return;
865 }
866 auto selectedMaskNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(maskPosition + !isSelected));
867 CHECK_NULL_VOID(selectedMaskNode);
868
869 auto selectedImageNode = AceType::DynamicCast<FrameNode>(selectedMaskNode->GetChildren().front());
870 CHECK_NULL_VOID(selectedImageNode);
871 auto selectedImageRenderContext = selectedImageNode->GetRenderContext();
872 CHECK_NULL_VOID(selectedImageRenderContext);
873
874 if (NonNegative(radiusRatio)) {
875 auto selectedMaskRenderContext = selectedMaskNode->GetRenderContext();
876 CHECK_NULL_VOID(selectedMaskRenderContext);
877
878 auto selectedMaskGeometryNode = selectedMaskNode->GetGeometryNode();
879 CHECK_NULL_VOID(selectedMaskGeometryNode);
880 OffsetF selectedMaskOffset = originalSelectedMaskOffset;
881 selectedMaskOffset.AddX(-imageSize * radiusRatio);
882 selectedMaskOffset.AddY(imageSize * (1.0f - radiusRatio));
883 selectedMaskGeometryNode->SetMarginFrameOffset(selectedMaskOffset);
884 selectedMaskGeometryNode->SetFrameSize(SizeF(imageSize * radiusRatio * 2.0f, imageSize * radiusRatio * 2.0f));
885 selectedMaskRenderContext->SyncGeometryProperties(nullptr);
886
887 BorderRadiusProperty borderRadiusProperty;
888 borderRadiusProperty.SetRadius(Dimension(imageSize * radiusRatio));
889 selectedMaskRenderContext->UpdateBorderRadius(borderRadiusProperty);
890
891 selectedImageRenderContext->UpdateOffset(OffsetT<Dimension>(Dimension(imageSize * radiusRatio),
892 Dimension(imageSize * (radiusRatio - 1.0f))));
893 auto selectedImageGeometryNode = selectedImageNode->GetGeometryNode();
894 CHECK_NULL_VOID(selectedImageGeometryNode);
895 selectedImageGeometryNode->SetFrameSize(SizeF(imageSize, imageSize));
896 auto selectedImageProperty = selectedImageNode->GetLayoutProperty<ImageLayoutProperty>();
897 selectedImageProperty->UpdateUserDefinedIdealSize(
898 CalcSize(NG::CalcLength(Dimension(imageSize)), NG::CalcLength(Dimension(imageSize))));
899 selectedImageRenderContext->SetVisible(false);
900 selectedImageRenderContext->SyncGeometryProperties(nullptr);
901 }
902 selectedImageRenderContext->UpdateOpacity(opacity);
903 }
904
HandleSubTabBarClick(const RefPtr<TabBarLayoutProperty> & layoutProperty,int32_t index)905 void TabBarPattern::HandleSubTabBarClick(const RefPtr<TabBarLayoutProperty>& layoutProperty, int32_t index)
906 {
907 auto host = GetHost();
908 CHECK_NULL_VOID(host);
909 auto tabsFrameNode = AceType::DynamicCast<TabsNode>(host->GetParent());
910 CHECK_NULL_VOID(tabsFrameNode);
911 auto swiperFrameNode = AceType::DynamicCast<FrameNode>(tabsFrameNode->GetTabs());
912 CHECK_NULL_VOID(swiperFrameNode);
913 auto swiperPattern = swiperFrameNode->GetPattern<SwiperPattern>();
914 CHECK_NULL_VOID(swiperPattern);
915 CHECK_NULL_VOID(swiperController_);
916 swiperController_->FinishAnimation();
917 int32_t indicator = swiperPattern->GetCurrentIndex();
918 if (indicator == index) {
919 return;
920 }
921 changeByClick_ = true;
922 auto originalPaintRect = layoutProperty->GetIndicatorRect(indicator);
923 auto targetPaintRect = layoutProperty->GetIndicatorRect(index);
924 auto paintProperty = host->GetPaintProperty<TabBarPaintProperty>();
925 CHECK_NULL_VOID(paintProperty);
926 paintProperty->UpdateIndicator(targetPaintRect);
927 float targetOffset = 0.0f;
928 if (host->GetGeometryNode()->GetPaddingSize().Width() < childrenMainSize_ &&
929 layoutProperty->GetTabBarModeValue(TabBarMode::FIXED) == TabBarMode::SCROLLABLE) {
930 auto space = GetSpace(index);
931 float frontChildrenMainSize = CalculateFrontChildrenMainSize(index);
932 float backChildrenMainSize = CalculateBackChildrenMainSize(index);
933 targetOffset = space < 0.0f ? -frontChildrenMainSize
934 : frontChildrenMainSize < space ? 0.0f
935 : backChildrenMainSize < space
936 ? host->GetGeometryNode()->GetPaddingSize().Width() - childrenMainSize_
937 : space - frontChildrenMainSize;
938 if (tabItemOffsets_.empty()) {
939 return;
940 }
941 PlayTranslateAnimation(originalPaintRect.GetX() + originalPaintRect.Width() / 2,
942 targetPaintRect.GetX() + targetPaintRect.Width() / 2 - tabItemOffsets_.front().GetX() + scrollMargin_ +
943 targetOffset + GetLeftPadding(),
944 targetOffset);
945 } else {
946 PlayTranslateAnimation(originalPaintRect.GetX() + originalPaintRect.Width() / 2,
947 targetPaintRect.GetX() + targetPaintRect.Width() / 2, targetOffset);
948 }
949 swiperController_->SwipeTo(index);
950 layoutProperty->UpdateIndicator(index);
951 }
952
HandleTouchEvent(const TouchLocationInfo & info)953 void TabBarPattern::HandleTouchEvent(const TouchLocationInfo& info)
954 {
955 if (IsContainsBuilder()) {
956 return;
957 }
958 auto host = GetHost();
959 CHECK_NULL_VOID(host);
960 auto totalCount = host->TotalChildCount() - MASK_COUNT;
961 if (totalCount < 0) {
962 return;
963 }
964 auto touchType = info.GetTouchType();
965 auto index = CalculateSelectedIndex(info.GetLocalLocation());
966 if (touchType == TouchType::DOWN && index >= 0 && index < totalCount) {
967 HandleTouchDown(index);
968 touchingIndex_ = index;
969 return;
970 }
971 if ((touchType == TouchType::UP || touchType == TouchType::CANCEL) && touchingIndex_.has_value()) {
972 HandleTouchUp(index);
973 touchingIndex_.reset();
974 }
975 }
976
CalculateSelectedIndex(const Offset & info)977 int32_t TabBarPattern::CalculateSelectedIndex(const Offset& info)
978 {
979 if (tabItemOffsets_.empty()) {
980 return -1;
981 }
982 auto host = GetHost();
983 CHECK_NULL_RETURN(host, -1);
984 auto geometryNode = host->GetGeometryNode();
985 CHECK_NULL_RETURN(geometryNode, -1);
986 auto frameSize = geometryNode->GetFrameSize();
987 auto layoutProperty = host->GetLayoutProperty<TabBarLayoutProperty>();
988 CHECK_NULL_RETURN(layoutProperty, -1);
989 auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
990 auto local = OffsetF(info.GetX(), info.GetY());
991 if (axis == Axis::VERTICAL) {
992 auto clickRange = std::make_pair(tabItemOffsets_[0].GetY(), tabItemOffsets_[tabItemOffsets_.size() - 1].GetY());
993 if (LessNotEqual(local.GetY(), clickRange.first) || GreatNotEqual(local.GetY(), clickRange.second)) {
994 LOGW("clicked (%{public}lf) position out of range [%{public}lf, %{public}lf]", local.GetY(),
995 clickRange.first, clickRange.second);
996 return -1;
997 }
998 } else {
999 auto clickRange = std::make_pair(tabItemOffsets_[0].GetX(), tabItemOffsets_[tabItemOffsets_.size() - 1].GetX());
1000 if (!isRTL_) {
1001 if (LessNotEqual(local.GetX(), clickRange.first) || GreatNotEqual(local.GetX(), clickRange.second)) {
1002 LOGW("clicked (%{public}lf) position out of range [%{public}lf, %{public}lf]", local.GetX(),
1003 clickRange.first, clickRange.second);
1004 return -1;
1005 }
1006 } else {
1007 if (GreatNotEqual(local.GetX(), frameSize.MainSize(axis)) ||
1008 LessNotEqual(local.GetX(), clickRange.second)) {
1009 LOGW("clicked (%{public}lf) position out of range [%{public}lf, %{public}lf]", local.GetX(),
1010 clickRange.first, clickRange.second);
1011 return -1;
1012 }
1013 }
1014 }
1015 auto pos = std::lower_bound(tabItemOffsets_.begin(), tabItemOffsets_.end(), local,
1016 [axis, isRTL = isRTL_](const OffsetF& a, const OffsetF& b) {
1017 return isRTL
1018 ? GreatNotEqual(a.GetX(), b.GetX())
1019 : (axis == Axis::VERTICAL ? LessNotEqual(a.GetY(), b.GetY()) : LessNotEqual(a.GetX(), b.GetX()));
1020 });
1021
1022 if (pos == tabItemOffsets_.end()) {
1023 return -1;
1024 }
1025 return isRTL_ ? std::distance(tabItemOffsets_.begin(), pos) : std::distance(tabItemOffsets_.begin(), pos) - 1;
1026 }
1027
HandleTouchDown(int32_t index)1028 void TabBarPattern::HandleTouchDown(int32_t index)
1029 {
1030 const auto& removeSwiperEventCallback = swiperController_->GetRemoveSwiperEventCallback();
1031 if (removeSwiperEventCallback) {
1032 removeSwiperEventCallback();
1033 }
1034 SetTouching(true);
1035 auto pipelineContext = PipelineContext::GetCurrentContext();
1036 CHECK_NULL_VOID(pipelineContext);
1037 auto tabTheme = pipelineContext->GetTheme<TabTheme>();
1038 CHECK_NULL_VOID(tabTheme);
1039 PlayPressAnimation(index, tabTheme->GetSubTabBarPressedColor(), AnimationType::PRESS);
1040 }
1041
HandleTouchUp(int32_t index)1042 void TabBarPattern::HandleTouchUp(int32_t index)
1043 {
1044 const auto& addSwiperEventCallback = swiperController_->GetAddSwiperEventCallback();
1045 if (addSwiperEventCallback) {
1046 addSwiperEventCallback();
1047 }
1048 auto pipelineContext = PipelineContext::GetCurrentContext();
1049 CHECK_NULL_VOID(pipelineContext);
1050 auto tabTheme = pipelineContext->GetTheme<TabTheme>();
1051 CHECK_NULL_VOID(tabTheme);
1052 if (IsTouching()) {
1053 SetTouching(false);
1054 if (hoverIndex_.has_value() && touchingIndex_.value_or(-1) == index) {
1055 PlayPressAnimation(index, tabTheme->GetSubTabBarHoverColor(), AnimationType::HOVERTOPRESS);
1056 return;
1057 }
1058 PlayPressAnimation(touchingIndex_.value_or(-1), Color::TRANSPARENT, AnimationType::PRESS);
1059 if (hoverIndex_.has_value()) {
1060 PlayPressAnimation(hoverIndex_.value(), tabTheme->GetSubTabBarHoverColor(), AnimationType::HOVER);
1061 }
1062 }
1063 }
1064
PlayPressAnimation(int32_t index,const Color & pressColor,AnimationType animationType)1065 void TabBarPattern::PlayPressAnimation(int32_t index, const Color& pressColor, AnimationType animationType)
1066 {
1067 auto pipelineContext = PipelineContext::GetCurrentContext();
1068 CHECK_NULL_VOID(pipelineContext);
1069 auto tabTheme = pipelineContext->GetTheme<TabTheme>();
1070 CHECK_NULL_VOID(tabTheme);
1071 AnimationOption option = AnimationOption();
1072 option.SetDuration(animationType == AnimationType::HOVERTOPRESS
1073 ? static_cast<int32_t>(tabTheme->GetSubTabBarHoverToPressDuration())
1074 : static_cast<int32_t>(tabTheme->GetSubTabBarHoverDuration()));
1075 option.SetDelay(0);
1076
1077 option.SetCurve(animationType == AnimationType::PRESS ? AceType::MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f)
1078 : animationType == AnimationType::HOVER ? Curves::FRICTION
1079 : Curves::SHARP);
1080 option.SetFillMode(FillMode::FORWARDS);
1081 Color color = pressColor;
1082 auto layoutProperty = GetLayoutProperty<TabBarLayoutProperty>();
1083 if (color == Color::TRANSPARENT && tabBarStyles_[index] == TabBarStyle::SUBTABBATSTYLE && index == indicator_ &&
1084 selectedModes_[index] == SelectedMode::BOARD && layoutProperty->GetAxis() == Axis::HORIZONTAL) {
1085 color = indicatorStyles_[index].color;
1086 }
1087 AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this), selectedIndex = index, color = color]() {
1088 auto tabBar = weak.Upgrade();
1089 if (tabBar) {
1090 auto host = tabBar->GetHost();
1091 CHECK_NULL_VOID(host);
1092 auto columnNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(selectedIndex));
1093 CHECK_NULL_VOID(columnNode);
1094 auto renderContext = columnNode->GetRenderContext();
1095 CHECK_NULL_VOID(renderContext);
1096 if (tabBar->tabBarStyles_[selectedIndex] != TabBarStyle::SUBTABBATSTYLE) {
1097 BorderRadiusProperty borderRadiusProperty;
1098 auto pipelineContext = PipelineContext::GetCurrentContext();
1099 auto tabTheme = pipelineContext->GetTheme<TabTheme>();
1100 borderRadiusProperty.SetRadius(tabTheme->GetFocusIndicatorRadius());
1101 renderContext->UpdateBorderRadius(borderRadiusProperty);
1102 }
1103 renderContext->UpdateBackgroundColor(color);
1104 columnNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1105 }
1106 }, [weak = AceType::WeakClaim(this), selectedIndex = index]() {
1107 auto tabBar = weak.Upgrade();
1108 if (tabBar) {
1109 if (tabBar->tabBarStyles_[selectedIndex] != TabBarStyle::SUBTABBATSTYLE) {
1110 auto host = tabBar->GetHost();
1111 auto columnNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(selectedIndex));
1112 auto renderContext = columnNode->GetRenderContext();
1113 renderContext->ResetBorderRadius();
1114 columnNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1115 }
1116 }
1117 });
1118 }
1119
UpdateCurrentOffset(float offset)1120 void TabBarPattern::UpdateCurrentOffset(float offset)
1121 {
1122 auto host = GetHost();
1123 CHECK_NULL_VOID(host);
1124 currentOffset_ = currentOffset_ + offset;
1125 UpdateIndicator(indicator_);
1126 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1127 }
1128
UpdateIndicator(int32_t indicator)1129 void TabBarPattern::UpdateIndicator(int32_t indicator)
1130 {
1131 auto layoutProperty = GetLayoutProperty<TabBarLayoutProperty>();
1132 CHECK_NULL_VOID(layoutProperty);
1133 layoutProperty->UpdateIndicator(indicator);
1134
1135 auto tabBarNode = GetHost();
1136 CHECK_NULL_VOID(tabBarNode);
1137 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
1138 CHECK_NULL_VOID(tabBarPattern);
1139 auto paintProperty = GetPaintProperty<TabBarPaintProperty>();
1140 if (indicator_ >= static_cast<int32_t>(tabBarStyles_.size())) {
1141 return;
1142 }
1143 if (tabBarPattern->IsContainsBuilder() || layoutProperty->GetAxis() == Axis::VERTICAL ||
1144 tabBarStyles_[indicator] == TabBarStyle::BOTTOMTABBATSTYLE) {
1145 paintProperty->UpdateIndicator({});
1146 tabBarNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1147 return;
1148 }
1149
1150 RectF rect = layoutProperty->GetIndicatorRect(indicator);
1151 paintProperty->UpdateIndicator(rect);
1152 currentIndicatorOffset_ = rect.GetX() + rect.Width() / 2;
1153 tabBarNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1154 if (tabBarStyles_[indicator] == TabBarStyle::SUBTABBATSTYLE) {
1155 UpdateSubTabBoard();
1156 }
1157 }
1158
UpdateGradientRegions()1159 void TabBarPattern::UpdateGradientRegions()
1160 {
1161 auto layoutProperty = GetLayoutProperty<TabBarLayoutProperty>();
1162 CHECK_NULL_VOID(layoutProperty);
1163 auto barMode = layoutProperty->GetTabBarMode().value_or(TabBarMode::FIXED);
1164 auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
1165 auto tarBarNode = GetHost();
1166 CHECK_NULL_VOID(tarBarNode);
1167 auto geometryNode = tarBarNode->GetGeometryNode();
1168 CHECK_NULL_VOID(geometryNode);
1169 auto frameRect = geometryNode->GetFrameRect();
1170
1171 std::fill(gradientRegions_.begin(), gradientRegions_.end(), false);
1172 if (barMode == TabBarMode::SCROLLABLE && !tabItemOffsets_.empty()) {
1173 if (axis == Axis::HORIZONTAL) {
1174 if (LessNotEqual(tabItemOffsets_.front().GetX() - GetLeftPadding(), scrollMargin_)) {
1175 gradientRegions_[LEFT_GRADIENT] = true;
1176 }
1177 if (GreatNotEqual(tabItemOffsets_.back().GetX() + scrollMargin_, frameRect.Width() - GetLeftPadding())) {
1178 gradientRegions_[RIGHT_GRADIENT] = true;
1179 }
1180 } else if (axis == Axis::VERTICAL) {
1181 if (LessNotEqual(tabItemOffsets_.front().GetY(), 0.0f)) {
1182 gradientRegions_[TOP_GRADIENT] = true;
1183 }
1184 if (GreatNotEqual(tabItemOffsets_.front().GetY() + childrenMainSize_, frameRect.Height())) {
1185 gradientRegions_[BOTTOM_GRADIENT] = true;
1186 }
1187 }
1188 }
1189 tarBarNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1190 }
1191
UpdateTextColor(int32_t indicator)1192 void TabBarPattern::UpdateTextColor(int32_t indicator)
1193 {
1194 auto tabBarNode = GetHost();
1195 CHECK_NULL_VOID(tabBarNode);
1196 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
1197 CHECK_NULL_VOID(tabBarPattern);
1198 if (tabBarPattern->IsContainsBuilder()) {
1199 return;
1200 }
1201 auto columnNode = DynamicCast<FrameNode>(tabBarNode->GetChildAtIndex(indicator));
1202 CHECK_NULL_VOID(columnNode);
1203 auto selectedColumnId = columnNode->GetId();
1204 auto pipelineContext = PipelineContext::GetCurrentContext();
1205 CHECK_NULL_VOID(pipelineContext);
1206 auto tabTheme = pipelineContext->GetTheme<TabTheme>();
1207 CHECK_NULL_VOID(tabTheme);
1208 for (const auto& columnNode : tabBarNode->GetChildren()) {
1209 CHECK_NULL_VOID(columnNode);
1210 auto textNode = AceType::DynamicCast<FrameNode>(columnNode->GetChildren().back());
1211 CHECK_NULL_VOID(textNode);
1212 auto textLayoutProperty = textNode->GetLayoutProperty<TextLayoutProperty>();
1213 CHECK_NULL_VOID(textLayoutProperty);
1214 if (columnNode->GetId() == selectedColumnId) {
1215 textLayoutProperty->UpdateTextColor(tabTheme->GetActiveIndicatorColor());
1216 } else {
1217 textLayoutProperty->UpdateTextColor(tabTheme->GetSubTabTextOffColor());
1218 }
1219 textNode->MarkModifyDone();
1220 textNode->MarkDirtyNode();
1221 }
1222 }
1223
UpdateImageColor(int32_t indicator)1224 void TabBarPattern::UpdateImageColor(int32_t indicator)
1225 {
1226 auto tabBarNode = GetHost();
1227 CHECK_NULL_VOID(tabBarNode);
1228 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
1229 CHECK_NULL_VOID(tabBarPattern);
1230 if (tabBarPattern->IsContainsBuilder()) {
1231 return;
1232 }
1233 auto columnNode = DynamicCast<FrameNode>(tabBarNode->GetChildAtIndex(indicator));
1234 CHECK_NULL_VOID(columnNode);
1235 auto selectedColumnId = columnNode->GetId();
1236 auto pipelineContext = PipelineContext::GetCurrentContext();
1237 CHECK_NULL_VOID(pipelineContext);
1238 auto tabTheme = pipelineContext->GetTheme<TabTheme>();
1239 CHECK_NULL_VOID(tabTheme);
1240 for (const auto& columnNode : tabBarNode->GetChildren()) {
1241 CHECK_NULL_VOID(columnNode);
1242 auto imageNode = AceType::DynamicCast<FrameNode>(columnNode->GetChildren().front());
1243 CHECK_NULL_VOID(imageNode);
1244
1245 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
1246 CHECK_NULL_VOID(imageLayoutProperty);
1247 ImageSourceInfo info;
1248 auto imageSourceInfo = imageLayoutProperty->GetImageSourceInfo().value_or(info);
1249 imageSourceInfo.SetFillColor(columnNode->GetId() == selectedColumnId ? tabTheme->GetBottomTabIconOn() :
1250 tabTheme->GetBottomTabIconOff());
1251 imageLayoutProperty->UpdateImageSourceInfo(imageSourceInfo);
1252 imageNode->MarkModifyDone();
1253 imageNode->MarkDirtyNode();
1254 }
1255 }
1256
UpdateSubTabBoard()1257 void TabBarPattern::UpdateSubTabBoard()
1258 {
1259 auto layoutProperty = GetLayoutProperty<TabBarLayoutProperty>();
1260 CHECK_NULL_VOID(layoutProperty);
1261 auto axis = layoutProperty->GetAxis().value_or(Axis::HORIZONTAL);
1262
1263 if (indicator_ >= static_cast<int32_t>(indicatorStyles_.size()) ||
1264 indicator_ >= static_cast<int32_t>(selectedModes_.size())) {
1265 return;
1266 }
1267 auto tabBarNode = GetHost();
1268 CHECK_NULL_VOID(tabBarNode);
1269 auto paintProperty = GetPaintProperty<TabBarPaintProperty>();
1270 CHECK_NULL_VOID(paintProperty);
1271 auto columnNode = DynamicCast<FrameNode>(tabBarNode->GetChildAtIndex(indicator_));
1272 CHECK_NULL_VOID(columnNode);
1273 auto selectedColumnId = columnNode->GetId();
1274
1275 for (const auto& columnNode : tabBarNode->GetChildren()) {
1276 CHECK_NULL_VOID(columnNode);
1277 auto columnFrameNode = AceType::DynamicCast<FrameNode>(columnNode);
1278 auto renderContext = columnFrameNode->GetRenderContext();
1279 CHECK_NULL_VOID(renderContext);
1280 if (tabBarStyles_[indicator_] == TabBarStyle::SUBTABBATSTYLE) {
1281 if (selectedModes_[indicator_] == SelectedMode::BOARD && columnFrameNode->GetId() == selectedColumnId &&
1282 axis == Axis::HORIZONTAL) {
1283 renderContext->UpdateBackgroundColor(indicatorStyles_[indicator_].color);
1284 } else {
1285 renderContext->UpdateBackgroundColor(Color::BLACK.BlendOpacity(0.0f));
1286 }
1287 columnFrameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1288 }
1289 }
1290 }
1291
GetSelectedMode() const1292 SelectedMode TabBarPattern::GetSelectedMode() const
1293 {
1294 if (indicator_ >= static_cast<int32_t>(selectedModes_.size())) {
1295 return SelectedMode::INDICATOR;
1296 } else {
1297 return selectedModes_[indicator_];
1298 }
1299 }
1300
IsContainsBuilder()1301 bool TabBarPattern::IsContainsBuilder()
1302 {
1303 return std::any_of(tabBarType_.begin(), tabBarType_.end(), [](const auto& isBuilder) { return isBuilder.second; });
1304 }
1305
PlayTranslateAnimation(float startPos,float endPos,float targetCurrentOffset)1306 void TabBarPattern::PlayTranslateAnimation(float startPos, float endPos, float targetCurrentOffset)
1307 {
1308 LOGI("Play translate animation startPos: %{public}lf, endPos: %{public}lf", startPos, endPos);
1309 auto curve = MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f);
1310 isAnimating_ = true;
1311
1312 // If animation is still running, stop it before play new animation.
1313 StopTranslateAnimation();
1314 SetSwiperCurve(curve);
1315
1316 auto translate = AceType::MakeRefPtr<CurveAnimation<double>>(startPos, endPos, curve);
1317 auto weak = AceType::WeakClaim(this);
1318 translate->AddListener(Animation<double>::ValueCallback([weak, startPos, endPos](double value) {
1319 auto tabBarPattern = weak.Upgrade();
1320 CHECK_NULL_VOID(tabBarPattern);
1321 tabBarPattern->UpdateIndicatorCurrentOffset(static_cast<float>(value - tabBarPattern->currentIndicatorOffset_));
1322 }));
1323 auto startCurrentOffset = currentOffset_;
1324 auto tabBarTranslate = AceType::MakeRefPtr<CurveAnimation<double>>(startCurrentOffset, targetCurrentOffset, curve);
1325 tabBarTranslate->AddListener(
1326 Animation<double>::ValueCallback([weak, startCurrentOffset, targetCurrentOffset](double value) {
1327 auto tabBarPattern = weak.Upgrade();
1328 CHECK_NULL_VOID(tabBarPattern);
1329 tabBarPattern->currentOffset_ = value;
1330 auto host = tabBarPattern->GetHost();
1331 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1332 }));
1333
1334 if (!controller_) {
1335 auto pipeline = PipelineBase::GetCurrentContext();
1336 CHECK_NULL_VOID(pipeline);
1337 controller_ = CREATE_ANIMATOR(pipeline);
1338 }
1339 controller_->ClearStopListeners();
1340 controller_->ClearInterpolators();
1341 auto pipelineContext = PipelineContext::GetCurrentContext();
1342 CHECK_NULL_VOID(pipelineContext);
1343 auto tabTheme = pipelineContext->GetTheme<TabTheme>();
1344 CHECK_NULL_VOID(tabTheme);
1345 controller_->SetDuration(
1346 static_cast<int32_t>(animationDuration_.value_or(tabTheme->GetTabContentAnimationDuration())));
1347 controller_->AddInterpolator(translate);
1348 controller_->AddInterpolator(tabBarTranslate);
1349 controller_->Play();
1350 }
1351
StopTranslateAnimation()1352 void TabBarPattern::StopTranslateAnimation()
1353 {
1354 if (controller_ && !controller_->IsStopped()) {
1355 controller_->Stop();
1356 }
1357 }
1358
PlayTabBarTranslateAnimation(int32_t targetIndex)1359 void TabBarPattern::PlayTabBarTranslateAnimation(int32_t targetIndex)
1360 {
1361 auto host = GetHost();
1362 CHECK_NULL_VOID(host);
1363 if (host->GetGeometryNode()->GetPaddingSize().Width() >= childrenMainSize_) {
1364 return;
1365 }
1366 auto space = GetSpace(targetIndex);
1367 float frontChildrenMainSize = CalculateFrontChildrenMainSize(targetIndex);
1368 float backChildrenMainSize = CalculateBackChildrenMainSize(targetIndex);
1369 auto targetOffset = space < 0.0f ? -frontChildrenMainSize
1370 : frontChildrenMainSize < space ? 0.0f
1371 : backChildrenMainSize < space
1372 ? host->GetGeometryNode()->GetPaddingSize().Width() - childrenMainSize_
1373 : space - frontChildrenMainSize;
1374 auto startOffset = currentOffset_;
1375 LOGI("Play translate animation startPos: %{public}lf, endPos: %{public}lf", startOffset, targetOffset);
1376 auto curve = MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f);
1377
1378 // If animation is still running, stop it before play new animation.
1379 StopTabBarTranslateAnimation();
1380
1381 auto weak = AceType::WeakClaim(this);
1382 auto tabBarTranslate = AceType::MakeRefPtr<CurveAnimation<double>>(startOffset, targetOffset, curve);
1383 tabBarTranslate->AddListener(Animation<double>::ValueCallback([weak, startOffset, targetOffset](double value) {
1384 auto tabBarPattern = weak.Upgrade();
1385 CHECK_NULL_VOID(tabBarPattern);
1386 if (!NearEqual(value, startOffset) && !NearEqual(value, targetOffset) &&
1387 !NearEqual(startOffset, targetOffset)) {
1388 float moveRate = Curves::EASE_OUT->MoveInternal(
1389 static_cast<float>((value - startOffset) / (targetOffset - startOffset)));
1390 value = startOffset + (targetOffset - startOffset) * moveRate;
1391 }
1392 tabBarPattern->currentOffset_ = value;
1393 auto host = tabBarPattern->GetHost();
1394 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1395 }));
1396
1397 if (!controller_) {
1398 auto pipeline = PipelineBase::GetCurrentContext();
1399 CHECK_NULL_VOID(pipeline);
1400 controller_ = CREATE_ANIMATOR(pipeline);
1401 }
1402 controller_->ClearStopListeners();
1403 controller_->ClearInterpolators();
1404 auto pipelineContext = PipelineContext::GetCurrentContext();
1405 CHECK_NULL_VOID(pipelineContext);
1406 auto tabTheme = pipelineContext->GetTheme<TabTheme>();
1407 CHECK_NULL_VOID(tabTheme);
1408 controller_->SetDuration(
1409 static_cast<int32_t>(animationDuration_.value_or(tabTheme->GetTabContentAnimationDuration())));
1410 controller_->AddInterpolator(tabBarTranslate);
1411 controller_->Play();
1412 }
1413
StopTabBarTranslateAnimation()1414 void TabBarPattern::StopTabBarTranslateAnimation()
1415 {
1416 if (tabBarTranslateController_ && !tabBarTranslateController_->IsStopped()) {
1417 tabBarTranslateController_->Stop();
1418 }
1419 }
1420
UpdateIndicatorCurrentOffset(float offset)1421 void TabBarPattern::UpdateIndicatorCurrentOffset(float offset)
1422 {
1423 currentIndicatorOffset_ = currentIndicatorOffset_ + offset;
1424 auto host = GetHost();
1425 CHECK_NULL_VOID(host);
1426 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1427 }
1428
CreateNodePaintMethod()1429 RefPtr<NodePaintMethod> TabBarPattern::CreateNodePaintMethod()
1430 {
1431 if (indicator_ < 0 || indicator_ >= static_cast<int32_t>(indicatorStyles_.size()) ||
1432 indicator_ >= static_cast<int32_t>(selectedModes_.size())) {
1433 return nullptr;
1434 }
1435 Color backgroundColor = Color::WHITE;
1436 auto tabBarNode = GetHost();
1437 CHECK_NULL_RETURN(tabBarNode, nullptr);
1438 auto tabBarRenderContext = tabBarNode->GetRenderContext();
1439 CHECK_NULL_RETURN(tabBarRenderContext, nullptr);
1440 if (tabBarRenderContext->GetBackgroundColor().has_value()) {
1441 backgroundColor = tabBarRenderContext->GetBackgroundColor().value();
1442 } else {
1443 auto tabsNode = AceType::DynamicCast<FrameNode>(tabBarNode->GetParent());
1444 CHECK_NULL_RETURN(tabsNode, nullptr);
1445 auto tabsRenderContext = tabsNode->GetRenderContext();
1446 CHECK_NULL_RETURN(tabsRenderContext, nullptr);
1447 backgroundColor = tabsRenderContext->GetBackgroundColor().value_or(Color::WHITE);
1448 }
1449 if (!tabBarModifier_) {
1450 tabBarModifier_ = AceType::MakeRefPtr<TabBarModifier>();
1451 }
1452
1453 IndicatorStyle indicatorStyle;
1454 GetIndicatorStyle(indicatorStyle);
1455
1456 return MakeRefPtr<TabBarPaintMethod>(tabBarModifier_, gradientRegions_, backgroundColor, indicatorStyle,
1457 currentIndicatorOffset_, selectedModes_[indicator_]);
1458 }
1459
GetIndicatorStyle(IndicatorStyle & indicatorStyle)1460 void TabBarPattern::GetIndicatorStyle(IndicatorStyle& indicatorStyle)
1461 {
1462 if (indicator_ < 0 || indicator_ >= static_cast<int32_t>(indicatorStyles_.size())) {
1463 return;
1464 }
1465 indicatorStyle = indicatorStyles_[indicator_];
1466
1467 auto host = GetHost();
1468 CHECK_NULL_VOID(host);
1469 auto layoutProperty = host->GetLayoutProperty<TabBarLayoutProperty>();
1470 CHECK_NULL_VOID(layoutProperty);
1471
1472 if (NonPositive(indicatorStyle.width.Value())) {
1473 indicatorStyle.width = Dimension(layoutProperty->GetIndicatorRect(indicator_).Width());
1474 }
1475
1476 if (!isTouchingSwiper_ || axis_ != Axis::HORIZONTAL) {
1477 return;
1478 }
1479
1480 if (swiperStartIndex_ < 0 || swiperStartIndex_ >= static_cast<int32_t>(tabBarStyles_.size()) ||
1481 tabBarStyles_[swiperStartIndex_] != TabBarStyle::SUBTABBATSTYLE ||
1482 swiperStartIndex_ >= static_cast<int32_t>(selectedModes_.size()) ||
1483 selectedModes_[swiperStartIndex_] != SelectedMode::INDICATOR ||
1484 swiperStartIndex_ >= static_cast<int32_t>(indicatorStyles_.size())) {
1485 return;
1486 }
1487
1488 auto nextIndex = swiperStartIndex_ + 1;
1489
1490 if (nextIndex < 0 || nextIndex >= static_cast<int32_t>(tabBarStyles_.size()) ||
1491 tabBarStyles_[nextIndex] != TabBarStyle::SUBTABBATSTYLE ||
1492 nextIndex >= static_cast<int32_t>(selectedModes_.size()) ||
1493 selectedModes_[nextIndex] != SelectedMode::INDICATOR ||
1494 nextIndex >= static_cast<int32_t>(indicatorStyles_.size())) {
1495 return;
1496 }
1497
1498 indicatorStyle = indicatorStyles_[swiperStartIndex_];
1499
1500 if (NonPositive(indicatorStyle.width.Value())) {
1501 indicatorStyle.width = Dimension(layoutProperty->GetIndicatorRect(swiperStartIndex_).Width());
1502 }
1503
1504 IndicatorStyle nextIndicatorStyle = indicatorStyles_[nextIndex];
1505 if (NonPositive(nextIndicatorStyle.width.Value())) {
1506 nextIndicatorStyle.width = Dimension(layoutProperty->GetIndicatorRect(nextIndex).Width());
1507 }
1508 indicatorStyle.width =
1509 Dimension(indicatorStyle.width.ConvertToPx() +
1510 (nextIndicatorStyle.width.ConvertToPx() - indicatorStyle.width.ConvertToPx()) * turnPageRate_);
1511 indicatorStyle.marginTop = Dimension(
1512 indicatorStyle.marginTop.ConvertToPx() +
1513 (nextIndicatorStyle.marginTop.ConvertToPx() - indicatorStyle.marginTop.ConvertToPx()) * turnPageRate_);
1514 indicatorStyle.height =
1515 Dimension(indicatorStyle.height.ConvertToPx() +
1516 (nextIndicatorStyle.height.ConvertToPx() - indicatorStyle.height.ConvertToPx()) * turnPageRate_);
1517 LinearColor color = LinearColor(indicatorStyle.color) +
1518 (LinearColor(nextIndicatorStyle.color) - LinearColor(indicatorStyle.color)) * turnPageRate_;
1519 indicatorStyle.color = color.ToColor();
1520
1521 if (LessOrEqual(turnPageRate_, 0.0f) || GreatOrEqual(turnPageRate_, 1.0f)) {
1522 isTouchingSwiper_ = false;
1523 turnPageRate_ = 0.0f;
1524 }
1525 }
1526
GetSpace(int32_t indicator)1527 float TabBarPattern::GetSpace(int32_t indicator)
1528 {
1529 auto host = GetHost();
1530 CHECK_NULL_RETURN(host, 0.0f);
1531 auto geometryNode = host->GetGeometryNode();
1532 auto childFrameNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(indicator));
1533 CHECK_NULL_RETURN(childFrameNode, 0.0f);
1534 auto childGeometryNode = childFrameNode->GetGeometryNode();
1535
1536 return (geometryNode->GetPaddingSize().MainSize(axis_) - childGeometryNode->GetMarginFrameSize().MainSize(axis_)) /
1537 2;
1538 }
1539
CalculateFrontChildrenMainSize(int32_t indicator)1540 float TabBarPattern::CalculateFrontChildrenMainSize(int32_t indicator)
1541 {
1542 auto host = GetHost();
1543 CHECK_NULL_RETURN(host, 0.0f);
1544 float frontChildrenMainSize = scrollMargin_;
1545 for (int32_t index = 0; index < indicator; ++index) {
1546 auto childFrameNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(index));
1547 CHECK_NULL_RETURN(childFrameNode, 0.0f);
1548 auto childGeometryNode = childFrameNode->GetGeometryNode();
1549 auto childFrameSize = childGeometryNode->GetMarginFrameSize();
1550 frontChildrenMainSize += childFrameSize.MainSize(axis_);
1551 }
1552 return frontChildrenMainSize;
1553 }
1554
CalculateBackChildrenMainSize(int32_t indicator)1555 float TabBarPattern::CalculateBackChildrenMainSize(int32_t indicator)
1556 {
1557 auto host = GetHost();
1558 CHECK_NULL_RETURN(host, 0.0f);
1559 float backChildrenMainSize = scrollMargin_;
1560 auto childCount = host->GetChildren().size() - MASK_COUNT;
1561 for (uint32_t index = static_cast<uint32_t>(indicator) + 1; index < childCount; ++index) {
1562 auto childFrameNode = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(index));
1563 CHECK_NULL_RETURN(childFrameNode, 0.0f);
1564 auto childGeometryNode = childFrameNode->GetGeometryNode();
1565 auto childFrameSize = childGeometryNode->GetMarginFrameSize();
1566 backChildrenMainSize += childFrameSize.MainSize(axis_);
1567 }
1568 return backChildrenMainSize;
1569 }
1570
SetEdgeEffect(const RefPtr<GestureEventHub> & gestureHub)1571 void TabBarPattern::SetEdgeEffect(const RefPtr<GestureEventHub>& gestureHub)
1572 {
1573 CHECK_NULL_VOID(gestureHub);
1574 if (scrollEffect_) {
1575 gestureHub->RemoveScrollEdgeEffect(scrollEffect_);
1576 scrollEffect_.Reset();
1577 }
1578 if (!scrollEffect_) {
1579 auto springEffect = AceType::MakeRefPtr<ScrollSpringEffect>();
1580 CHECK_NULL_VOID(springEffect);
1581 springEffect->SetOutBoundaryCallback([weak = AceType::WeakClaim(this)]() {
1582 auto pattern = weak.Upgrade();
1583 CHECK_NULL_RETURN_NOLOG(pattern, false);
1584 return pattern->IsAtTop() || pattern->IsAtBottom();
1585 });
1586 // add callback to springEdgeEffect
1587 SetEdgeEffectCallback(springEffect);
1588 scrollEffect_ = springEffect;
1589 gestureHub->AddScrollEdgeEffect(axis_, scrollEffect_);
1590 }
1591 }
1592
SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect> & scrollEffect)1593 void TabBarPattern::SetEdgeEffectCallback(const RefPtr<ScrollEdgeEffect>& scrollEffect)
1594 {
1595 scrollEffect->SetCurrentPositionCallback([weak = AceType::WeakClaim(this)]() -> double {
1596 auto tabBar = weak.Upgrade();
1597 CHECK_NULL_RETURN_NOLOG(tabBar, 0.0);
1598 return tabBar->tabBarStyle_ == TabBarStyle::SUBTABBATSTYLE ? tabBar->currentOffset_ : 0.0;
1599 });
1600 scrollEffect->SetLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
1601 auto tabBar = weak.Upgrade();
1602 auto host = tabBar->GetHost();
1603 return host->GetGeometryNode()->GetPaddingSize().Width() - tabBar->childrenMainSize_;
1604 });
1605 scrollEffect->SetTrailingCallback([]() -> double { return 0.0; });
1606 scrollEffect->SetInitLeadingCallback([weak = AceType::WeakClaim(this)]() -> double {
1607 auto tabBar = weak.Upgrade();
1608 auto host = tabBar->GetHost();
1609 return host->GetGeometryNode()->GetPaddingSize().Width() - tabBar->childrenMainSize_;
1610 });
1611 scrollEffect->SetInitTrailingCallback([]() -> double { return 0.0; });
1612 }
1613
IsAtTop() const1614 bool TabBarPattern::IsAtTop() const
1615 {
1616 return NonNegative(currentOffset_);
1617 }
1618
IsAtBottom() const1619 bool TabBarPattern::IsAtBottom() const
1620 {
1621 if (tabItemOffsets_.empty()) {
1622 return false;
1623 }
1624 auto host = GetHost();
1625 CHECK_NULL_RETURN(host, false);
1626 return LessOrEqual(tabItemOffsets_.back().GetX() + scrollMargin_,
1627 host->GetGeometryNode()->GetPaddingSize().Width() + GetLeftPadding());
1628 }
1629
IsOutOfBoundary()1630 bool TabBarPattern::IsOutOfBoundary()
1631 {
1632 if (tabItemOffsets_.empty()) {
1633 return false;
1634 }
1635 auto host = GetHost();
1636 CHECK_NULL_RETURN(host, false);
1637 auto geometryNode = host->GetGeometryNode();
1638 CHECK_NULL_RETURN(geometryNode, false);
1639
1640 auto mainSize = geometryNode->GetPaddingSize().Width();
1641 bool outOfStart = Positive(tabItemOffsets_.front().GetX() - scrollMargin_ - GetLeftPadding()) &&
1642 GreatNotEqual(tabItemOffsets_.back().GetX() + scrollMargin_, mainSize + GetLeftPadding());
1643 bool outOfEnd = LessNotEqual(tabItemOffsets_.back().GetX() + scrollMargin_, mainSize + GetLeftPadding()) &&
1644 Negative(tabItemOffsets_.front().GetX() - scrollMargin_ - GetLeftPadding());
1645 return outOfStart || outOfEnd;
1646 }
1647
SetAccessibilityAction()1648 void TabBarPattern::SetAccessibilityAction()
1649 {
1650 auto host = GetHost();
1651 CHECK_NULL_VOID(host);
1652 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1653 CHECK_NULL_VOID(accessibilityProperty);
1654 accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
1655 const auto& pattern = weakPtr.Upgrade();
1656 CHECK_NULL_VOID(pattern);
1657 auto tabBarLayoutProperty = pattern->GetLayoutProperty<TabBarLayoutProperty>();
1658 CHECK_NULL_VOID(tabBarLayoutProperty);
1659 auto frameNode = pattern->GetHost();
1660 CHECK_NULL_VOID(frameNode);
1661 if (tabBarLayoutProperty->GetTabBarMode().value_or(TabBarMode::FIXED) == TabBarMode::SCROLLABLE &&
1662 frameNode->TotalChildCount() - MASK_COUNT > 1) {
1663 auto index = pattern->GetIndicator() + 1;
1664 pattern->PlayTabBarTranslateAnimation(index);
1665 pattern->FocusIndexChange(index);
1666 frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
1667 }
1668 });
1669
1670 accessibilityProperty->SetActionScrollBackward([weakPtr = WeakClaim(this)]() {
1671 const auto& pattern = weakPtr.Upgrade();
1672 CHECK_NULL_VOID(pattern);
1673 auto tabBarLayoutProperty = pattern->GetLayoutProperty<TabBarLayoutProperty>();
1674 CHECK_NULL_VOID(tabBarLayoutProperty);
1675 auto frameNode = pattern->GetHost();
1676 CHECK_NULL_VOID(frameNode);
1677 if (tabBarLayoutProperty->GetTabBarMode().value_or(TabBarMode::FIXED) == TabBarMode::SCROLLABLE &&
1678 frameNode->TotalChildCount() - MASK_COUNT > 1) {
1679 auto index = pattern->GetIndicator() - 1;
1680 pattern->PlayTabBarTranslateAnimation(index);
1681 pattern->FocusIndexChange(index);
1682 frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
1683 }
1684 });
1685 }
1686
ProvideRestoreInfo()1687 std::string TabBarPattern::ProvideRestoreInfo()
1688 {
1689 auto jsonObj = JsonUtil::Create(true);
1690 auto tabBarLayoutProperty = GetLayoutProperty<TabBarLayoutProperty>();
1691 CHECK_NULL_RETURN(tabBarLayoutProperty, "");
1692 jsonObj->Put("Index", tabBarLayoutProperty->GetIndicator().value_or(0));
1693 return jsonObj->ToString();
1694 }
1695
OnRestoreInfo(const std::string & restoreInfo)1696 void TabBarPattern::OnRestoreInfo(const std::string& restoreInfo)
1697 {
1698 auto host = GetHost();
1699 CHECK_NULL_VOID(host);
1700 auto tabBarLayoutProperty = GetLayoutProperty<TabBarLayoutProperty>();
1701 CHECK_NULL_VOID(tabBarLayoutProperty);
1702 auto info = JsonUtil::ParseJsonString(restoreInfo);
1703 if (!info->IsValid() || !info->IsObject()) {
1704 return;
1705 }
1706 auto jsonIsOn = info->GetValue("Index");
1707 auto index = jsonIsOn->GetInt();
1708 auto totalCount = host->TotalChildCount();
1709 if (index < 0 || index >= totalCount || !swiperController_ ||
1710 indicator_ >= static_cast<int32_t>(tabBarStyles_.size())) {
1711 return;
1712 }
1713 tabBarLayoutProperty->UpdateIndicator(index);
1714 if (animationDuration_.has_value()) {
1715 swiperController_->SwipeTo(index);
1716 } else {
1717 swiperController_->SwipeToWithoutAnimation(index);
1718 }
1719 }
1720
ToJsonValue(std::unique_ptr<JsonValue> & json) const1721 void TabBarPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
1722 {
1723 Pattern::ToJsonValue(json);
1724 auto selectedModes = JsonUtil::CreateArray(true);
1725 for (const auto& selectedMode : selectedModes_) {
1726 auto mode = JsonUtil::Create(true);
1727 mode->Put("mode", selectedMode == SelectedMode::INDICATOR ? "INDICATOR" : "BOARD");
1728 selectedModes->Put(mode);
1729 }
1730 json->Put("selectedModes", selectedModes->ToString().c_str());
1731
1732 auto indicatorStyles = JsonUtil::CreateArray(true);
1733 for (const auto& indicatorStyle : indicatorStyles_) {
1734 auto indicator = JsonUtil::Create(true);
1735 indicator->Put("color", indicatorStyle.color.ColorToString().c_str());
1736 indicator->Put("height", indicatorStyle.height.ToString().c_str());
1737 indicator->Put("width", indicatorStyle.width.ToString().c_str());
1738 indicator->Put("borderRadius", indicatorStyle.borderRadius.ToString().c_str());
1739 indicator->Put("marginTop", indicatorStyle.marginTop.ToString().c_str());
1740 indicatorStyles->Put(indicator);
1741 }
1742 json->Put("indicatorStyles", indicatorStyles->ToString().c_str());
1743
1744 auto tabBarStyles = JsonUtil::CreateArray(true);
1745 for (const auto& tabBarStyle : tabBarStyles_) {
1746 auto style = JsonUtil::Create(true);
1747 style->Put("style", tabBarStyle == TabBarStyle::NOSTYLE ? "NOSTYLE"
1748 : tabBarStyle == TabBarStyle::SUBTABBATSTYLE ? "SUBTABBATSTYLE"
1749 : "BOTTOMTABBATSTYLE");
1750 tabBarStyles->Put(style);
1751 }
1752 json->Put("tabBarStyles", tabBarStyles->ToString().c_str());
1753 }
1754
FromJson(const std::unique_ptr<JsonValue> & json)1755 void TabBarPattern::FromJson(const std::unique_ptr<JsonValue>& json)
1756 {
1757 auto selectedModes = JsonUtil::ParseJsonString(json->GetString("selectedModes"));
1758 for (int32_t i = 0; i < selectedModes->GetArraySize(); i++) {
1759 auto selectedMode = selectedModes->GetArrayItem(i);
1760 auto mode = selectedMode->GetString("mode");
1761 SetSelectedMode(mode == "INDICATOR" ? SelectedMode::INDICATOR : SelectedMode::BOARD, i);
1762 }
1763
1764 auto indicatorStyles = JsonUtil::ParseJsonString(json->GetString("indicatorStyles"));
1765 for (int32_t i = 0; i < indicatorStyles->GetArraySize(); i++) {
1766 auto indicatorStyle = indicatorStyles->GetArrayItem(i);
1767 IndicatorStyle style;
1768 style.color = Color::ColorFromString(indicatorStyle->GetString("color"));
1769 style.height = Dimension::FromString(indicatorStyle->GetString("height"));
1770 style.width = Dimension::FromString(indicatorStyle->GetString("width"));
1771 style.borderRadius = Dimension::FromString(indicatorStyle->GetString("borderRadius"));
1772 style.marginTop = Dimension::FromString(indicatorStyle->GetString("marginTop"));
1773 SetIndicatorStyle(style, i);
1774 }
1775
1776 auto tabBarStyles = JsonUtil::ParseJsonString(json->GetString("tabBarStyles"));
1777 for (int32_t i = 0; i < tabBarStyles->GetArraySize(); i++) {
1778 auto tabBarStyle = tabBarStyles->GetArrayItem(i);
1779 auto style = tabBarStyle->GetString("style");
1780 SetTabBarStyle(style == "NOSTYLE" ? TabBarStyle::NOSTYLE
1781 : style == "SUBTABBATSTYLE" ? TabBarStyle::SUBTABBATSTYLE
1782 : TabBarStyle::BOTTOMTABBATSTYLE,
1783 i);
1784 }
1785
1786 auto layoutProperty = GetLayoutProperty<TabBarLayoutProperty>();
1787 CHECK_NULL_VOID(layoutProperty);
1788 auto indicatorValue = layoutProperty->GetIndicatorValue(0);
1789 UpdateIndicator(indicatorValue);
1790 Pattern::FromJson(json);
1791 }
1792
AdjustFocusPosition()1793 void TabBarPattern::AdjustFocusPosition()
1794 {
1795 auto host = GetHost();
1796 CHECK_NULL_VOID(host);
1797 auto layoutProperty = host->GetLayoutProperty<TabBarLayoutProperty>();
1798 CHECK_NULL_VOID(layoutProperty);
1799 if (focusIndicator_ < 0 || static_cast<uint32_t>(focusIndicator_ + 1) >= tabItemOffsets_.size() ||
1800 layoutProperty->GetTabBarModeValue(TabBarMode::FIXED) != TabBarMode::SCROLLABLE) {
1801 return;
1802 }
1803 if (axis_ == Axis::HORIZONTAL) {
1804 auto mainSize = host->GetGeometryNode()->GetPaddingSize().Width();
1805 if (LessNotEqual(tabItemOffsets_[focusIndicator_].GetX(), GetLeftPadding())) {
1806 currentOffset_ -= tabItemOffsets_[focusIndicator_].GetX() - GetLeftPadding();
1807 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1808 } else if (GreatNotEqual(tabItemOffsets_[focusIndicator_ + 1].GetX(), mainSize + GetLeftPadding())) {
1809 currentOffset_ += mainSize + GetLeftPadding() - tabItemOffsets_[focusIndicator_ + 1].GetX();
1810 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1811 }
1812 } else {
1813 auto mainSize = host->GetGeometryNode()->GetPaddingSize().Height();
1814 if (LessNotEqual(tabItemOffsets_[focusIndicator_].GetY(), 0.0f)) {
1815 currentOffset_ -= tabItemOffsets_[focusIndicator_].GetY();
1816 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1817 } else if (GreatNotEqual(tabItemOffsets_[focusIndicator_ + 1].GetY(), mainSize)) {
1818 currentOffset_ += mainSize - tabItemOffsets_[focusIndicator_ + 1].GetY();
1819 host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1820 }
1821 }
1822 }
1823
TabBarClickEvent(int32_t index) const1824 void TabBarPattern::TabBarClickEvent(int32_t index) const
1825 {
1826 auto host = GetHost();
1827 CHECK_NULL_VOID(host);
1828 auto tabsNode = AceType::DynamicCast<TabsNode>(host->GetParent());
1829 CHECK_NULL_VOID(tabsNode);
1830 auto tabsPattern = tabsNode->GetPattern<TabsPattern>();
1831 CHECK_NULL_VOID(tabsPattern);
1832 auto tabBarClickEvent = tabsPattern->GetTabBarClickEvent();
1833 CHECK_NULL_VOID(tabBarClickEvent);
1834 (*tabBarClickEvent)(index);
1835 }
1836
CheckSwiperDisable() const1837 bool TabBarPattern::CheckSwiperDisable() const
1838 {
1839 auto host = GetHost();
1840 CHECK_NULL_RETURN(host, true);
1841 auto tabsNode = AceType::DynamicCast<TabsNode>(host->GetParent());
1842 CHECK_NULL_RETURN(tabsNode, true);
1843 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
1844 CHECK_NULL_RETURN(swiperNode, true);
1845 auto swiperPaintProperty = swiperNode->GetPaintProperty<SwiperPaintProperty>();
1846 CHECK_NULL_RETURN(swiperPaintProperty, true);
1847 return swiperPaintProperty->GetDisableSwipe().value_or(false);
1848 }
1849
SetSwiperCurve(const RefPtr<Curve> & curve) const1850 void TabBarPattern::SetSwiperCurve(const RefPtr<Curve>& curve) const
1851 {
1852 auto host = GetHost();
1853 CHECK_NULL_VOID(host);
1854 auto tabsNode = AceType::DynamicCast<TabsNode>(host->GetParent());
1855 CHECK_NULL_VOID(tabsNode);
1856 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
1857 CHECK_NULL_VOID(swiperNode);
1858 auto swiperPaintProperty = swiperNode->GetPaintProperty<SwiperPaintProperty>();
1859 CHECK_NULL_VOID(swiperPaintProperty);
1860 swiperPaintProperty->UpdateCurve(curve);
1861 }
1862
ApplyTurnPageRateToIndicator(float turnPageRate)1863 void TabBarPattern::ApplyTurnPageRateToIndicator(float turnPageRate)
1864 {
1865 auto host = GetHost();
1866 CHECK_NULL_VOID(host);
1867 auto layoutProperty = host->GetLayoutProperty<TabBarLayoutProperty>();
1868 CHECK_NULL_VOID(layoutProperty);
1869 if (swiperStartIndex_ < 0 || swiperStartIndex_ >= static_cast<int32_t>(tabBarStyles_.size()) ||
1870 tabBarStyles_[swiperStartIndex_] != TabBarStyle::SUBTABBATSTYLE ||
1871 swiperStartIndex_ >= static_cast<int32_t>(selectedModes_.size()) ||
1872 selectedModes_[swiperStartIndex_] != SelectedMode::INDICATOR) {
1873 return;
1874 }
1875
1876 auto index = swiperStartIndex_ + 1;
1877 if (index < 0 || index >= static_cast<int32_t>(tabBarStyles_.size()) ||
1878 tabBarStyles_[index] != TabBarStyle::SUBTABBATSTYLE || index >= static_cast<int32_t>(selectedModes_.size()) ||
1879 selectedModes_[index] != SelectedMode::INDICATOR) {
1880 return;
1881 }
1882
1883 if (GreatOrEqual(turnPageRate, 1.0f)) {
1884 turnPageRate_ = 1.0f;
1885 } else if (LessOrEqual(turnPageRate, 0.0f)) {
1886 turnPageRate_ = 0.0f;
1887 } else {
1888 turnPageRate_ = turnPageRate;
1889 }
1890
1891 auto originalPaintRect = layoutProperty->GetIndicatorRect(swiperStartIndex_);
1892 auto targetPaintRect = layoutProperty->GetIndicatorRect(index);
1893 auto paintRectDiff = std::abs(targetPaintRect.GetX() + targetPaintRect.Width() / 2 - originalPaintRect.GetX() -
1894 originalPaintRect.Width() / 2);
1895
1896 currentIndicatorOffset_ = originalPaintRect.GetX() + originalPaintRect.Width() / 2 + paintRectDiff * turnPageRate_;
1897 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1898 }
1899
AdjustOffset(double & offset) const1900 void TabBarPattern::AdjustOffset(double& offset) const
1901 {
1902 auto host = GetHost();
1903 CHECK_NULL_VOID(host);
1904 auto geometryNode = host->GetGeometryNode();
1905 CHECK_NULL_VOID(geometryNode);
1906 auto mainSize = geometryNode->GetPaddingSize().Width();
1907 if (GreatNotEqual(currentOffset_ + offset, 0.0f)) {
1908 offset = -currentOffset_;
1909 } else if (LessNotEqual(childrenMainSize_ + currentOffset_ + offset, mainSize)) {
1910 offset = mainSize - childrenMainSize_ - currentOffset_;
1911 }
1912 }
1913
InitTurnPageRateEvent()1914 void TabBarPattern::InitTurnPageRateEvent()
1915 {
1916 auto turnPageRateCallback = [weak = WeakClaim(this)](int32_t swipingIndex, float turnPageRate) {
1917 auto pattern = weak.Upgrade();
1918 if (pattern) {
1919 if (!pattern->CheckSwiperDisable() && pattern->axis_ == Axis::HORIZONTAL && pattern->isTouchingSwiper_) {
1920 pattern->swiperStartIndex_ = swipingIndex;
1921 pattern->ApplyTurnPageRateToIndicator(turnPageRate);
1922 } else {
1923 pattern->turnPageRate_ = 0.0f;
1924 }
1925 }
1926 };
1927 swiperController_->SetTurnPageRateCallback(std::move(turnPageRateCallback));
1928 auto host = GetHost();
1929 CHECK_NULL_VOID(host);
1930 auto tabsNode = AceType::DynamicCast<TabsNode>(host->GetParent());
1931 CHECK_NULL_VOID(tabsNode);
1932 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
1933 auto eventHub = swiperNode->GetEventHub<SwiperEventHub>();
1934 CHECK_NULL_VOID(eventHub);
1935 eventHub->SetAnimationEndEvent([weak = WeakClaim(this)](int32_t index, const AnimationCallbackInfo& info) {
1936 auto pattern = weak.Upgrade();
1937 if (pattern) {
1938 pattern->isTouchingSwiper_ = false;
1939 }
1940 });
1941 }
1942
GetLeftPadding() const1943 float TabBarPattern::GetLeftPadding() const
1944 {
1945 auto host = GetHost();
1946 CHECK_NULL_RETURN(host, 0.0f);
1947 auto geometryNode = host->GetGeometryNode();
1948 CHECK_NULL_RETURN(geometryNode, 0.0f);
1949 if (!geometryNode->GetPadding()) {
1950 return 0.0f;
1951 }
1952 return geometryNode->GetPadding()->left.value_or(0.0f);
1953 }
1954 } // namespace OHOS::Ace::NG
1955