1 /*
2 * Copyright (c) 2022-2024 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/tabs_pattern.h"
17
18 #include "base/geometry/axis.h"
19 #include "base/geometry/dimension.h"
20 #include "base/log/log_wrapper.h"
21 #include "base/utils/utils.h"
22 #include "core/common/recorder/event_recorder.h"
23 #include "core/common/recorder/node_data_cache.h"
24 #include "core/components/common/layout/constants.h"
25 #include "core/components/tab_bar/tabs_event.h"
26 #include "core/components_ng/base/observer_handler.h"
27 #include "core/components_ng/pattern/divider/divider_layout_property.h"
28 #include "core/components_ng/pattern/divider/divider_render_property.h"
29 #include "core/components_ng/pattern/swiper/swiper_model.h"
30 #include "core/components_ng/pattern/swiper/swiper_pattern.h"
31 #include "core/components_ng/pattern/tabs/tab_bar_layout_property.h"
32 #include "core/components_ng/pattern/tabs/tab_bar_paint_property.h"
33 #include "core/components_ng/pattern/tabs/tab_bar_pattern.h"
34 #include "core/components_ng/pattern/tabs/tab_content_node.h"
35 #include "core/components_ng/pattern/tabs/tabs_layout_property.h"
36 #include "core/components_ng/pattern/tabs/tabs_node.h"
37 #include "core/components_ng/property/property.h"
38 #include "core/components_v2/inspector/inspector_constants.h"
39 #include "core/pipeline_ng/pipeline_context.h"
40
41 namespace OHOS::Ace::NG {
42 namespace {
43 constexpr int32_t CHILDREN_MIN_SIZE = 2;
44 constexpr char APP_TABS_NO_ANIMATION_SWITCH[] = "APP_TABS_NO_ANIMATION_SWITCH";
45 } // namespace
46
OnAttachToFrameNode()47 void TabsPattern::OnAttachToFrameNode()
48 {
49 auto host = GetHost();
50 CHECK_NULL_VOID(host);
51 host->GetRenderContext()->SetClipToFrame(true);
52 // expand to navigation bar by default
53 host->GetLayoutProperty()->UpdateSafeAreaExpandOpts(
54 { .type = SAFE_AREA_TYPE_SYSTEM, .edges = SAFE_AREA_EDGE_BOTTOM });
55 }
56
SetOnChangeEvent(std::function<void (const BaseEventInfo *)> && event)57 void TabsPattern::SetOnChangeEvent(std::function<void(const BaseEventInfo*)>&& event)
58 {
59 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
60 CHECK_NULL_VOID(tabsNode);
61 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
62 CHECK_NULL_VOID(swiperNode);
63
64 ChangeEventWithPreIndex changeEvent([weak = WeakClaim(this), jsEvent = std::move(event)](
65 int32_t preIndex, int32_t currentIndex) {
66 auto pattern = weak.Upgrade();
67 CHECK_NULL_VOID(pattern);
68 auto tabsNode = AceType::DynamicCast<TabsNode>(pattern->GetHost());
69 CHECK_NULL_VOID(tabsNode);
70 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
71 CHECK_NULL_VOID(tabBarNode);
72 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
73 CHECK_NULL_VOID(tabBarPattern);
74 if (tabBarPattern->IsMaskAnimationExecuted()) {
75 return;
76 }
77 auto tabsLayoutProperty = tabsNode->GetLayoutProperty<TabsLayoutProperty>();
78 CHECK_NULL_VOID(tabsLayoutProperty);
79 tabsLayoutProperty->UpdateIndex(currentIndex);
80 tabBarPattern->OnTabBarIndexChange(currentIndex);
81 pattern->FireTabContentStateCallback(preIndex, currentIndex);
82
83 /* js callback */
84 if (jsEvent && tabsNode->IsOnMainTree()) {
85 pattern->RecordChangeEvent(currentIndex);
86 auto context = tabsNode->GetContext();
87 CHECK_NULL_VOID(context);
88 TAG_LOGI(
89 AceLogTag::ACE_TABS, "onChange preIndex:%{public}d, currentIndex:%{public}d", preIndex, currentIndex);
90 context->AddAfterLayoutTask(
91 [currentIndex, jsEvent]() {
92 TabContentChangeEvent eventInfo(currentIndex);
93 jsEvent(&eventInfo);
94 }, true);
95 }
96 });
97
98 if (onChangeEvent_) {
99 (*onChangeEvent_).swap(changeEvent);
100 } else {
101 onChangeEvent_ = std::make_shared<ChangeEventWithPreIndex>(changeEvent);
102 auto eventHub = swiperNode->GetEventHub<SwiperEventHub>();
103 CHECK_NULL_VOID(eventHub);
104 eventHub->AddOnChangeEventWithPreIndex(onChangeEvent_);
105 }
106 }
107
FireTabContentStateCallback(int32_t oldIndex,int32_t nextIndex) const108 void TabsPattern::FireTabContentStateCallback(int32_t oldIndex, int32_t nextIndex) const
109 {
110 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
111 CHECK_NULL_VOID(tabsNode);
112 std::string id = tabsNode->GetInspectorId().value_or("");
113 int32_t uniqueId = tabsNode->GetId();
114 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
115 CHECK_NULL_VOID(swiperNode);
116
117 auto oldTabContent = AceType::DynamicCast<TabContentNode>(swiperNode->GetChildByIndex(oldIndex));
118 if (oldTabContent) {
119 std::string oldTabContentId = oldTabContent->GetInspectorId().value_or("");
120 int32_t oldTabContentUniqueId = oldTabContent->GetId();
121 TabContentInfo oldTabContentInfo(oldTabContentId, oldTabContentUniqueId, TabContentState::ON_HIDE, oldIndex,
122 id, uniqueId);
123 UIObserverHandler::GetInstance().NotifyTabContentStateUpdate(oldTabContentInfo);
124 }
125
126 auto nextTabContent = AceType::DynamicCast<TabContentNode>(swiperNode->GetChildByIndex(nextIndex));
127 if (nextTabContent) {
128 std::string nextTabContentId = nextTabContent->GetInspectorId().value_or("");
129 int32_t nextTabContentUniqueId = nextTabContent->GetId();
130 TabContentInfo nextTabContentInfo(nextTabContentId, nextTabContentUniqueId, TabContentState::ON_SHOW, nextIndex,
131 id, uniqueId);
132 UIObserverHandler::GetInstance().NotifyTabContentStateUpdate(nextTabContentInfo);
133 }
134 }
135
RecordChangeEvent(int32_t index)136 void TabsPattern::RecordChangeEvent(int32_t index)
137 {
138 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
139 CHECK_NULL_VOID(tabsNode);
140 if (Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
141 auto inspectorId = tabsNode->GetInspectorId().value_or("");
142 auto tabBarText = GetTabBarTextByIndex(index);
143 Recorder::EventParamsBuilder builder;
144 builder.SetId(inspectorId)
145 .SetType(tabsNode->GetTag())
146 .SetIndex(index)
147 .SetText(tabBarText)
148 .SetHost(tabsNode)
149 .SetDescription(tabsNode->GetAutoEventParamValue(""));
150 Recorder::EventRecorder::Get().OnChange(std::move(builder));
151 if (!inspectorId.empty()) {
152 Recorder::NodeDataCache::Get().PutMultiple(tabsNode, inspectorId, tabBarText, index);
153 }
154 }
155 }
156
GetTabBarTextByIndex(int32_t index) const157 std::string TabsPattern::GetTabBarTextByIndex(int32_t index) const
158 {
159 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
160 CHECK_NULL_RETURN(tabsNode, "");
161 auto tabBar = tabsNode->GetTabBar();
162 CHECK_NULL_RETURN(tabBar, "");
163 auto tabBarItem = tabBar->GetChildAtIndex(index);
164 CHECK_NULL_RETURN(tabBarItem, "");
165 auto node = AceType::DynamicCast<FrameNode>(tabBarItem);
166 CHECK_NULL_RETURN(node, "");
167 return node->GetAccessibilityProperty<NG::AccessibilityProperty>()->GetGroupText(true);
168 }
169
SetOnTabBarClickEvent(std::function<void (const BaseEventInfo *)> && event)170 void TabsPattern::SetOnTabBarClickEvent(std::function<void(const BaseEventInfo*)>&& event)
171 {
172 ChangeEvent tabBarClickEvent([jsEvent = std::move(event)](int32_t index) {
173 /* js callback */
174 if (jsEvent) {
175 TabContentChangeEvent eventInfo(index);
176 jsEvent(&eventInfo);
177 }
178 });
179
180 if (onTabBarClickEvent_) {
181 (*onTabBarClickEvent_).swap(tabBarClickEvent);
182 } else {
183 onTabBarClickEvent_ = std::make_shared<ChangeEvent>(tabBarClickEvent);
184 }
185 }
186
SetAnimationStartEvent(AnimationStartEvent && event)187 void TabsPattern::SetAnimationStartEvent(AnimationStartEvent&& event)
188 {
189 if (animationStartEvent_) {
190 (*animationStartEvent_).swap(event);
191 } else {
192 auto host = GetHost();
193 CHECK_NULL_VOID(host);
194 auto tabsNode = AceType::DynamicCast<TabsNode>(host);
195 CHECK_NULL_VOID(tabsNode);
196 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
197 CHECK_NULL_VOID(swiperNode);
198 auto eventHub = swiperNode->GetEventHub<SwiperEventHub>();
199 CHECK_NULL_VOID(eventHub);
200 animationStartEvent_ = std::make_shared<AnimationStartEvent>(std::move(event));
201 eventHub->AddAnimationStartEvent(animationStartEvent_);
202 }
203 }
204
SetAnimationEndEvent(AnimationEndEvent && event)205 void TabsPattern::SetAnimationEndEvent(AnimationEndEvent&& event)
206 {
207 if (animationEndEvent_) {
208 (*animationEndEvent_).swap(event);
209 } else {
210 auto host = GetHost();
211 CHECK_NULL_VOID(host);
212 auto tabsNode = AceType::DynamicCast<TabsNode>(host);
213 CHECK_NULL_VOID(tabsNode);
214 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
215 CHECK_NULL_VOID(swiperNode);
216 auto eventHub = swiperNode->GetEventHub<SwiperEventHub>();
217 CHECK_NULL_VOID(eventHub);
218 animationEndEvent_ = std::make_shared<AnimationEndEvent>(std::move(event));
219 eventHub->AddAnimationEndEvent(animationEndEvent_);
220 }
221 }
222
SetOnSelectedEvent(std::function<void (const BaseEventInfo *)> && event)223 void TabsPattern::SetOnSelectedEvent(std::function<void(const BaseEventInfo*)>&& event)
224 {
225 ChangeEvent selectedEvent([jsEvent = std::move(event)](int32_t index) {
226 /* js callback */
227 if (jsEvent) {
228 TabContentChangeEvent eventInfo(index);
229 jsEvent(&eventInfo);
230 }
231 });
232 if (selectedEvent_) {
233 (*selectedEvent_).swap(selectedEvent);
234 } else {
235 auto host = GetHost();
236 CHECK_NULL_VOID(host);
237 auto tabsNode = AceType::DynamicCast<TabsNode>(host);
238 CHECK_NULL_VOID(tabsNode);
239 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
240 CHECK_NULL_VOID(swiperNode);
241 auto eventHub = swiperNode->GetEventHub<SwiperEventHub>();
242 CHECK_NULL_VOID(eventHub);
243 selectedEvent_ = std::make_shared<ChangeEvent>(std::move(selectedEvent));
244 eventHub->AddOnSlectedEvent(selectedEvent_);
245 }
246 }
247
OnUpdateShowDivider()248 void TabsPattern::OnUpdateShowDivider()
249 {
250 auto host = AceType::DynamicCast<TabsNode>(GetHost());
251 CHECK_NULL_VOID(host);
252 auto layoutProperty = host->GetLayoutProperty<TabsLayoutProperty>();
253 TabsItemDivider defaultDivider;
254 auto divider = layoutProperty->GetDivider().value_or(defaultDivider);
255 auto children = host->GetChildren();
256 if (children.size() < CHILDREN_MIN_SIZE) {
257 return;
258 }
259
260 auto dividerFrameNode = AceType::DynamicCast<FrameNode>(host->GetDivider());
261 CHECK_NULL_VOID(dividerFrameNode);
262 auto dividerRenderProperty = dividerFrameNode->GetPaintProperty<DividerRenderProperty>();
263 CHECK_NULL_VOID(dividerRenderProperty);
264 dividerRenderProperty->UpdateDividerColor(divider.color);
265
266 auto dividerLayoutProperty = dividerFrameNode->GetLayoutProperty<DividerLayoutProperty>();
267 CHECK_NULL_VOID(dividerLayoutProperty);
268 dividerLayoutProperty->UpdateStrokeWidth(divider.strokeWidth);
269 dividerFrameNode->MarkModifyDone();
270 }
271
UpdateSwiperDisableSwipe(bool disableSwipe)272 void TabsPattern::UpdateSwiperDisableSwipe(bool disableSwipe)
273 {
274 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
275 CHECK_NULL_VOID(tabsNode);
276 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
277 CHECK_NULL_VOID(swiperNode);
278 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
279 CHECK_NULL_VOID(swiperPattern);
280 auto props = swiperNode->GetLayoutProperty<SwiperLayoutProperty>();
281 CHECK_NULL_VOID(props);
282 props->UpdateDisableSwipe(disableSwipe);
283 swiperPattern->UpdateSwiperPanEvent(disableSwipe);
284 swiperPattern->SetSwiperEventCallback(disableSwipe);
285 }
286
SetSwiperPaddingAndBorder()287 void TabsPattern::SetSwiperPaddingAndBorder()
288 {
289 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
290 CHECK_NULL_VOID(tabsNode);
291 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
292 CHECK_NULL_VOID(swiperNode);
293 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
294 CHECK_NULL_VOID(swiperPattern);
295 auto layoutProperty = tabsNode->GetLayoutProperty<TabsLayoutProperty>();
296 CHECK_NULL_VOID(layoutProperty);
297 swiperPattern->SetTabsPaddingAndBorder(layoutProperty->CreatePaddingAndBorder());
298 }
299
OnModifyDone()300 void TabsPattern::OnModifyDone()
301 {
302 Pattern::OnModifyDone();
303 UpdateSwiperDisableSwipe(isCustomAnimation_ ? true : isDisableSwipe_);
304 SetSwiperPaddingAndBorder();
305 InitFocusEvent();
306 InitAccessibilityZIndex();
307
308 if (onChangeEvent_) {
309 return;
310 }
311 SetOnChangeEvent(nullptr);
312 OnUpdateShowDivider();
313 }
314
OnAfterModifyDone()315 void TabsPattern::OnAfterModifyDone()
316 {
317 auto host = GetHost();
318 CHECK_NULL_VOID(host);
319 auto inspectorId = host->GetInspectorId().value_or("");
320 if (inspectorId.empty()) {
321 return;
322 }
323 auto property = GetLayoutProperty<TabsLayoutProperty>();
324 CHECK_NULL_VOID(property);
325 auto index = property->GetIndexValue(0);
326 auto tabBarText = GetTabBarTextByIndex(index);
327 Recorder::NodeDataCache::Get().PutMultiple(host, inspectorId, tabBarText, index);
328 }
329
SetOnIndexChangeEvent(std::function<void (const BaseEventInfo *)> && event)330 void TabsPattern::SetOnIndexChangeEvent(std::function<void(const BaseEventInfo*)>&& event)
331 {
332 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
333 CHECK_NULL_VOID(tabsNode);
334 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
335 CHECK_NULL_VOID(swiperNode);
336
337 ChangeEvent changeEvent([weak = WeakClaim(this), jsEvent = std::move(event)](int32_t index) {
338 auto pattern = weak.Upgrade();
339 CHECK_NULL_VOID(pattern);
340 auto tabsNode = AceType::DynamicCast<TabsNode>(pattern->GetHost());
341 CHECK_NULL_VOID(tabsNode);
342 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
343 CHECK_NULL_VOID(tabBarNode);
344 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
345 CHECK_NULL_VOID(tabBarPattern);
346 if (tabBarPattern->IsMaskAnimationExecuted()) {
347 return;
348 }
349
350 /* js callback */
351 if (jsEvent) {
352 TabContentChangeEvent eventInfo(index);
353 jsEvent(&eventInfo);
354 }
355 });
356
357 if (onIndexChangeEvent_) {
358 (*onIndexChangeEvent_).swap(changeEvent);
359 } else {
360 onIndexChangeEvent_ = std::make_shared<ChangeEvent>(changeEvent);
361 auto eventHub = swiperNode->GetEventHub<SwiperEventHub>();
362 CHECK_NULL_VOID(eventHub);
363 eventHub->AddOnChangeEvent(onIndexChangeEvent_);
364 }
365 }
366
ProvideRestoreInfo()367 std::string TabsPattern::ProvideRestoreInfo()
368 {
369 auto jsonObj = JsonUtil::Create(true);
370 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
371 CHECK_NULL_RETURN(tabsNode, "");
372 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
373 CHECK_NULL_RETURN(tabBarNode, "");
374 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
375 CHECK_NULL_RETURN(tabBarPattern, "");
376 return tabBarPattern->ProvideRestoreInfo();
377 }
378
OnRestoreInfo(const std::string & restoreInfo)379 void TabsPattern::OnRestoreInfo(const std::string& restoreInfo)
380 {
381 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
382 CHECK_NULL_VOID(tabsNode);
383 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
384 CHECK_NULL_VOID(tabBarNode);
385 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
386 CHECK_NULL_VOID(tabBarPattern);
387 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
388 CHECK_NULL_VOID(swiperNode);
389 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
390 CHECK_NULL_VOID(swiperPattern);
391 auto swiperLayoutProperty = swiperNode->GetLayoutProperty<SwiperLayoutProperty>();
392 CHECK_NULL_VOID(swiperLayoutProperty);
393 auto info = JsonUtil::ParseJsonString(restoreInfo);
394 if (!info->IsValid() || !info->IsObject()) {
395 return;
396 }
397 auto jsonIsOn = info->GetValue("Index");
398 swiperLayoutProperty->UpdateIndex(jsonIsOn->GetInt());
399
400 swiperPattern->OnRestoreInfo(restoreInfo);
401 tabBarPattern->OnRestoreInfo(restoreInfo);
402 }
403
AddInnerOnGestureRecognizerJudgeBegin(GestureRecognizerJudgeFunc && gestureRecognizerJudgeFunc)404 void TabsPattern::AddInnerOnGestureRecognizerJudgeBegin(GestureRecognizerJudgeFunc&& gestureRecognizerJudgeFunc)
405 {
406 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
407 CHECK_NULL_VOID(tabsNode);
408 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
409 CHECK_NULL_VOID(swiperNode);
410 auto targetComponent = swiperNode->GetTargetComponent().Upgrade();
411 CHECK_NULL_VOID(targetComponent);
412 targetComponent->SetOnGestureRecognizerJudgeBegin(std::move(gestureRecognizerJudgeFunc));
413 targetComponent->SetInnerNodeGestureRecognizerJudge(true);
414 }
415
RecoverInnerOnGestureRecognizerJudgeBegin()416 void TabsPattern::RecoverInnerOnGestureRecognizerJudgeBegin()
417 {
418 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
419 CHECK_NULL_VOID(tabsNode);
420 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
421 CHECK_NULL_VOID(swiperNode);
422 auto targetComponent = swiperNode->GetTargetComponent().Upgrade();
423 CHECK_NULL_VOID(targetComponent);
424 targetComponent->SetOnGestureRecognizerJudgeBegin(nullptr);
425 targetComponent->SetInnerNodeGestureRecognizerJudge(false);
426 }
427
GetScopeFocusAlgorithm()428 ScopeFocusAlgorithm TabsPattern::GetScopeFocusAlgorithm()
429 {
430 auto property = GetLayoutProperty<TabsLayoutProperty>();
431 CHECK_NULL_RETURN(property, {});
432 bool isVertical = true;
433 if (property->GetAxis().has_value()) {
434 isVertical = property->GetAxis().value() == Axis::HORIZONTAL;
435 }
436 return ScopeFocusAlgorithm(isVertical, true, ScopeType::OTHERS,
437 [wp = WeakClaim(this)](
438 FocusStep step, const WeakPtr<FocusHub>& currFocusNode, WeakPtr<FocusHub>& nextFocusNode) -> bool {
439 auto tabs = wp.Upgrade();
440 if (tabs) {
441 nextFocusNode = tabs->GetNextFocusNode(step, currFocusNode);
442 }
443 return nextFocusNode.Upgrade() != currFocusNode.Upgrade();
444 });
445 }
446
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)447 WeakPtr<FocusHub> TabsPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
448 {
449 auto curFocusNode = currentFocusNode.Upgrade();
450 CHECK_NULL_RETURN(curFocusNode, nullptr);
451
452 auto property = GetLayoutProperty<TabsLayoutProperty>();
453 CHECK_NULL_RETURN(property, nullptr);
454 auto axis = property->GetAxis().value_or(Axis::HORIZONTAL);
455 auto tabBarPosition = property->GetTabBarPosition().value_or(BarPosition::START);
456 auto isRTL = property->GetNonAutoLayoutDirection() == TextDirection::RTL;
457
458 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
459 CHECK_NULL_RETURN(tabsNode, nullptr);
460 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
461 CHECK_NULL_RETURN(tabBarNode, nullptr);
462 auto tabBarFocusNode = tabBarNode->GetFocusHub();
463 CHECK_NULL_RETURN(tabBarFocusNode, nullptr);
464 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
465 CHECK_NULL_RETURN(swiperNode, nullptr);
466 auto swiperFocusNode = swiperNode->GetFocusHub();
467 CHECK_NULL_RETURN(swiperFocusNode, nullptr);
468
469 if (curFocusNode->GetFrameName() == V2::TAB_BAR_ETS_TAG) {
470 if (tabBarPosition == BarPosition::START) {
471 if (step == FocusStep::TAB || (axis == Axis::HORIZONTAL && step == FocusStep::DOWN) ||
472 (axis == Axis::VERTICAL && (isRTL ? step == FocusStep::LEFT : step == FocusStep::RIGHT))) {
473 return AceType::WeakClaim(AceType::RawPtr(swiperFocusNode));
474 }
475 } else {
476 if (step == FocusStep::SHIFT_TAB || (axis == Axis::HORIZONTAL && step == FocusStep::UP) ||
477 (axis == Axis::VERTICAL && (isRTL ? step == FocusStep::RIGHT : step == FocusStep::LEFT))) {
478 return AceType::WeakClaim(AceType::RawPtr(swiperFocusNode));
479 }
480 }
481 } else if (curFocusNode->GetFrameName() == V2::SWIPER_ETS_TAG) {
482 if (tabBarPosition == BarPosition::START) {
483 if (step == FocusStep::SHIFT_TAB || (axis == Axis::HORIZONTAL && step == FocusStep::UP) ||
484 (axis == Axis::VERTICAL && (isRTL ? step == FocusStep::RIGHT : step == FocusStep::LEFT))) {
485 return AceType::WeakClaim(AceType::RawPtr(tabBarFocusNode));
486 }
487 } else {
488 if (step == FocusStep::TAB || (axis == Axis::HORIZONTAL && step == FocusStep::DOWN) ||
489 (axis == Axis::VERTICAL && (isRTL ? step == FocusStep::LEFT : step == FocusStep::RIGHT))) {
490 return AceType::WeakClaim(AceType::RawPtr(tabBarFocusNode));
491 }
492 }
493 if (step == FocusStep::LEFT_END || step == FocusStep::RIGHT_END || step == FocusStep::UP_END ||
494 step == FocusStep::DOWN_END) {
495 return AceType::WeakClaim(AceType::RawPtr(swiperFocusNode));
496 }
497 }
498 return nullptr;
499 }
500
InitFocusEvent()501 void TabsPattern::InitFocusEvent()
502 {
503 auto host = GetHost();
504 CHECK_NULL_VOID(host);
505 auto focusHub = host->GetFocusHub();
506 CHECK_NULL_VOID(focusHub);
507
508 auto getNextFocusNodeFunc = [weak = WeakClaim(this)](
509 FocusReason reason, FocusIntension intension) -> RefPtr<FocusHub> {
510 if (reason != FocusReason::FOCUS_TRAVEL) {
511 return nullptr;
512 }
513 auto pattern = weak.Upgrade();
514 CHECK_NULL_RETURN(pattern, nullptr);
515 return pattern->GetCurrentFocusNode(intension);
516 };
517 focusHub->SetOnGetNextFocusNodeFunc(getNextFocusNodeFunc);
518 }
519
GetCurrentFocusNode(FocusIntension intension)520 RefPtr<FocusHub> TabsPattern::GetCurrentFocusNode(FocusIntension intension)
521 {
522 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
523 CHECK_NULL_RETURN(tabsNode, nullptr);
524 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
525 CHECK_NULL_RETURN(tabBarNode, nullptr);
526 auto tabBarFocusHub = tabBarNode->GetFocusHub();
527 CHECK_NULL_RETURN(tabBarFocusHub, nullptr);
528 if (!tabBarFocusHub->GetFocusable()) {
529 return nullptr;
530 }
531 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
532 CHECK_NULL_RETURN(swiperNode, nullptr);
533 auto swiperFocusHub = swiperNode->GetFocusHub();
534 CHECK_NULL_RETURN(swiperFocusHub, nullptr);
535
536 auto property = GetLayoutProperty<TabsLayoutProperty>();
537 CHECK_NULL_RETURN(property, nullptr);
538 auto axis = property->GetAxis().value_or(Axis::HORIZONTAL);
539 auto barPosition = property->GetTabBarPosition().value_or(BarPosition::START);
540 auto isRTL = property->GetNonAutoLayoutDirection() == TextDirection::RTL;
541
542 auto focusFirstNodeIntension = intension == FocusIntension::TAB || intension == FocusIntension::SELECT ||
543 intension == FocusIntension::HOME;
544 auto focusLastNodeIntension = intension == FocusIntension::SHIFT_TAB || intension == FocusIntension::END;
545 auto firstFocusHub = barPosition == BarPosition::START ? tabBarFocusHub : swiperFocusHub;
546 auto lastFocusHub = barPosition == BarPosition::START ? swiperFocusHub : tabBarFocusHub;
547 if (focusFirstNodeIntension) {
548 return firstFocusHub;
549 } else if (focusLastNodeIntension) {
550 return lastFocusHub;
551 } else if (axis == Axis::HORIZONTAL) {
552 if (intension == FocusIntension::DOWN || intension == FocusIntension::LEFT ||
553 intension == FocusIntension::RIGHT) {
554 return firstFocusHub;
555 } else if (intension == FocusIntension::UP) {
556 return lastFocusHub;
557 }
558 } else {
559 if (intension == FocusIntension::DOWN || intension == FocusIntension::UP) {
560 return firstFocusHub;
561 } else if (intension == FocusIntension::LEFT) {
562 return (isRTL ? barPosition == BarPosition::END : barPosition == BarPosition::START) ? swiperFocusHub
563 : tabBarFocusHub;
564 } else if (intension == FocusIntension::RIGHT) {
565 return (isRTL ? barPosition == BarPosition::END : barPosition == BarPosition::START) ? tabBarFocusHub
566 : swiperFocusHub;
567 }
568 }
569 return nullptr;
570 }
571
InitAccessibilityZIndex()572 void TabsPattern::InitAccessibilityZIndex()
573 {
574 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
575 CHECK_NULL_VOID(tabsNode);
576 auto tabsLayoutProperty = GetLayoutProperty<TabsLayoutProperty>();
577 CHECK_NULL_VOID(tabsLayoutProperty);
578 BarPosition barPosition = tabsLayoutProperty->GetTabBarPositionValue(BarPosition::START);
579 if (barPosition != barPosition_) {
580 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
581 CHECK_NULL_VOID(swiperNode);
582 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
583 CHECK_NULL_VOID(tabBarNode);
584 auto swiperAccessibilityProperty = swiperNode->GetAccessibilityProperty<NG::AccessibilityProperty>();
585 CHECK_NULL_VOID(swiperAccessibilityProperty);
586 auto tabBarAccessibilityProperty = tabBarNode->GetAccessibilityProperty<NG::AccessibilityProperty>();
587 CHECK_NULL_VOID(tabBarAccessibilityProperty);
588 if (barPosition == BarPosition::START) {
589 swiperAccessibilityProperty->SetAccessibilityZIndex(1);
590 tabBarAccessibilityProperty->SetAccessibilityZIndex(0);
591 } else {
592 swiperAccessibilityProperty->SetAccessibilityZIndex(0);
593 tabBarAccessibilityProperty->SetAccessibilityZIndex(1);
594 }
595 barPosition_ = barPosition;
596 }
597 }
598
BeforeCreateLayoutWrapper()599 void TabsPattern::BeforeCreateLayoutWrapper()
600 {
601 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
602 CHECK_NULL_VOID(tabsNode);
603 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
604 CHECK_NULL_VOID(tabBarNode);
605 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
606 CHECK_NULL_VOID(swiperNode);
607 auto tabsLayoutProperty = GetLayoutProperty<TabsLayoutProperty>();
608 CHECK_NULL_VOID(tabsLayoutProperty);
609 UpdateIndex(tabsNode, tabBarNode, swiperNode, tabsLayoutProperty);
610
611 if (isInit_) {
612 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
613 CHECK_NULL_VOID(swiperPattern);
614 swiperPattern->SetOnHiddenChangeForParent();
615 auto parent = tabsNode->GetAncestorNodeOfFrame(false);
616 CHECK_NULL_VOID(parent);
617 while (parent && parent->GetTag() != V2::NAVDESTINATION_VIEW_ETS_TAG) {
618 parent = parent->GetAncestorNodeOfFrame(false);
619 }
620 if (!parent) {
621 auto willShowIndex = tabsLayoutProperty->GetIndex().value_or(0);
622 swiperPattern->FireSelectedEvent(-1, willShowIndex);
623 swiperPattern->FireWillShowEvent(willShowIndex);
624 }
625 isInit_ = false;
626 }
627
628 auto childrenUpdated = swiperNode->GetChildrenUpdated() != -1;
629 if (childrenUpdated) {
630 HandleChildrenUpdated(swiperNode, tabBarNode);
631
632 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
633 CHECK_NULL_VOID(tabBarPattern);
634 auto index = tabsLayoutProperty->GetIndexValue(0);
635 auto tabContentNum = swiperNode->TotalChildCount();
636 if (index >= tabContentNum) {
637 index = 0;
638 }
639 UpdateSelectedState(swiperNode, tabBarPattern, tabsLayoutProperty, index);
640 }
641 }
642
UpdateIndex(const RefPtr<FrameNode> & tabsNode,const RefPtr<FrameNode> & tabBarNode,const RefPtr<FrameNode> & swiperNode,const RefPtr<TabsLayoutProperty> & tabsLayoutProperty)643 void TabsPattern::UpdateIndex(const RefPtr<FrameNode>& tabsNode, const RefPtr<FrameNode>& tabBarNode,
644 const RefPtr<FrameNode>& swiperNode, const RefPtr<TabsLayoutProperty>& tabsLayoutProperty)
645 {
646 if (!tabsLayoutProperty->GetIndexSetByUser().has_value()) {
647 return;
648 }
649 auto tabsPattern = tabsNode->GetPattern<TabsPattern>();
650 CHECK_NULL_VOID(tabsPattern);
651 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
652 CHECK_NULL_VOID(tabBarPattern);
653 auto indexSetByUser = tabsLayoutProperty->GetIndexSetByUser().value();
654 auto index = indexSetByUser;
655 tabsLayoutProperty->ResetIndexSetByUser();
656 auto tabContentNum = swiperNode->TotalChildCount();
657 if (index >= tabContentNum) {
658 index = 0;
659 }
660 SetLastWeakFocusNode(tabsNode, tabBarNode, tabsLayoutProperty, index);
661 if (!tabsLayoutProperty->GetIndex().has_value()) {
662 UpdateSelectedState(swiperNode, tabBarPattern, tabsLayoutProperty, index);
663 tabsLayoutProperty->UpdateIndex(indexSetByUser);
664 } else {
665 auto preIndex = tabsLayoutProperty->GetIndex().value();
666 if (preIndex == index || index < 0) {
667 return;
668 }
669 if (tabsPattern->GetInterceptStatus()) {
670 auto ret = tabsPattern->OnContentWillChange(preIndex, index);
671 if (ret.has_value() && !ret.value()) {
672 return;
673 }
674 }
675 AceAsyncTraceBeginCommercial(0, APP_TABS_NO_ANIMATION_SWITCH);
676 tabBarPattern->SetMaskAnimationByCreate(true);
677 UpdateSelectedState(swiperNode, tabBarPattern, tabsLayoutProperty, index);
678 }
679 }
680
SetLastWeakFocusNode(const RefPtr<FrameNode> & tabsNode,const RefPtr<FrameNode> & tabBarNode,const RefPtr<TabsLayoutProperty> & tabsLayoutProperty,int32_t index)681 void TabsPattern::SetLastWeakFocusNode(const RefPtr<FrameNode>& tabsNode, const RefPtr<FrameNode>& tabBarNode,
682 const RefPtr<TabsLayoutProperty>& tabsLayoutProperty, int32_t index)
683 {
684 auto tabsFocusNode = tabsNode->GetFocusHub();
685 CHECK_NULL_VOID(tabsFocusNode);
686 auto tabBarFocusNode = tabBarNode->GetFocusHub();
687 CHECK_NULL_VOID(tabBarFocusNode);
688 if (!tabsFocusNode->IsCurrentFocus() && !tabsFocusNode->GetLastWeakFocusNode().Upgrade()) {
689 auto tabBarPosition = tabsLayoutProperty->GetTabBarPosition().value_or(BarPosition::START);
690 if (tabBarPosition == BarPosition::START) {
691 tabsFocusNode->SetLastWeakFocusNode(AceType::WeakClaim(AceType::RawPtr(tabBarFocusNode)));
692 }
693 if (!tabBarFocusNode->IsCurrentFocus() && !tabBarFocusNode->GetLastWeakFocusNode().Upgrade()) {
694 auto childNode = AceType::DynamicCast<FrameNode>(tabBarNode->GetChildAtIndex(index));
695 CHECK_NULL_VOID(childNode);
696 auto childFocusNode = childNode->GetFocusHub();
697 CHECK_NULL_VOID(childFocusNode);
698 childFocusNode->SetFocusDependence(FocusDependence::SELF);
699 tabBarFocusNode->SetLastWeakFocusNode(AceType::WeakClaim(AceType::RawPtr(childFocusNode)));
700 }
701 }
702 }
703
SetAnimateMode(TabAnimateMode mode)704 void TabsPattern::SetAnimateMode(TabAnimateMode mode)
705 {
706 animateMode_ = mode;
707 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
708 CHECK_NULL_VOID(tabsNode);
709 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
710 CHECK_NULL_VOID(swiperNode);
711 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
712 CHECK_NULL_VOID(swiperPattern);
713 swiperPattern->SetJumpAnimationMode(mode);
714 }
715
716 /**
717 * @brief Handles the update of children in the TabsPattern component.
718 *
719 * This function is responsible for updating the children of the TabsPattern component,
720 * specifically the swiperNode and tabBarNode. It performs the following steps:
721 * 1. Creates a map of tabBarItems using the tabBarItemNodes from the tabBarNode.
722 * 2. Traverses the tree of UINodes starting from the swiperNode using a stack.
723 * 3. For each UINode, if it is an instance of TabContentNode, it retrieves the corresponding
724 * tabBarItem from the tabBarItems map and moves it to position 0.
725 * 4. Continues traversing the tree by pushing the children of the current UINode onto the stack.
726 *
727 * @param swiperNode The FrameNode representing the swiper component.
728 * @param tabBarNode The FrameNode representing the tab bar component.
729 */
HandleChildrenUpdated(const RefPtr<FrameNode> & swiperNode,const RefPtr<FrameNode> & tabBarNode)730 void TabsPattern::HandleChildrenUpdated(const RefPtr<FrameNode>& swiperNode, const RefPtr<FrameNode>& tabBarNode)
731 {
732 std::map<int32_t, RefPtr<FrameNode>> tabBarItems;
733 for (const auto& tabBarItemNode : tabBarNode->GetChildren()) {
734 CHECK_NULL_VOID(tabBarItemNode);
735 auto tabBarItemFrameNode = AceType::DynamicCast<FrameNode>(tabBarItemNode);
736 tabBarItems[tabBarItemFrameNode->GetId()] = tabBarItemFrameNode;
737 }
738 std::stack<RefPtr<UINode>> stack;
739 stack.push(swiperNode);
740 while (!stack.empty()) {
741 auto parent = stack.top();
742 stack.pop();
743 if (AceType::InstanceOf<TabContentNode>(parent)) {
744 auto tabContentNode = AceType::DynamicCast<TabContentNode>(parent);
745 auto tabBarItem = tabBarItems[tabContentNode->GetTabBarItemId()];
746 CHECK_NULL_VOID(tabBarItem);
747 tabBarItem->MovePosition(0);
748 continue;
749 }
750 for (const auto& child : parent->GetChildren()) {
751 stack.push(child);
752 }
753 }
754
755 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
756 CHECK_NULL_VOID(tabBarPattern);
757 tabBarPattern->AdjustTabBarInfo();
758 }
759
760 /**
761 * @brief Update selected state.
762 *
763 * This function is responsible for updating the indicator, text color, font weight, image color,
764 * and index of the tab bar and swiper nodes when updating children or creating a tab.
765 *
766 * @param swiperNode The node representing the swiper.
767 * @param tabBarPattern The pattern for the tab bar.
768 * @param tabsLayoutProperty The layout property for the tabs.
769 * @param index The index of the tab being created.
770 */
UpdateSelectedState(const RefPtr<FrameNode> & swiperNode,const RefPtr<TabBarPattern> & tabBarPattern,const RefPtr<TabsLayoutProperty> & tabsLayoutProperty,int index)771 void TabsPattern::UpdateSelectedState(const RefPtr<FrameNode>& swiperNode, const RefPtr<TabBarPattern>& tabBarPattern,
772 const RefPtr<TabsLayoutProperty>& tabsLayoutProperty, int index)
773 {
774 if (index < 0) {
775 index = 0;
776 }
777 tabBarPattern->UpdateIndicator(index);
778 tabBarPattern->ResetIndicatorAnimationState();
779 tabBarPattern->UpdateSubTabBoard(index);
780 tabBarPattern->UpdateTextColorAndFontWeight(index);
781 tabBarPattern->AdjustSymbolStats(index);
782 tabBarPattern->UpdateImageColor(index);
783 CHECK_NULL_VOID(swiperNode);
784 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
785 CHECK_NULL_VOID(swiperPattern);
786 if (!swiperPattern->IsInFastAnimation()) {
787 auto swiperLayoutProperty = swiperNode->GetLayoutProperty<SwiperLayoutProperty>();
788 CHECK_NULL_VOID(swiperLayoutProperty);
789 swiperLayoutProperty->UpdateIndex(index);
790 }
791 tabsLayoutProperty->UpdateIndex(index);
792 }
793
SetOnUnselectedEvent(std::function<void (const BaseEventInfo *)> && event)794 void TabsPattern::SetOnUnselectedEvent(std::function<void(const BaseEventInfo*)>&& event)
795 {
796 ChangeEvent unselectedEvent([jsEvent = std::move(event)](int32_t index) {
797 /* js callback */
798 if (jsEvent) {
799 TabContentChangeEvent eventInfo(index);
800 jsEvent(&eventInfo);
801 }
802 });
803 if (unselectedEvent_) {
804 (*unselectedEvent_).swap(unselectedEvent);
805 } else {
806 auto host = GetHost();
807 CHECK_NULL_VOID(host);
808 auto tabsNode = AceType::DynamicCast<TabsNode>(host);
809 CHECK_NULL_VOID(tabsNode);
810 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
811 CHECK_NULL_VOID(swiperNode);
812 auto eventHub = swiperNode->GetEventHub<SwiperEventHub>();
813 CHECK_NULL_VOID(eventHub);
814 unselectedEvent_ = std::make_shared<ChangeEvent>(std::move(unselectedEvent));
815 eventHub->AddOnUnselectedEvent(unselectedEvent_);
816 }
817 }
818 } // namespace OHOS::Ace::NG
819