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 #include "core/components_ng/pattern/tabs/tabs_node.h"
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->GetOrCreateEventHub<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->GetOrCreateEventHub<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->GetOrCreateEventHub<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->GetOrCreateEventHub<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->GetOrCreateEventHub<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 if (!tabsLayoutProperty->GetIndex().has_value()) {
661 UpdateSelectedState(swiperNode, tabBarPattern, tabsLayoutProperty, index);
662 tabsLayoutProperty->UpdateIndex(indexSetByUser < 0 ? 0 : indexSetByUser);
663 } else {
664 auto preIndex = tabsLayoutProperty->GetIndex().value();
665 if (preIndex == index || index < 0) {
666 return;
667 }
668 if (tabsPattern->GetInterceptStatus()) {
669 auto ret = tabsPattern->OnContentWillChange(preIndex, index);
670 if (ret.has_value() && !ret.value()) {
671 return;
672 }
673 }
674 AceAsyncTraceBeginCommercial(0, APP_TABS_NO_ANIMATION_SWITCH);
675 tabBarPattern->SetMaskAnimationByCreate(true);
676 UpdateSelectedState(swiperNode, tabBarPattern, tabsLayoutProperty, index);
677 }
678 }
679
SetAnimateMode(TabAnimateMode mode)680 void TabsPattern::SetAnimateMode(TabAnimateMode mode)
681 {
682 animateMode_ = mode;
683 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
684 CHECK_NULL_VOID(tabsNode);
685 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
686 CHECK_NULL_VOID(swiperNode);
687 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
688 CHECK_NULL_VOID(swiperPattern);
689 swiperPattern->SetJumpAnimationMode(mode);
690 }
691
692 /**
693 * @brief Handles the update of children in the TabsPattern component.
694 *
695 * This function is responsible for updating the children of the TabsPattern component,
696 * specifically the swiperNode and tabBarNode. It performs the following steps:
697 * 1. Creates a map of tabBarItems using the tabBarItemNodes from the tabBarNode.
698 * 2. Traverses the tree of UINodes starting from the swiperNode using a stack.
699 * 3. For each UINode, if it is an instance of TabContentNode, it retrieves the corresponding
700 * tabBarItem from the tabBarItems map and moves it to position 0.
701 * 4. Continues traversing the tree by pushing the children of the current UINode onto the stack.
702 *
703 * @param swiperNode The FrameNode representing the swiper component.
704 * @param tabBarNode The FrameNode representing the tab bar component.
705 */
HandleChildrenUpdated(const RefPtr<FrameNode> & swiperNode,const RefPtr<FrameNode> & tabBarNode)706 void TabsPattern::HandleChildrenUpdated(const RefPtr<FrameNode>& swiperNode, const RefPtr<FrameNode>& tabBarNode)
707 {
708 std::map<int32_t, RefPtr<FrameNode>> tabBarItems;
709 for (const auto& tabBarItemNode : tabBarNode->GetChildren()) {
710 CHECK_NULL_VOID(tabBarItemNode);
711 auto tabBarItemFrameNode = AceType::DynamicCast<FrameNode>(tabBarItemNode);
712 tabBarItems[tabBarItemFrameNode->GetId()] = tabBarItemFrameNode;
713 }
714 std::stack<RefPtr<UINode>> stack;
715 stack.push(swiperNode);
716 while (!stack.empty()) {
717 auto parent = stack.top();
718 stack.pop();
719 if (AceType::InstanceOf<TabContentNode>(parent)) {
720 auto tabContentNode = AceType::DynamicCast<TabContentNode>(parent);
721 auto tabBarItem = tabBarItems[tabContentNode->GetTabBarItemId()];
722 CHECK_NULL_VOID(tabBarItem);
723 tabBarItem->MovePosition(0);
724 continue;
725 }
726 for (const auto& child : parent->GetChildren()) {
727 stack.push(child);
728 }
729 }
730
731 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
732 CHECK_NULL_VOID(tabBarPattern);
733 tabBarPattern->AdjustTabBarInfo();
734 }
735
736 /**
737 * @brief Update selected state.
738 *
739 * This function is responsible for updating the indicator, text color, font weight, image color,
740 * and index of the tab bar and swiper nodes when updating children or creating a tab.
741 *
742 * @param swiperNode The node representing the swiper.
743 * @param tabBarPattern The pattern for the tab bar.
744 * @param tabsLayoutProperty The layout property for the tabs.
745 * @param index The index of the tab being created.
746 */
UpdateSelectedState(const RefPtr<FrameNode> & swiperNode,const RefPtr<TabBarPattern> & tabBarPattern,const RefPtr<TabsLayoutProperty> & tabsLayoutProperty,int index)747 void TabsPattern::UpdateSelectedState(const RefPtr<FrameNode>& swiperNode, const RefPtr<TabBarPattern>& tabBarPattern,
748 const RefPtr<TabsLayoutProperty>& tabsLayoutProperty, int index)
749 {
750 if (index < 0) {
751 index = 0;
752 }
753 tabBarPattern->UpdateIndicator(index);
754 tabBarPattern->ResetIndicatorAnimationState();
755 tabBarPattern->UpdateSubTabBoard(index);
756 tabBarPattern->UpdateTextColorAndFontWeight(index);
757 tabBarPattern->AdjustSymbolStats(index);
758 tabBarPattern->UpdateImageColor(index);
759 CHECK_NULL_VOID(swiperNode);
760 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
761 CHECK_NULL_VOID(swiperPattern);
762 if (!swiperPattern->IsInFastAnimation()) {
763 auto swiperLayoutProperty = swiperNode->GetLayoutProperty<SwiperLayoutProperty>();
764 CHECK_NULL_VOID(swiperLayoutProperty);
765 swiperLayoutProperty->UpdateIndex(index);
766 }
767 tabsLayoutProperty->UpdateIndex(index);
768 }
769
SetOnUnselectedEvent(std::function<void (const BaseEventInfo *)> && event)770 void TabsPattern::SetOnUnselectedEvent(std::function<void(const BaseEventInfo*)>&& event)
771 {
772 ChangeEvent unselectedEvent([jsEvent = std::move(event)](int32_t index) {
773 /* js callback */
774 if (jsEvent) {
775 TabContentChangeEvent eventInfo(index);
776 jsEvent(&eventInfo);
777 }
778 });
779 if (unselectedEvent_) {
780 (*unselectedEvent_).swap(unselectedEvent);
781 } else {
782 auto host = GetHost();
783 CHECK_NULL_VOID(host);
784 auto tabsNode = AceType::DynamicCast<TabsNode>(host);
785 CHECK_NULL_VOID(tabsNode);
786 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
787 CHECK_NULL_VOID(swiperNode);
788 auto eventHub = swiperNode->GetOrCreateEventHub<SwiperEventHub>();
789 CHECK_NULL_VOID(eventHub);
790 unselectedEvent_ = std::make_shared<ChangeEvent>(std::move(unselectedEvent));
791 eventHub->AddOnUnselectedEvent(unselectedEvent_);
792 }
793 }
794
OnColorModeChange(uint32_t colorMode)795 void TabsPattern::OnColorModeChange(uint32_t colorMode)
796 {
797 CHECK_NULL_VOID(SystemProperties::ConfigChangePerform());
798 Pattern::OnColorModeChange(colorMode);
799 auto host = GetHost();
800 CHECK_NULL_VOID(host);
801 auto tabsNode = AceType::DynamicCast<TabsNode>(host);
802 CHECK_NULL_VOID(tabsNode);
803 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
804 CHECK_NULL_VOID(tabBarNode);
805 auto pipeline = host->GetContextWithCheck();
806 CHECK_NULL_VOID(pipeline);
807 auto theme = pipeline->GetTheme<TabTheme>();
808 CHECK_NULL_VOID(theme);
809 auto tabsLayoutProperty = tabsNode->GetLayoutProperty<TabsLayoutProperty>();
810 CHECK_NULL_VOID(tabsLayoutProperty);
811
812 if (!tabsLayoutProperty->HasDividerColorSetByUser() ||
813 (tabsLayoutProperty->HasDividerColorSetByUser() && !tabsLayoutProperty->GetDividerColorSetByUserValue())) {
814 auto currentDivider = tabsLayoutProperty->GetDivider().value_or(TabsItemDivider());
815 currentDivider.color = theme->GetDividerColor();
816 auto dividerFrameNode = AceType::DynamicCast<FrameNode>(tabsNode->GetDivider());
817 CHECK_NULL_VOID(dividerFrameNode);
818 auto dividerRenderProperty = dividerFrameNode->GetPaintProperty<DividerRenderProperty>();
819 CHECK_NULL_VOID(dividerRenderProperty);
820 dividerRenderProperty->UpdateDividerColor(currentDivider.color);
821 }
822 tabBarNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
823 }
824 } // namespace OHOS::Ace::NG
825