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