1 /*
2 * Copyright (c) 2021-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 "frameworks/bridge/declarative_frontend/jsview/js_tabs.h"
17 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
18
19 #include "base/log/ace_scoring_log.h"
20 #include "bridge/declarative_frontend/engine/functions/js_swiper_function.h"
21 #include "bridge/declarative_frontend/engine/functions/js_tabs_function.h"
22 #include "bridge/declarative_frontend/jsview/js_scrollable.h"
23 #include "bridge/declarative_frontend/jsview/js_tabs_controller.h"
24 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
25 #include "bridge/declarative_frontend/jsview/models/tabs_model_impl.h"
26 #include "core/components/common/layout/constants.h"
27 #include "core/components/common/properties/decoration.h"
28 #include "core/components_ng/base/view_stack_model.h"
29 #include "core/components_ng/base/view_stack_processor.h"
30 #include "core/components_ng/pattern/tabs/tab_content_transition_proxy.h"
31 #include "core/components_ng/pattern/tabs/tabs_model_ng.h"
32
33 namespace OHOS::Ace {
34
35 std::unique_ptr<TabsModel> TabsModel::instance_ = nullptr;
36 std::mutex TabsModel::mutex_;
37
GetInstance()38 TabsModel* TabsModel::GetInstance()
39 {
40 if (!instance_) {
41 std::lock_guard<std::mutex> lock(mutex_);
42 if (!instance_) {
43 #ifdef NG_BUILD
44 instance_.reset(new NG::TabsModelNG());
45 #else
46 if (Container::IsCurrentUseNewPipeline()) {
47 instance_.reset(new NG::TabsModelNG());
48 } else {
49 instance_.reset(new Framework::TabsModelImpl());
50 }
51 #endif
52 }
53 }
54 return instance_.get();
55 }
56
57 } // namespace OHOS::Ace
58
59 namespace OHOS::Ace::Framework {
60 namespace {
61 constexpr int32_t SM_COLUMN_NUM = 4;
62 constexpr int32_t MD_COLUMN_NUM = 8;
63 constexpr int32_t LG_COLUMN_NUM = 12;
64 constexpr int32_t DEFAULT_CUSTOM_ANIMATION_TIMEOUT = 1000;
65 const std::vector<BarPosition> BAR_POSITIONS = { BarPosition::START, BarPosition::END };
66
67 const std::vector<BlurStyle> BAR_BLURSTYLE = {
68 BlurStyle::NO_MATERIAL,
69 BlurStyle::THIN,
70 BlurStyle::REGULAR,
71 BlurStyle::THICK,
72 BlurStyle::BACKGROUND_THIN,
73 BlurStyle::BACKGROUND_REGULAR,
74 BlurStyle::BACKGROUND_THICK,
75 BlurStyle::BACKGROUND_ULTRA_THICK,
76 BlurStyle::COMPONENT_ULTRA_THIN,
77 BlurStyle::COMPONENT_THIN,
78 BlurStyle::COMPONENT_REGULAR,
79 BlurStyle::COMPONENT_THICK,
80 BlurStyle::COMPONENT_ULTRA_THICK,
81 };
82
TabContentChangeEventToJSValue(const TabContentChangeEvent & eventInfo)83 JSRef<JSVal> TabContentChangeEventToJSValue(const TabContentChangeEvent& eventInfo)
84 {
85 return JSRef<JSVal>::Make(ToJSValue(eventInfo.GetIndex()));
86 }
87
88 } // namespace
89
SetOnChange(const JSCallbackInfo & info)90 void JSTabs::SetOnChange(const JSCallbackInfo& info)
91 {
92 if (!info[0]->IsFunction()) {
93 return;
94 }
95
96 auto changeHandler = AceType::MakeRefPtr<JsEventFunction<TabContentChangeEvent, 1>>(
97 JSRef<JSFunc>::Cast(info[0]), TabContentChangeEventToJSValue);
98 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
99 auto onChange = [executionContext = info.GetExecutionContext(), func = std::move(changeHandler), node = targetNode](
100 const BaseEventInfo* info) {
101 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext);
102 const auto* tabsInfo = TypeInfoHelper::DynamicCast<TabContentChangeEvent>(info);
103 if (!tabsInfo) {
104 TAG_LOGW(AceLogTag::ACE_TABS, "Tabs onChange callback execute failed.");
105 return;
106 }
107 ACE_SCORING_EVENT("Tabs.onChange");
108 ACE_SCOPED_TRACE("Tabs.onChange index %d", tabsInfo->GetIndex());
109 PipelineContext::SetCallBackNode(node);
110 func->Execute(*tabsInfo);
111 };
112 TabsModel::GetInstance()->SetOnChange(std::move(onChange));
113 }
114
SetOnTabBarClick(const JSCallbackInfo & info)115 void JSTabs::SetOnTabBarClick(const JSCallbackInfo& info)
116 {
117 if (!info[0]->IsFunction()) {
118 return;
119 }
120
121 auto changeHandler = AceType::MakeRefPtr<JsEventFunction<TabContentChangeEvent, 1>>(
122 JSRef<JSFunc>::Cast(info[0]), TabContentChangeEventToJSValue);
123 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
124 auto onTabBarClick = [executionContext = info.GetExecutionContext(), func = std::move(changeHandler),
125 node = targetNode](const BaseEventInfo* info) {
126 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext);
127 const auto* tabsInfo = TypeInfoHelper::DynamicCast<TabContentChangeEvent>(info);
128 if (!tabsInfo) {
129 TAG_LOGW(AceLogTag::ACE_TABS, "Tabs onTabBarClick callback execute failed.");
130 return;
131 }
132 ACE_SCORING_EVENT("Tabs.onTabBarClick");
133 PipelineContext::SetCallBackNode(node);
134 func->Execute(*tabsInfo);
135 UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "Tabs.onTabBarClick");
136 };
137 TabsModel::GetInstance()->SetOnTabBarClick(std::move(onTabBarClick));
138 }
139
SetOnUnselected(const JSCallbackInfo & info)140 void JSTabs::SetOnUnselected(const JSCallbackInfo& info)
141 {
142 if (!info[0]->IsFunction()) {
143 return;
144 }
145 auto unselectedHandler = AceType::MakeRefPtr<JsEventFunction<TabContentChangeEvent, 1>>(
146 JSRef<JSFunc>::Cast(info[0]), TabContentChangeEventToJSValue);
147 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
148 auto onUnselected = [executionContext = info.GetExecutionContext(), func = std::move(unselectedHandler),
149 node = targetNode](const BaseEventInfo* info) {
150 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext);
151 const auto* tabsInfo = TypeInfoHelper::DynamicCast<TabContentChangeEvent>(info);
152 if (!tabsInfo) {
153 TAG_LOGW(AceLogTag::ACE_TABS, "Tabs onUnselected callback execute failed.");
154 return;
155 }
156 ACE_SCORING_EVENT("Tabs.onUnselected");
157 ACE_SCOPED_TRACE("Tabs.onUnselected index %d", tabsInfo->GetIndex());
158 PipelineContext::SetCallBackNode(node);
159 func->Execute(*tabsInfo);
160 };
161 TabsModel::GetInstance()->SetOnUnselected(std::move(onUnselected));
162 }
163
SetOnAnimationStart(const JSCallbackInfo & info)164 void JSTabs::SetOnAnimationStart(const JSCallbackInfo& info)
165 {
166 if (!info[0]->IsFunction()) {
167 return;
168 }
169
170 auto animationStartHandler = AceType::MakeRefPtr<JsSwiperFunction>(JSRef<JSFunc>::Cast(info[0]));
171 auto onAnimationStart = [executionContext = info.GetExecutionContext(),
172 func = std::move(animationStartHandler)](
173 int32_t index, int32_t targetIndex, const AnimationCallbackInfo& info) {
174 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext);
175 ACE_SCORING_EVENT("Tabs.onAnimationStart");
176 func->Execute(index, targetIndex, info);
177 };
178 TabsModel::GetInstance()->SetOnAnimationStart(std::move(onAnimationStart));
179 }
180
SetOnAnimationEnd(const JSCallbackInfo & info)181 void JSTabs::SetOnAnimationEnd(const JSCallbackInfo& info)
182 {
183 if (!info[0]->IsFunction()) {
184 return;
185 }
186
187 auto animationEndHandler = AceType::MakeRefPtr<JsSwiperFunction>(JSRef<JSFunc>::Cast(info[0]));
188 auto onAnimationEnd = [executionContext = info.GetExecutionContext(), func = std::move(animationEndHandler)](
189 int32_t index, const AnimationCallbackInfo& info) {
190 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext);
191 ACE_SCORING_EVENT("Tabs.onAnimationEnd");
192 func->Execute(index, info);
193 UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "Tabs.onAnimationEnd");
194 };
195 TabsModel::GetInstance()->SetOnAnimationEnd(std::move(onAnimationEnd));
196 }
197
SetOnGestureSwipe(const JSCallbackInfo & info)198 void JSTabs::SetOnGestureSwipe(const JSCallbackInfo& info)
199 {
200 if (!info[0]->IsFunction()) {
201 return;
202 }
203
204 auto gestureSwipeHandler = AceType::MakeRefPtr<JsSwiperFunction>(JSRef<JSFunc>::Cast(info[0]));
205 auto onGestureSwipe = [executionContext = info.GetExecutionContext(), func = std::move(gestureSwipeHandler)](
206 int32_t index, const AnimationCallbackInfo& info) {
207 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext);
208 ACE_SCORING_EVENT("Tabs.onGestureSwipe");
209 func->Execute(index, info);
210 };
211 TabsModel::GetInstance()->SetOnGestureSwipe(std::move(onGestureSwipe));
212 }
213
SetOnSelected(const JSCallbackInfo & info)214 void JSTabs::SetOnSelected(const JSCallbackInfo& info)
215 {
216 if (!info[0]->IsFunction()) {
217 return;
218 }
219 auto selectedHandler = AceType::MakeRefPtr<JsEventFunction<TabContentChangeEvent, 1>>(
220 JSRef<JSFunc>::Cast(info[0]), TabContentChangeEventToJSValue);
221 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
222 auto onSelected = [executionContext = info.GetExecutionContext(), func = std::move(selectedHandler),
223 node = targetNode](const BaseEventInfo* info) {
224 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext);
225 const auto* tabsInfo = TypeInfoHelper::DynamicCast<TabContentChangeEvent>(info);
226 if (!tabsInfo) {
227 TAG_LOGW(AceLogTag::ACE_TABS, "Tabs onSelected callback execute failed.");
228 return;
229 }
230 ACE_SCORING_EVENT("Tabs.onSelected");
231 ACE_SCOPED_TRACE("Tabs.onSelected index %d", tabsInfo->GetIndex());
232 PipelineContext::SetCallBackNode(node);
233 func->Execute(*tabsInfo);
234 };
235 TabsModel::GetInstance()->SetOnSelected(std::move(onSelected));
236 }
237
ParseTabsIndexObject(const JSCallbackInfo & info,const JSRef<JSVal> & changeEventVal)238 void ParseTabsIndexObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
239 {
240 CHECK_NULL_VOID(!changeEventVal->IsUndefined() && changeEventVal->IsFunction());
241
242 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
243 WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
244 auto onChangeEvent = [executionContext = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
245 const BaseEventInfo* info) {
246 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext);
247 const auto* tabsInfo = TypeInfoHelper::DynamicCast<TabContentChangeEvent>(info);
248 if (!tabsInfo) {
249 TAG_LOGW(AceLogTag::ACE_TABS, "ParseTabsIndexObject execute onChange event failed.");
250 return;
251 }
252 ACE_SCORING_EVENT("Tabs.onChangeEvent");
253 PipelineContext::SetCallBackNode(node);
254 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(tabsInfo->GetIndex()));
255 func->ExecuteJS(1, &newJSVal);
256 };
257 TabsModel::GetInstance()->SetOnChangeEvent(std::move(onChangeEvent));
258 }
259
Create(const JSCallbackInfo & info)260 void JSTabs::Create(const JSCallbackInfo& info)
261 {
262 BarPosition barPosition = BarPosition::START;
263 RefPtr<TabController> tabController;
264 RefPtr<NG::TabsControllerNG> tabsController = AceType::MakeRefPtr<NG::TabsControllerNG>();
265 int32_t index = -1;
266 JSRef<JSVal> changeEventVal;
267 auto jsValue = info[0];
268 if (jsValue->IsObject()) {
269 JSRef<JSObject> obj = JSRef<JSObject>::Cast(jsValue);
270 JSRef<JSVal> val = obj->GetProperty("barPosition");
271 if (val->IsNumber()) {
272 auto barPositionVal = val->ToNumber<int32_t>();
273 if (barPositionVal >= 0 && barPositionVal < static_cast<int32_t>(BAR_POSITIONS.size())) {
274 barPosition = BAR_POSITIONS[barPositionVal];
275 }
276 }
277 JSRef<JSVal> controller = obj->GetProperty("controller");
278 if (controller->IsObject()) {
279 auto* jsTabsController = JSRef<JSObject>::Cast(controller)->Unwrap<JSTabsController>();
280 if (jsTabsController) {
281 jsTabsController->SetInstanceId(Container::CurrentId());
282 tabController = jsTabsController->GetController();
283 jsTabsController->SetTabsController(tabsController);
284 }
285 }
286 JSRef<JSVal> indexVal = obj->GetProperty("index");
287 if (indexVal->IsNumber()) {
288 index = indexVal->ToNumber<int32_t>();
289 index = index < 0 ? 0 : index;
290 if (!tabController) {
291 tabController = JSTabsController::CreateController();
292 }
293 #ifndef NG_BUILD
294 tabController->SetInitialIndex(index);
295 #endif
296 changeEventVal = obj->GetProperty("$index");
297 } else if (indexVal->IsObject()) {
298 JSRef<JSObject> indexObj = JSRef<JSObject>::Cast(indexVal);
299 auto indexValueProperty = indexObj->GetProperty("value");
300 if (indexValueProperty->IsNumber()) {
301 index = indexValueProperty->ToNumber<int32_t>();
302 index = index < 0 ? 0 : index;
303 }
304 changeEventVal = indexObj->GetProperty("changeEvent");
305 }
306 }
307
308 TabsModel::GetInstance()->Create(barPosition, index, tabController, tabsController);
309 ParseTabsIndexObject(info, changeEventVal);
310 SetBarModifier(info, jsValue);
311 }
312
Pop()313 void JSTabs::Pop()
314 {
315 if (ViewStackModel::GetInstance()->IsPrebuilding()) {
316 return ViewStackModel::GetInstance()->PushPrebuildCompCmd("[JSTabs][pop]", &JSTabs::Pop);
317 }
318 TabsModel::GetInstance()->Pop();
319 }
320
SetBarPosition(const JSCallbackInfo & info)321 void JSTabs::SetBarPosition(const JSCallbackInfo& info)
322 {
323 BarPosition barVal = BarPosition::START;
324 if (info.Length() > 0 && info[0]->IsNumber()) {
325 auto barPositionVal = info[0]->ToNumber<int32_t>();
326 if (barPositionVal >= 0 && barPositionVal < static_cast<int32_t>(BAR_POSITIONS.size())) {
327 barVal = BAR_POSITIONS[barPositionVal];
328 }
329 }
330
331 TabsModel::GetInstance()->SetTabBarPosition(barVal);
332 }
333
SetVertical(const std::string & value)334 void JSTabs::SetVertical(const std::string& value)
335 {
336 TabsModel::GetInstance()->SetIsVertical(StringToBool(value));
337 }
338
SetScrollable(const std::string & value)339 void JSTabs::SetScrollable(const std::string& value)
340 {
341 if (value == "undefined") {
342 TabsModel::GetInstance()->SetScrollable(true);
343 return;
344 }
345 TabsModel::GetInstance()->SetScrollable(StringToBool(value));
346 }
347
SetBarMode(const JSCallbackInfo & info)348 void JSTabs::SetBarMode(const JSCallbackInfo& info)
349 {
350 TabBarMode barMode = TabBarMode::FIXED;
351 if (info.Length() < 1) {
352 TabsModel::GetInstance()->SetTabBarMode(barMode);
353 return;
354 }
355 auto barModeInfo = info[0];
356 if (barModeInfo->IsString()) {
357 barMode = ConvertStrToTabBarMode(barModeInfo->ToString());
358 }
359 if (barMode == TabBarMode::SCROLLABLE) {
360 if (info.Length() > 1 && info[1]->IsObject()) {
361 SetScrollableBarModeOptions(info[1]);
362 } else {
363 TabsModel::GetInstance()->ResetScrollableBarModeOptions();
364 }
365 }
366 TabsModel::GetInstance()->SetTabBarMode(barMode);
367 }
368
SetBarWidth(const JSCallbackInfo & info)369 void JSTabs::SetBarWidth(const JSCallbackInfo& info)
370 {
371 if (info.Length() < 1) {
372 return;
373 }
374
375 CalcDimension width = Dimension(-1.0, DimensionUnit::VP);
376 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
377 if (!ParseJsDimensionVpNG(info[0], width)) {
378 width = Dimension(-1.0, DimensionUnit::VP);
379 TabsModel::GetInstance()->SetTabBarWidth(width);
380 return;
381 }
382 } else {
383 ParseJsDimensionVp(info[0], width);
384 }
385
386 TabsModel::GetInstance()->SetTabBarWidth(width);
387 }
388
SetBarHeight(const JSCallbackInfo & info)389 void JSTabs::SetBarHeight(const JSCallbackInfo& info)
390 {
391 if (info.Length() < 1) {
392 return;
393 }
394 CalcDimension height = Dimension(-1.0, DimensionUnit::VP);
395 bool adaptiveHeight = false;
396 auto barHeightInfo = info[0];
397 if (barHeightInfo->IsString() && barHeightInfo->ToString() == "auto") {
398 adaptiveHeight = true;
399 } else {
400 if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
401 if (!ParseJsDimensionVpNG(barHeightInfo, height)) {
402 height = Dimension(-1.0, DimensionUnit::VP);
403 }
404 } else {
405 ParseJsDimensionVp(barHeightInfo, height);
406 }
407 }
408 TabsModel::GetInstance()->SetBarAdaptiveHeight(adaptiveHeight);
409 TabsModel::GetInstance()->SetTabBarHeight(height);
410 }
411
SetWidth(const JSCallbackInfo & info)412 void JSTabs::SetWidth(const JSCallbackInfo& info)
413 {
414 JSViewAbstract::JsWidth(info);
415 if (info.Length() < 1) {
416 return;
417 }
418 auto widthInfo = info[0];
419 if (widthInfo->IsString() && widthInfo->ToString().empty()) {
420 return;
421 }
422 if (widthInfo->IsString() && widthInfo->ToString() == "auto") {
423 ViewAbstractModel::GetInstance()->ClearWidthOrHeight(true);
424 TabsModel::GetInstance()->SetWidthAuto(true);
425 return;
426 }
427
428 TabsModel::GetInstance()->SetWidthAuto(false);
429 }
430
SetHeight(const JSCallbackInfo & info)431 void JSTabs::SetHeight(const JSCallbackInfo& info)
432 {
433 JSViewAbstract::JsHeight(info);
434 if (info.Length() < 1) {
435 return;
436 }
437 auto heightInfo = info[0];
438 if (heightInfo->IsString() && heightInfo->ToString().empty()) {
439 return;
440 }
441 if (heightInfo->IsString() && heightInfo->ToString() == "auto") {
442 ViewAbstractModel::GetInstance()->ClearWidthOrHeight(false);
443 TabsModel::GetInstance()->SetHeightAuto(true);
444 return;
445 }
446
447 TabsModel::GetInstance()->SetHeightAuto(false);
448 }
449
SetIndex(int32_t index)450 void JSTabs::SetIndex(int32_t index)
451 {
452 TabsModel::GetInstance()->SetIndex(index);
453 }
454
SetAnimationDuration(const JSCallbackInfo & info)455 void JSTabs::SetAnimationDuration(const JSCallbackInfo& info)
456 {
457 if (info.Length() <= 0) {
458 TabsModel::GetInstance()->SetAnimationDuration(-1);
459 return;
460 }
461 auto animationDurationInfo = info[0];
462 if ((!animationDurationInfo->IsNull() && !animationDurationInfo->IsNumber()) ||
463 (animationDurationInfo->IsNull() && Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN))) {
464 TabsModel::GetInstance()->SetAnimationDuration(-1);
465 return;
466 }
467 auto value = animationDurationInfo->IsNumber() ? animationDurationInfo->ToNumber<int32_t>() : 0;
468 TabsModel::GetInstance()->SetAnimationDuration(value);
469 }
470
SetFadingEdge(const JSCallbackInfo & info)471 void JSTabs::SetFadingEdge(const JSCallbackInfo& info)
472 {
473 bool fadingEdge = true;
474 if (info.Length() > 0) {
475 ParseJsBool(info[0], fadingEdge);
476 }
477 TabsModel::GetInstance()->SetFadingEdge(fadingEdge);
478 }
479
SetBarOverlap(const JSCallbackInfo & info)480 void JSTabs::SetBarOverlap(const JSCallbackInfo& info)
481 {
482 bool barOverlap = false;
483 if (info.Length() > 0) {
484 ParseJsBool(info[0], barOverlap);
485 }
486 TabsModel::GetInstance()->SetBarOverlap(barOverlap);
487 }
488
SetBarBackgroundColor(const JSCallbackInfo & info)489 void JSTabs::SetBarBackgroundColor(const JSCallbackInfo& info)
490 {
491 Color backgroundColor = Color::BLACK.BlendOpacity(0.0f);
492 if (info.Length() > 0) {
493 ConvertFromJSValue(info[0], backgroundColor);
494 }
495 TabsModel::GetInstance()->SetBarBackgroundColor(backgroundColor);
496 }
497
SetBarBackgroundBlurStyle(const JSCallbackInfo & info)498 void JSTabs::SetBarBackgroundBlurStyle(const JSCallbackInfo& info)
499 {
500 if (info.Length() == 0) {
501 return;
502 }
503 BlurStyleOption styleOption;
504 if (info[0]->IsNumber()) {
505 auto blurStyle = info[0]->ToNumber<int32_t>();
506 if (blurStyle >= static_cast<int>(BlurStyle::NO_MATERIAL) &&
507 blurStyle <= static_cast<int>(BlurStyle::COMPONENT_ULTRA_THICK)) {
508 styleOption.blurStyle = static_cast<BlurStyle>(blurStyle);
509 }
510 }
511 if (info.Length() > 1 && info[1]->IsObject()) {
512 JSRef<JSObject> jsOption = JSRef<JSObject>::Cast(info[1]);
513 ParseBlurStyleOption(jsOption, styleOption);
514 }
515 TabsModel::GetInstance()->SetBarBackgroundBlurStyle(styleOption);
516 }
517
SetDivider(const JSCallbackInfo & info)518 void JSTabs::SetDivider(const JSCallbackInfo& info)
519 {
520 TabsItemDivider divider;
521 CalcDimension dividerStrokeWidth;
522 CalcDimension dividerStartMargin;
523 CalcDimension dividerEndMargin;
524 RefPtr<TabTheme> tabTheme = GetTheme<TabTheme>();
525 CHECK_NULL_VOID(tabTheme);
526
527 if (info.Length() > 0) {
528 auto dividerInfo = info[0];
529 JSRef<JSObject> obj = JSRef<JSObject>::New();
530 if (dividerInfo->IsObject()) {
531 obj = JSRef<JSObject>::Cast(dividerInfo);
532 }
533 if (dividerInfo->IsNull()) {
534 divider.isNull = true;
535 } else {
536 if (!dividerInfo->IsObject() || !ParseJsDimensionVp(obj->GetProperty("strokeWidth"), dividerStrokeWidth) ||
537 dividerStrokeWidth.Value() < 0.0f || dividerStrokeWidth.Unit() == DimensionUnit::PERCENT) {
538 divider.strokeWidth.Reset();
539 } else {
540 divider.strokeWidth = dividerStrokeWidth;
541 }
542 if (!dividerInfo->IsObject() || !ConvertFromJSValue(obj->GetProperty("color"), divider.color)) {
543 divider.color = tabTheme->GetDividerColor();
544 }
545 if (!dividerInfo->IsObject() || !ParseJsDimensionVp(obj->GetProperty("startMargin"), dividerStartMargin) ||
546 dividerStartMargin.Value() < 0.0f || dividerStartMargin.Unit() == DimensionUnit::PERCENT) {
547 divider.startMargin.Reset();
548 } else {
549 divider.startMargin = dividerStartMargin;
550 }
551 if (!dividerInfo->IsObject() || !ParseJsDimensionVp(obj->GetProperty("endMargin"), dividerEndMargin) ||
552 dividerEndMargin.Value() < 0.0f || dividerEndMargin.Unit() == DimensionUnit::PERCENT) {
553 divider.endMargin.Reset();
554 } else {
555 divider.endMargin = dividerEndMargin;
556 }
557 }
558 }
559 TabsModel::GetInstance()->SetDivider(divider);
560 }
561
SetClip(const JSCallbackInfo & info)562 void JSTabs::SetClip(const JSCallbackInfo& info)
563 {
564 if (info[0]->IsObject() || !Container::IsCurrentUseNewPipeline()) {
565 JSViewAbstract::JsClip(info);
566 return;
567 }
568 if (info[0]->IsBoolean()) {
569 TabsModel::GetInstance()->SetClipEdge(info[0]->ToBoolean());
570 }
571 }
572
SetScrollableBarModeOptions(const JSRef<JSVal> & info)573 void JSTabs::SetScrollableBarModeOptions(const JSRef<JSVal>& info)
574 {
575 ScrollableBarModeOptions option;
576 auto optionParam = JSRef<JSObject>::Cast(info);
577 CalcDimension margin = Dimension(0.0, DimensionUnit::VP);
578 if (!ParseJsDimensionVp(optionParam->GetProperty("margin"), margin) || Negative(margin.Value()) ||
579 margin.Unit() == DimensionUnit::PERCENT) {
580 option.margin = 0.0_vp;
581 } else {
582 option.margin = margin;
583 }
584
585 auto nonScrollableLayoutStyle = optionParam->GetProperty("nonScrollableLayoutStyle");
586 int32_t layoutStyle;
587 if (!ConvertFromJSValue(nonScrollableLayoutStyle, layoutStyle) ||
588 layoutStyle < static_cast<int32_t>(LayoutStyle::ALWAYS_CENTER) ||
589 layoutStyle > static_cast<int32_t>(LayoutStyle::SPACE_BETWEEN_OR_CENTER)) {
590 option.nonScrollableLayoutStyle = std::nullopt;
591 } else {
592 option.nonScrollableLayoutStyle = (static_cast<LayoutStyle>(layoutStyle));
593 }
594 TabsModel::GetInstance()->SetScrollableBarModeOptions(option);
595 }
596
SetBarGridAlign(const JSCallbackInfo & info)597 void JSTabs::SetBarGridAlign(const JSCallbackInfo& info)
598 {
599 BarGridColumnOptions columnOption;
600 if (info.Length() > 0 && info[0]->IsObject()) {
601 auto gridParam = JSRef<JSObject>::Cast(info[0]);
602 auto sm = gridParam->GetProperty("sm");
603 if (sm->IsNumber() && sm->ToNumber<int32_t>() >= 0 && sm->ToNumber<int32_t>() <= SM_COLUMN_NUM &&
604 sm->ToNumber<int32_t>() % 2 == 0) {
605 columnOption.sm = sm->ToNumber<int32_t>();
606 }
607 auto md = gridParam->GetProperty("md");
608 if (md->IsNumber() && md->ToNumber<int32_t>() >= 0 && md->ToNumber<int32_t>() <= MD_COLUMN_NUM &&
609 md->ToNumber<int32_t>() % 2 == 0) {
610 columnOption.md = md->ToNumber<int32_t>();
611 }
612 auto lg = gridParam->GetProperty("lg");
613 if (lg->IsNumber() && lg->ToNumber<int32_t>() >= 0 && lg->ToNumber<int32_t>() <= LG_COLUMN_NUM &&
614 lg->ToNumber<int32_t>() % 2 == 0) {
615 columnOption.lg = lg->ToNumber<int32_t>();
616 }
617 CalcDimension columnGutter;
618 if (ParseJsDimensionVp(gridParam->GetProperty("gutter"), columnGutter) && NonNegative(columnGutter.Value()) &&
619 columnGutter.Unit() != DimensionUnit::PERCENT) {
620 columnOption.gutter = columnGutter;
621 }
622 CalcDimension columnMargin;
623 if (ParseJsDimensionVp(gridParam->GetProperty("margin"), columnMargin) && NonNegative(columnMargin.Value()) &&
624 columnMargin.Unit() != DimensionUnit::PERCENT) {
625 columnOption.margin = columnMargin;
626 }
627 }
628 TabsModel::GetInstance()->SetBarGridAlign(columnOption);
629 }
630
SetCustomContentTransition(const JSCallbackInfo & info)631 void JSTabs::SetCustomContentTransition(const JSCallbackInfo& info)
632 {
633 if (info.Length() != 1) {
634 return;
635 }
636
637 auto customContentTransitionInfo = info[0];
638 if (customContentTransitionInfo->IsUndefined() || !customContentTransitionInfo->IsFunction()) {
639 TabsModel::GetInstance()->SetIsCustomAnimation(false);
640 return;
641 }
642
643 RefPtr<JsTabsFunction> jsCustomAnimationFunc =
644 AceType::MakeRefPtr<JsTabsFunction>(JSRef<JSFunc>::Cast(customContentTransitionInfo));
645 auto onCustomAnimation = [execCtx = info.GetExecutionContext(), func = std::move(jsCustomAnimationFunc)](
646 int32_t from, int32_t to) -> TabContentAnimatedTransition {
647 TabContentAnimatedTransition transitionInfo;
648 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, transitionInfo);
649
650 auto ret = func->Execute(from, to);
651 if (!ret->IsObject()) {
652 return transitionInfo;
653 }
654
655 auto transitionObj = JSRef<JSObject>::Cast(ret);
656 JSRef<JSVal> timeoutProperty = transitionObj->GetProperty("timeout");
657 if (timeoutProperty->IsNumber()) {
658 auto timeout = timeoutProperty->ToNumber<int32_t>();
659 transitionInfo.timeout = timeout < 0 ? DEFAULT_CUSTOM_ANIMATION_TIMEOUT : timeout;
660 } else {
661 transitionInfo.timeout = DEFAULT_CUSTOM_ANIMATION_TIMEOUT;
662 }
663
664 JSRef<JSVal> transition = transitionObj->GetProperty("transition");
665 if (transition->IsFunction()) {
666 RefPtr<JsTabsFunction> jsOnTransition =
667 AceType::MakeRefPtr<JsTabsFunction>(JSRef<JSFunc>::Cast(transition));
668 auto onTransition = [execCtx, func = std::move(jsOnTransition)](
669 const RefPtr<TabContentTransitionProxy>& proxy) {
670 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
671 ACE_SCORING_EVENT("onTransition");
672 func->Execute(proxy);
673 };
674
675 transitionInfo.transition = std::move(onTransition);
676 }
677
678 return transitionInfo;
679 };
680 TabsModel::GetInstance()->SetIsCustomAnimation(true);
681 TabsModel::GetInstance()->SetOnCustomAnimation(std::move(onCustomAnimation));
682 }
683
SetOnContentWillChange(const JSCallbackInfo & info)684 void JSTabs::SetOnContentWillChange(const JSCallbackInfo& info)
685 {
686 if (!info[0]->IsFunction()) {
687 return;
688 }
689
690 auto handler = AceType::MakeRefPtr<JsTabsFunction>(JSRef<JSFunc>::Cast(info[0]));
691 auto callback = [execCtx = info.GetExecutionContext(), func = std::move(handler)]
692 (int32_t currentIndex, int32_t comingIndex) -> bool {
693 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, true);
694 ACE_SCORING_EVENT("Tabs.onContentWillChange");
695 auto ret = func->Execute(currentIndex, comingIndex);
696 if (!ret->IsBoolean()) {
697 return true;
698 }
699 return ret->ToBoolean();
700 };
701 TabsModel::GetInstance()->SetOnContentWillChange(std::move(callback));
702 }
703
SetAnimateMode(const JSCallbackInfo & info)704 void JSTabs::SetAnimateMode(const JSCallbackInfo& info)
705 {
706 JSRef<JSVal> args = info[0];
707 if (!args->IsNumber()) {
708 TabsModel::GetInstance()->SetAnimateMode(TabAnimateMode::CONTENT_FIRST);
709 return;
710 }
711 uint32_t value = args->ToNumber<uint32_t>();
712 if (value >= static_cast<uint32_t>(TabAnimateMode::MAX_VALUE)) {
713 TabsModel::GetInstance()->SetAnimateMode(TabAnimateMode::CONTENT_FIRST);
714 return;
715 }
716 TabsModel::GetInstance()->SetAnimateMode(static_cast<TabAnimateMode>(value));
717 }
718
SetEdgeEffect(const JSCallbackInfo & info)719 void JSTabs::SetEdgeEffect(const JSCallbackInfo& info)
720 {
721 auto edgeEffect = EdgeEffect::SPRING;
722 if (info.Length() > 0) {
723 edgeEffect = JSScrollable::ParseEdgeEffect(info[0], EdgeEffect::SPRING);
724 }
725 TabsModel::GetInstance()->SetEdgeEffect(edgeEffect);
726 }
727
SetBarBackgroundEffect(const JSCallbackInfo & info)728 void JSTabs::SetBarBackgroundEffect(const JSCallbackInfo& info)
729 {
730 if (info.Length() == 0) {
731 return;
732 }
733 EffectOption option;
734 if (info[0]->IsObject()) {
735 JSRef<JSObject> jsOption = JSRef<JSObject>::Cast(info[0]);
736 ParseEffectOption(jsOption, option);
737 }
738 TabsModel::GetInstance()->SetBarBackgroundEffect(option);
739 }
740
SetPageFlipMode(const JSCallbackInfo & info)741 void JSTabs::SetPageFlipMode(const JSCallbackInfo& info)
742 {
743 // default value
744 int32_t value = 0;
745 if (info.Length() < 1 || !info[0]->IsNumber()) {
746 TabsModel::GetInstance()->SetPageFlipMode(value);
747 return;
748 }
749 JSViewAbstract::ParseJsInt32(info[0], value);
750 TabsModel::GetInstance()->SetPageFlipMode(value);
751 }
752
SetBarModifier(const JSCallbackInfo & info,const JsiRef<JsiValue> & jsValue)753 void JSTabs::SetBarModifier(const JSCallbackInfo& info, const JsiRef<JsiValue>& jsValue)
754 {
755 if (!jsValue->IsObject()) {
756 return;
757 }
758 JSRef<JSObject> obj = JSRef<JSObject>::Cast(jsValue);
759 JSRef<JSVal> val = obj->GetProperty("barModifier");
760 if (!val->IsObject()) {
761 return;
762 }
763 JSRef<JSObject> modifierObj = JSRef<JSObject>::Cast(val);
764 auto vm = info.GetVm();
765 auto globalObj = JSNApi::GetGlobalObject(vm);
766 auto globalFunc = globalObj->Get(vm, panda::StringRef::NewFromUtf8(vm, "applyCommonModifierToNode"));
767 JsiValue jsiValue(globalFunc);
768 JsiRef<JsiValue> globalFuncRef = JsiRef<JsiValue>::Make(jsiValue);
769 std::function<void(WeakPtr<NG::FrameNode>)> onApply = nullptr;
770 if (globalFuncRef->IsFunction()) {
771 RefPtr<JsFunction> jsFunc =
772 AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(globalFuncRef));
773 onApply = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
774 modifierParam = std::move(modifierObj)](WeakPtr<NG::FrameNode> frameNode) {
775 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
776 CHECK_NULL_VOID(func);
777 auto node = frameNode.Upgrade();
778 CHECK_NULL_VOID(node);
779 JSRef<JSVal> params[2];
780 params[0] = modifierParam;
781 params[1] = JSRef<JSVal>::Make(panda::NativePointerRef::New(execCtx.vm_, AceType::RawPtr(node)));
782 PipelineContext::SetCallBackNode(node);
783 func->ExecuteJS(2, params);
784 };
785 }
786 TabsModel::GetInstance()->SetBarModifier(std::move(onApply));
787 }
788
SetCachedMaxCount(const JSCallbackInfo & info)789 void JSTabs::SetCachedMaxCount(const JSCallbackInfo& info)
790 {
791 if (info.Length() <= 1) {
792 return;
793 }
794
795 std::optional<int32_t> cachedMaxCount;
796 if (info[0]->IsNumber()) {
797 auto count = info[0]->ToNumber<int32_t>();
798 if (count >= 0) {
799 cachedMaxCount = count;
800 }
801 }
802 auto cacheMode = TabsCacheMode::CACHE_BOTH_SIDE;
803 if (info[1]->IsNumber()) {
804 auto mode = info[1]->ToNumber<int32_t>();
805 if (mode >= static_cast<int32_t>(TabsCacheMode::CACHE_BOTH_SIDE) &&
806 mode <= static_cast<int32_t>(TabsCacheMode::CACHE_LATEST_SWITCHED)) {
807 cacheMode = static_cast<TabsCacheMode>(mode);
808 }
809 }
810 TabsModel::GetInstance()->SetCachedMaxCount(cachedMaxCount, cacheMode);
811 }
812
JSBind(BindingTarget globalObj)813 void JSTabs::JSBind(BindingTarget globalObj)
814 {
815 JsTabContentTransitionProxy::JSBind(globalObj);
816 JSClass<JSTabs>::Declare("Tabs");
817 JSClass<JSTabs>::StaticMethod("create", &JSTabs::Create);
818 JSClass<JSTabs>::StaticMethod("pop", &JSTabs::Pop);
819 JSClass<JSTabs>::StaticMethod("vertical", &JSTabs::SetVertical);
820 JSClass<JSTabs>::StaticMethod("barPosition", &JSTabs::SetBarPosition);
821 JSClass<JSTabs>::StaticMethod("barBackgroundBlurStyle", &JSTabs::SetBarBackgroundBlurStyle);
822 JSClass<JSTabs>::StaticMethod("scrollable", &JSTabs::SetScrollable);
823 JSClass<JSTabs>::StaticMethod("barMode", &JSTabs::SetBarMode);
824 JSClass<JSTabs>::StaticMethod("barWidth", &JSTabs::SetBarWidth);
825 JSClass<JSTabs>::StaticMethod("barHeight", &JSTabs::SetBarHeight);
826 JSClass<JSTabs>::StaticMethod("width", &JSTabs::SetWidth);
827 JSClass<JSTabs>::StaticMethod("height", &JSTabs::SetHeight);
828 JSClass<JSTabs>::StaticMethod("index", &JSTabs::SetIndex);
829 JSClass<JSTabs>::StaticMethod("animationDuration", &JSTabs::SetAnimationDuration);
830 JSClass<JSTabs>::StaticMethod("divider", &JSTabs::SetDivider);
831 JSClass<JSTabs>::StaticMethod("onChange", &JSTabs::SetOnChange);
832 JSClass<JSTabs>::StaticMethod("onTabBarClick", &JSTabs::SetOnTabBarClick);
833 JSClass<JSTabs>::StaticMethod("onUnselected", &JSTabs::SetOnUnselected);
834 JSClass<JSTabs>::StaticMethod("onAnimationStart", &JSTabs::SetOnAnimationStart);
835 JSClass<JSTabs>::StaticMethod("onAnimationEnd", &JSTabs::SetOnAnimationEnd);
836 JSClass<JSTabs>::StaticMethod("onGestureSwipe", &JSTabs::SetOnGestureSwipe);
837 JSClass<JSTabs>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
838 JSClass<JSTabs>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
839 JSClass<JSTabs>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
840 JSClass<JSTabs>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
841 JSClass<JSTabs>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
842 JSClass<JSTabs>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
843 JSClass<JSTabs>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
844 JSClass<JSTabs>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
845 JSClass<JSTabs>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
846 JSClass<JSTabs>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
847 JSClass<JSTabs>::StaticMethod("fadingEdge", &JSTabs::SetFadingEdge);
848 JSClass<JSTabs>::StaticMethod("barOverlap", &JSTabs::SetBarOverlap);
849 JSClass<JSTabs>::StaticMethod("barBackgroundColor", &JSTabs::SetBarBackgroundColor);
850 JSClass<JSTabs>::StaticMethod("clip", &JSTabs::SetClip);
851 JSClass<JSTabs>::StaticMethod("barGridAlign", &JSTabs::SetBarGridAlign);
852 JSClass<JSTabs>::StaticMethod("customContentTransition", &JSTabs::SetCustomContentTransition);
853 JSClass<JSTabs>::StaticMethod("onContentWillChange", &JSTabs::SetOnContentWillChange);
854 JSClass<JSTabs>::StaticMethod("animationMode", &JSTabs::SetAnimateMode);
855 JSClass<JSTabs>::StaticMethod("edgeEffect", &JSTabs::SetEdgeEffect);
856 JSClass<JSTabs>::StaticMethod("barBackgroundEffect", &JSTabs::SetBarBackgroundEffect);
857 JSClass<JSTabs>::StaticMethod("pageFlipMode", &JSTabs::SetPageFlipMode);
858 JSClass<JSTabs>::StaticMethod("cachedMaxCount", &JSTabs::SetCachedMaxCount);
859 JSClass<JSTabs>::StaticMethod("onSelected", &JSTabs::SetOnSelected);
860
861 JSClass<JSTabs>::InheritAndBind<JSContainerBase>(globalObj);
862 }
863
864 } // namespace OHOS::Ace::Framework
865