1 /*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/components_ng/pattern/tabs/tabs_pattern.h"
17 #include "core/components_ng/pattern/swiper/swiper_pattern.h"
18
19 #include "base/geometry/axis.h"
20 #include "base/geometry/dimension.h"
21 #include "base/utils/utils.h"
22 #include "core/components/common/layout/constants.h"
23 #include "core/components/tab_bar/tabs_event.h"
24 #include "core/components_ng/pattern/swiper/swiper_event_hub.h"
25 #include "core/components_ng/pattern/tabs/tab_bar_layout_property.h"
26 #include "core/components_ng/pattern/tabs/tab_bar_paint_property.h"
27 #include "core/components_ng/pattern/tabs/tab_bar_pattern.h"
28 #include "core/components_ng/pattern/tabs/tabs_node.h"
29 #include "core/components_ng/property/property.h"
30 #include "core/components_ng/pattern/divider/divider_layout_property.h"
31 #include "core/components_ng/pattern/divider/divider_render_property.h"
32 #include "core/components_v2/inspector/inspector_constants.h"
33 #include "core/pipeline_ng/pipeline_context.h"
34
35 namespace OHOS::Ace::NG {
36 namespace {
37 constexpr int32_t CHILDREN_MIN_SIZE = 2;
38 } // namespace
39
OnAttachToFrameNode()40 void TabsPattern::OnAttachToFrameNode()
41 {
42 auto host = GetHost();
43 CHECK_NULL_VOID(host);
44 host->GetRenderContext()->SetClipToFrame(true);
45 }
46
SetOnChangeEvent(std::function<void (const BaseEventInfo *)> && event)47 void TabsPattern::SetOnChangeEvent(std::function<void(const BaseEventInfo*)>&& event)
48 {
49 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
50 CHECK_NULL_VOID(tabsNode);
51 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
52 CHECK_NULL_VOID(tabBarNode);
53 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
54 CHECK_NULL_VOID(tabBarPattern);
55 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
56 CHECK_NULL_VOID(swiperNode);
57
58 ChangeEvent changeEvent([tabBarNode, tabBarPattern, jsEvent = std::move(event)](int32_t index) {
59 auto tabsNode = AceType::DynamicCast<TabsNode>(tabBarNode->GetParent());
60 CHECK_NULL_VOID(tabsNode);
61 auto tabsLayoutProperty = tabsNode->GetLayoutProperty<TabsLayoutProperty>();
62 CHECK_NULL_VOID(tabsLayoutProperty);
63 tabsLayoutProperty->UpdateIndex(index);
64 tabBarPattern->SetIsAnimating(false);
65 auto tabBarLayoutProperty = tabBarPattern->GetLayoutProperty<TabBarLayoutProperty>();
66 CHECK_NULL_VOID(tabBarLayoutProperty);
67 if (!tabBarPattern->IsMaskAnimationByCreate()) {
68 tabBarPattern->HandleBottomTabBarChange(index);
69 }
70 tabBarPattern->SetMaskAnimationByCreate(false);
71 if (tabBarLayoutProperty->GetTabBarMode().value_or(TabBarMode::FIXED) == TabBarMode::FIXED) {
72 tabBarPattern->SetIndicator(index);
73 }
74 tabBarPattern->UpdateIndicator(index);
75 tabBarPattern->UpdateTextColor(index);
76 if (tabBarLayoutProperty->GetTabBarMode().value_or(TabBarMode::FIXED) == TabBarMode::SCROLLABLE) {
77 if (tabBarPattern->GetTabBarStyle() == TabBarStyle::SUBTABBATSTYLE &&
78 tabBarLayoutProperty->GetAxisValue(Axis::HORIZONTAL) == Axis::HORIZONTAL) {
79 if (!tabBarPattern->GetChangeByClick()) {
80 tabBarPattern->PlayTabBarTranslateAnimation(index);
81 tabBarNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
82 } else {
83 tabBarPattern->SetChangeByClick(false);
84 }
85 } else {
86 tabBarNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
87 }
88 }
89 /* js callback */
90 if (jsEvent) {
91 TabContentChangeEvent eventInfo(index);
92 jsEvent(&eventInfo);
93 }
94 });
95
96 if (onChangeEvent_) {
97 (*onChangeEvent_).swap(changeEvent);
98 } else {
99 onChangeEvent_ = std::make_shared<ChangeEvent>(changeEvent);
100 auto eventHub = swiperNode->GetEventHub<SwiperEventHub>();
101 CHECK_NULL_VOID(eventHub);
102 eventHub->AddOnChangeEvent(onChangeEvent_);
103 }
104 }
105
SetOnTabBarClickEvent(std::function<void (const BaseEventInfo *)> && event)106 void TabsPattern::SetOnTabBarClickEvent(std::function<void(const BaseEventInfo*)>&& event)
107 {
108 ChangeEvent tabBarClickEvent([jsEvent = std::move(event)](int32_t index) {
109 /* js callback */
110 if (jsEvent) {
111 TabContentChangeEvent eventInfo(index);
112 jsEvent(&eventInfo);
113 }
114 });
115
116 if (onTabBarClickEvent_) {
117 (*onTabBarClickEvent_).swap(tabBarClickEvent);
118 } else {
119 onTabBarClickEvent_ = std::make_shared<ChangeEvent>(tabBarClickEvent);
120 }
121 }
122
OnUpdateShowDivider()123 void TabsPattern::OnUpdateShowDivider()
124 {
125 auto host = AceType::DynamicCast<TabsNode>(GetHost());
126 CHECK_NULL_VOID(host);
127 auto layoutProperty = host->GetLayoutProperty<TabsLayoutProperty>();
128 TabsItemDivider defaultDivider;
129 auto divider = layoutProperty->GetDivider().value_or(defaultDivider);
130 auto children = host->GetChildren();
131 if (children.size() < CHILDREN_MIN_SIZE) {
132 LOGE("OnUpdateShowDivider: children is empty or children's size is less than 2.");
133 return;
134 }
135
136 auto dividerFrameNode = AceType::DynamicCast<FrameNode>(host->GetDivider());
137 CHECK_NULL_VOID(dividerFrameNode);
138 auto dividerRenderProperty = dividerFrameNode->GetPaintProperty<DividerRenderProperty>();
139 CHECK_NULL_VOID(dividerRenderProperty);
140 dividerRenderProperty->UpdateDividerColor(divider.color);
141
142 auto dividerLayoutProperty = dividerFrameNode->GetLayoutProperty<DividerLayoutProperty>();
143 CHECK_NULL_VOID(dividerLayoutProperty);
144 dividerLayoutProperty->UpdateStrokeWidth(divider.strokeWidth);
145 dividerFrameNode->MarkModifyDone();
146 }
147
OnModifyDone()148 void TabsPattern::OnModifyDone()
149 {
150 Pattern::OnModifyDone();
151 if (onChangeEvent_) {
152 return;
153 }
154 SetOnChangeEvent(nullptr);
155 OnUpdateShowDivider();
156 }
157
SetOnIndexChangeEvent(std::function<void (const BaseEventInfo *)> && event)158 void TabsPattern::SetOnIndexChangeEvent(std::function<void(const BaseEventInfo*)>&& event)
159 {
160 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
161 CHECK_NULL_VOID(tabsNode);
162 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
163 CHECK_NULL_VOID(swiperNode);
164
165 ChangeEvent changeEvent([jsEvent = std::move(event)](int32_t index) {
166 /* js callback */
167 if (jsEvent) {
168 TabContentChangeEvent eventInfo(index);
169 jsEvent(&eventInfo);
170 }
171 });
172
173 if (onIndexChangeEvent_) {
174 (*onIndexChangeEvent_).swap(changeEvent);
175 } else {
176 onIndexChangeEvent_ = std::make_shared<ChangeEvent>(changeEvent);
177 auto eventHub = swiperNode->GetEventHub<SwiperEventHub>();
178 CHECK_NULL_VOID(eventHub);
179 eventHub->AddOnChangeEvent(onIndexChangeEvent_);
180 }
181 }
182
ProvideRestoreInfo()183 std::string TabsPattern::ProvideRestoreInfo()
184 {
185 auto jsonObj = JsonUtil::Create(true);
186 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
187 CHECK_NULL_RETURN(tabsNode, "");
188 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
189 CHECK_NULL_RETURN(tabBarNode, "");
190 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
191 CHECK_NULL_RETURN(tabBarPattern, "");
192 return tabBarPattern->ProvideRestoreInfo();
193 }
194
OnRestoreInfo(const std::string & restoreInfo)195 void TabsPattern::OnRestoreInfo(const std::string& restoreInfo)
196 {
197 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
198 CHECK_NULL_VOID(tabsNode);
199 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
200 CHECK_NULL_VOID(tabBarNode);
201 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
202 CHECK_NULL_VOID(tabBarPattern);
203 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
204 CHECK_NULL_VOID(swiperNode);
205 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
206 CHECK_NULL_VOID(swiperPattern);
207 auto swiperLayoutProperty = swiperNode->GetLayoutProperty<SwiperLayoutProperty>();
208 CHECK_NULL_VOID(swiperLayoutProperty);
209 auto info = JsonUtil::ParseJsonString(restoreInfo);
210 if (!info->IsValid() || !info->IsObject()) {
211 LOGW("TabsPattern:: restore info is invalid");
212 return;
213 }
214 auto jsonIsOn = info->GetValue("Index");
215 swiperLayoutProperty->UpdateIndex(jsonIsOn->GetInt());
216
217 swiperPattern->OnRestoreInfo(restoreInfo);
218 tabBarPattern->OnRestoreInfo(restoreInfo);
219 }
220
GetScopeFocusAlgorithm()221 ScopeFocusAlgorithm TabsPattern::GetScopeFocusAlgorithm()
222 {
223 auto property = GetLayoutProperty<TabsLayoutProperty>();
224 CHECK_NULL_RETURN(property, {});
225 bool isVertical = true;
226 if (property->GetAxis().has_value()) {
227 isVertical = property->GetAxis().value() == Axis::HORIZONTAL;
228 }
229 return ScopeFocusAlgorithm(isVertical, true, ScopeType::OTHERS,
230 [wp = WeakClaim(this)](
231 FocusStep step, const WeakPtr<FocusHub>& currFocusNode, WeakPtr<FocusHub>& nextFocusNode) {
232 auto tabs = wp.Upgrade();
233 if (tabs) {
234 nextFocusNode = tabs->GetNextFocusNode(step, currFocusNode);
235 }
236 });
237 }
238
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)239 WeakPtr<FocusHub> TabsPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
240 {
241 auto curFocusNode = currentFocusNode.Upgrade();
242 CHECK_NULL_RETURN(curFocusNode, nullptr);
243
244 auto property = GetLayoutProperty<TabsLayoutProperty>();
245 CHECK_NULL_RETURN(property, nullptr);
246 auto axis = property->GetAxis().value_or(Axis::HORIZONTAL);
247 auto tabBarPosition = property->GetTabBarPosition().value_or(BarPosition::START);
248
249 auto tabsNode = AceType::DynamicCast<TabsNode>(GetHost());
250 CHECK_NULL_RETURN(tabsNode, nullptr);
251 auto tabBarNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabBar());
252 CHECK_NULL_RETURN(tabBarNode, nullptr);
253 auto tabBarFocusNode = tabBarNode->GetFocusHub();
254 CHECK_NULL_RETURN(tabBarFocusNode, nullptr);
255 auto swiperNode = AceType::DynamicCast<FrameNode>(tabsNode->GetTabs());
256 CHECK_NULL_RETURN(swiperNode, nullptr);
257 auto swiperFocusNode = swiperNode->GetFocusHub();
258 CHECK_NULL_RETURN(swiperFocusNode, nullptr);
259
260 auto tabBarPattern = tabBarNode->GetPattern<TabBarPattern>();
261 CHECK_NULL_RETURN(tabBarPattern, nullptr);
262 tabBarPattern->SetFirstFocus(true);
263
264 if (curFocusNode->GetFrameName() == V2::TAB_BAR_ETS_TAG &&
265 ((tabBarPosition == BarPosition::START && axis == Axis::HORIZONTAL && step == FocusStep::DOWN) ||
266 (tabBarPosition == BarPosition::START && axis == Axis::VERTICAL && step == FocusStep::RIGHT) ||
267 (tabBarPosition == BarPosition::END && axis == Axis::HORIZONTAL && step == FocusStep::UP) ||
268 (tabBarPosition == BarPosition::END && axis == Axis::VERTICAL && step == FocusStep::LEFT))) {
269 return AceType::WeakClaim(AceType::RawPtr(swiperFocusNode));
270 }
271 if (curFocusNode->GetFrameName() == V2::SWIPER_ETS_TAG) {
272 if ((tabBarPosition == BarPosition::START && axis == Axis::HORIZONTAL && step == FocusStep::UP) ||
273 (tabBarPosition == BarPosition::START && axis == Axis::VERTICAL && step == FocusStep::LEFT) ||
274 (tabBarPosition == BarPosition::END && axis == Axis::HORIZONTAL && step == FocusStep::DOWN) ||
275 (tabBarPosition == BarPosition::END && axis == Axis::VERTICAL && step == FocusStep::RIGHT)) {
276 return AceType::WeakClaim(AceType::RawPtr(tabBarFocusNode));
277 }
278 if (step == FocusStep::LEFT_END || step == FocusStep::RIGHT_END || step == FocusStep::UP_END ||
279 step == FocusStep::DOWN_END) {
280 return AceType::WeakClaim(AceType::RawPtr(swiperFocusNode));
281 }
282 }
283 return nullptr;
284 }
285 } // namespace OHOS::Ace::NG
286