1 /*
2 * Copyright (c) 2021-2022 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_swiper.h"
17
18 #include <algorithm>
19 #include <iterator>
20
21 #include "base/log/ace_scoring_log.h"
22 #include "bridge/common/utils/utils.h"
23 #include "bridge/declarative_frontend/engine/functions/js_click_function.h"
24 #include "bridge/declarative_frontend/jsview/models/swiper_model_impl.h"
25 #include "bridge/declarative_frontend/view_stack_processor.h"
26 #include "bridge/js_frontend/engine/jsi/js_value.h"
27 #include "core/components/common/layout/constants.h"
28 #include "core/components/common/properties/scroll_bar.h"
29 #include "core/components/swiper/swiper_component.h"
30 #include "core/components_ng/pattern/swiper/swiper_model_ng.h"
31
32 namespace OHOS::Ace {
33
34 std::unique_ptr<SwiperModel> SwiperModel::instance_ = nullptr;
35
GetInstance()36 SwiperModel* SwiperModel::GetInstance()
37 {
38 if (!instance_) {
39 #ifdef NG_BUILD
40 instance_.reset(new NG::SwiperModelNG());
41 #else
42 if (Container::IsCurrentUseNewPipeline()) {
43 instance_.reset(new NG::SwiperModelNG());
44 } else {
45 instance_.reset(new Framework::SwiperModelImpl());
46 }
47 #endif
48 }
49 return instance_.get();
50 }
51
52 } // namespace OHOS::Ace
53 namespace OHOS::Ace::Framework {
54 namespace {
55
56 const std::vector<EdgeEffect> EDGE_EFFECT = { EdgeEffect::SPRING, EdgeEffect::FADE, EdgeEffect::NONE };
57 const std::vector<SwiperDisplayMode> DISPLAY_MODE = { SwiperDisplayMode::STRETCH, SwiperDisplayMode::AUTO_LINEAR };
58
SwiperChangeEventToJSValue(const SwiperChangeEvent & eventInfo)59 JSRef<JSVal> SwiperChangeEventToJSValue(const SwiperChangeEvent& eventInfo)
60 {
61 return JSRef<JSVal>::Make(ToJSValue(eventInfo.GetIndex()));
62 }
63
64 } // namespace
65
Create(const JSCallbackInfo & info)66 void JSSwiper::Create(const JSCallbackInfo& info)
67 {
68 auto controller = SwiperModel::GetInstance()->Create();
69
70 if (info.Length() > 0 && info[0]->IsObject()) {
71 auto* jsController = JSRef<JSObject>::Cast(info[0])->Unwrap<JSSwiperController>();
72 if (jsController) {
73 jsController->SetController(controller);
74 }
75 }
76 }
77
JsRemoteMessage(const JSCallbackInfo & info)78 void JSSwiper::JsRemoteMessage(const JSCallbackInfo& info)
79 {
80 RemoteCallback remoteCallback;
81 JSInteractableView::JsRemoteMessage(info, remoteCallback);
82
83 SwiperModel::GetInstance()->SetRemoteMessageEventId(std::move(remoteCallback));
84 }
85
JSBind(BindingTarget globalObj)86 void JSSwiper::JSBind(BindingTarget globalObj)
87 {
88 JSClass<JSSwiper>::Declare("Swiper");
89 MethodOptions opt = MethodOptions::NONE;
90 JSClass<JSSwiper>::StaticMethod("create", &JSSwiper::Create, opt);
91 JSClass<JSSwiper>::StaticMethod("autoPlay", &JSSwiper::SetAutoPlay, opt);
92 JSClass<JSSwiper>::StaticMethod("duration", &JSSwiper::SetDuration, opt);
93 JSClass<JSSwiper>::StaticMethod("index", &JSSwiper::SetIndex, opt);
94 JSClass<JSSwiper>::StaticMethod("interval", &JSSwiper::SetInterval, opt);
95 JSClass<JSSwiper>::StaticMethod("loop", &JSSwiper::SetLoop, opt);
96 JSClass<JSSwiper>::StaticMethod("vertical", &JSSwiper::SetVertical, opt);
97 JSClass<JSSwiper>::StaticMethod("indicator", &JSSwiper::SetIndicator, opt);
98 JSClass<JSSwiper>::StaticMethod("displayMode", &JSSwiper::SetDisplayMode);
99 JSClass<JSSwiper>::StaticMethod("effectMode", &JSSwiper::SetEffectMode);
100 JSClass<JSSwiper>::StaticMethod("displayCount", &JSSwiper::SetDisplayCount);
101 JSClass<JSSwiper>::StaticMethod("itemSpace", &JSSwiper::SetItemSpace);
102 JSClass<JSSwiper>::StaticMethod("cachedCount", &JSSwiper::SetCachedCount);
103 JSClass<JSSwiper>::StaticMethod("curve", &JSSwiper::SetCurve);
104 JSClass<JSSwiper>::StaticMethod("onChange", &JSSwiper::SetOnChange);
105 JSClass<JSSwiper>::StaticMethod("onAnimationStart", &JSSwiper::SetOnAnimationStart);
106 JSClass<JSSwiper>::StaticMethod("onAnimationEnd", &JSSwiper::SetOnAnimationEnd);
107 JSClass<JSSwiper>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
108 JSClass<JSSwiper>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
109 JSClass<JSSwiper>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
110 JSClass<JSSwiper>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
111 JSClass<JSSwiper>::StaticMethod("remoteMessage", &JSSwiper::JsRemoteMessage);
112 JSClass<JSSwiper>::StaticMethod("onClick", &JSSwiper::SetOnClick);
113 JSClass<JSSwiper>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
114 JSClass<JSSwiper>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
115 JSClass<JSSwiper>::StaticMethod("indicatorStyle", &JSSwiper::SetIndicatorStyle);
116 JSClass<JSSwiper>::StaticMethod("enabled", &JSSwiper::SetEnabled);
117 JSClass<JSSwiper>::StaticMethod("disableSwipe", &JSSwiper::SetDisableSwipe);
118 JSClass<JSSwiper>::StaticMethod("height", &JSSwiper::SetHeight);
119 JSClass<JSSwiper>::StaticMethod("width", &JSSwiper::SetWidth);
120 JSClass<JSSwiper>::StaticMethod("size", &JSSwiper::SetSize);
121 JSClass<JSSwiper>::Inherit<JSContainerBase>();
122 JSClass<JSSwiper>::Inherit<JSViewAbstract>();
123 JSClass<JSSwiper>::Bind<>(globalObj);
124 }
125
SetAutoPlay(bool autoPlay)126 void JSSwiper::SetAutoPlay(bool autoPlay)
127 {
128 SwiperModel::GetInstance()->SetAutoPlay(autoPlay);
129 }
130
SetEnabled(const JSCallbackInfo & info)131 void JSSwiper::SetEnabled(const JSCallbackInfo& info)
132 {
133 JSViewAbstract::JsEnabled(info);
134 if (info.Length() < 1) {
135 LOGE("The info is wrong, it is supposed to have at least 1 arguments");
136 return;
137 }
138
139 if (!info[0]->IsBoolean()) {
140 LOGE("info is not bool.");
141 return;
142 }
143
144 SwiperModel::GetInstance()->SetEnabled(info[0]->IsBoolean());
145 }
146
SetDisableSwipe(bool disableSwipe)147 void JSSwiper::SetDisableSwipe(bool disableSwipe)
148 {
149 SwiperModel::GetInstance()->SetDisableSwipe(disableSwipe);
150 }
151
SetEffectMode(const JSCallbackInfo & info)152 void JSSwiper::SetEffectMode(const JSCallbackInfo& info)
153 {
154 if (info.Length() < 1) {
155 LOGE("The info is wrong, it is supposed to have atleast 1 arguments");
156 return;
157 }
158
159 if (!info[0]->IsNumber()) {
160 LOGE("info is not a number ");
161 return;
162 }
163
164 auto edgeEffect = info[0]->ToNumber<int32_t>();
165 if (edgeEffect < 0 || edgeEffect >= static_cast<int32_t>(EDGE_EFFECT.size())) {
166 LOGE("Edge effect: %{public}d illegal value", edgeEffect);
167 return;
168 }
169
170 SwiperModel::GetInstance()->SetEdgeEffect(EDGE_EFFECT[edgeEffect]);
171 }
172
SetDisplayCount(const JSCallbackInfo & info)173 void JSSwiper::SetDisplayCount(const JSCallbackInfo& info)
174 {
175 if (info.Length() < 1) {
176 LOGE("The info is wrong, it is supposed to have atleast 1 arguments");
177 return;
178 }
179
180 if (info[0]->IsString() && info[0]->ToString() == "auto") {
181 SwiperModel::GetInstance()->SetDisplayMode(SwiperDisplayMode::AUTO_LINEAR);
182 } else if (info[0]->IsNumber()) {
183 SwiperModel::GetInstance()->SetDisplayCount(info[0]->ToNumber<int32_t>());
184 }
185 }
186
SetDuration(int32_t duration)187 void JSSwiper::SetDuration(int32_t duration)
188 {
189 if (duration < 0) {
190 LOGE("duration is not valid: %{public}d", duration);
191 return;
192 }
193
194 SwiperModel::GetInstance()->SetDuration(duration);
195 }
196
SetIndex(int32_t index)197 void JSSwiper::SetIndex(int32_t index)
198 {
199 if (index < 0) {
200 LOGE("index is not valid: %{public}d", index);
201 return;
202 }
203
204 SwiperModel::GetInstance()->SetIndex(index);
205 }
206
SetInterval(int32_t interval)207 void JSSwiper::SetInterval(int32_t interval)
208 {
209 if (interval < 0) {
210 LOGE("interval is not valid: %{public}d", interval);
211 return;
212 }
213
214 SwiperModel::GetInstance()->SetAutoPlayInterval(interval);
215 }
216
SetLoop(bool loop)217 void JSSwiper::SetLoop(bool loop)
218 {
219 SwiperModel::GetInstance()->SetLoop(loop);
220 }
221
SetVertical(bool isVertical)222 void JSSwiper::SetVertical(bool isVertical)
223 {
224 SwiperModel::GetInstance()->SetDirection(isVertical ? Axis::VERTICAL : Axis::HORIZONTAL);
225 }
226
SetIndicator(bool showIndicator)227 void JSSwiper::SetIndicator(bool showIndicator)
228 {
229 SwiperModel::GetInstance()->SetShowIndicator(showIndicator);
230 }
231
SetIndicatorStyle(const JSCallbackInfo & info)232 void JSSwiper::SetIndicatorStyle(const JSCallbackInfo& info)
233 {
234 SwiperParameters swiperParameters;
235 if (info[0]->IsObject()) {
236 JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
237 JSRef<JSVal> leftValue = obj->GetProperty("left");
238 JSRef<JSVal> topValue = obj->GetProperty("top");
239 JSRef<JSVal> rightValue = obj->GetProperty("right");
240 JSRef<JSVal> bottomValue = obj->GetProperty("bottom");
241 JSRef<JSVal> sizeValue = obj->GetProperty("size");
242 JSRef<JSVal> maskValue = obj->GetProperty("mask");
243 JSRef<JSVal> colorValue = obj->GetProperty("color");
244 JSRef<JSVal> selectedColorValue = obj->GetProperty("selectedColor");
245
246 Dimension dimLeft;
247 if (ParseJsDimensionPx(leftValue, dimLeft)) {
248 swiperParameters.dimLeft = dimLeft;
249 }
250 Dimension dimTop;
251 if (ParseJsDimensionPx(topValue, dimTop)) {
252 swiperParameters.dimTop = dimTop;
253 }
254 Dimension dimRight;
255 if (ParseJsDimensionPx(rightValue, dimRight)) {
256 swiperParameters.dimRight = dimRight;
257 }
258 Dimension dimBottom;
259 if (ParseJsDimensionPx(bottomValue, dimBottom)) {
260 swiperParameters.dimBottom = dimBottom;
261 }
262 Dimension dimSize;
263 if (ParseJsDimensionPx(sizeValue, dimSize)) {
264 swiperParameters.dimSize = dimSize;
265 }
266 if (maskValue->IsBoolean()) {
267 auto mask = maskValue->ToBoolean();
268 swiperParameters.maskValue = mask;
269 }
270 Color colorVal;
271 if (ParseJsColor(colorValue, colorVal)) {
272 swiperParameters.colorVal = colorVal;
273 }
274 Color selectedColorVal;
275 if (ParseJsColor(selectedColorValue, selectedColorVal)) {
276 swiperParameters.selectedColorVal = selectedColorVal;
277 }
278 }
279
280 SwiperModel::GetInstance()->SetIndicatorStyle(swiperParameters);
281
282 info.ReturnSelf();
283 }
284
SetItemSpace(const JSCallbackInfo & info)285 void JSSwiper::SetItemSpace(const JSCallbackInfo& info)
286 {
287 if (info.Length() < 1) {
288 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
289 return;
290 }
291
292 Dimension value;
293 if (!ParseJsDimensionVp(info[0], value)) {
294 return;
295 }
296
297 if (LessNotEqual(value.Value(), 0.0)) {
298 value.SetValue(0.0);
299 }
300
301 SwiperModel::GetInstance()->SetItemSpace(value);
302 }
303
SetDisplayMode(int32_t index)304 void JSSwiper::SetDisplayMode(int32_t index)
305 {
306 if (index < 0 || index >= static_cast<int32_t>(DISPLAY_MODE.size())) {
307 LOGE("display mode is not valid: %{public}d", index);
308 return;
309 }
310
311 SwiperModel::GetInstance()->SetDisplayMode(DISPLAY_MODE[index]);
312 }
313
SetCachedCount(int32_t cachedCount)314 void JSSwiper::SetCachedCount(int32_t cachedCount)
315 {
316 SwiperModel::GetInstance()->SetCachedCount(cachedCount);
317 }
318
SetCurve(const std::string & curveStr)319 void JSSwiper::SetCurve(const std::string& curveStr)
320 {
321 RefPtr<Curve> curve = CreateCurve(curveStr);
322
323 SwiperModel::GetInstance()->SetCurve(curve);
324 }
325
SetOnChange(const JSCallbackInfo & info)326 void JSSwiper::SetOnChange(const JSCallbackInfo& info)
327 {
328 if (!info[0]->IsFunction()) {
329 return;
330 }
331
332 auto changeHandler = AceType::MakeRefPtr<JsEventFunction<SwiperChangeEvent, 1>>(
333 JSRef<JSFunc>::Cast(info[0]), SwiperChangeEventToJSValue);
334 auto onChange = [executionContext = info.GetExecutionContext(), func = std::move(changeHandler)](
335 const BaseEventInfo* info) {
336 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext);
337 const auto* swiperInfo = TypeInfoHelper::DynamicCast<SwiperChangeEvent>(info);
338 if (!swiperInfo) {
339 LOGE("HandleChangeEvent swiperInfo == nullptr");
340 return;
341 }
342 ACE_SCORING_EVENT("Swiper.OnChange");
343 func->Execute(*swiperInfo);
344 };
345
346 SwiperModel::GetInstance()->SetOnChange(std::move(onChange));
347 }
348
SetOnAnimationStart(const JSCallbackInfo & info)349 void JSSwiper::SetOnAnimationStart(const JSCallbackInfo& info)
350 {
351 if (!info[0]->IsFunction()) {
352 return;
353 }
354
355 auto animationStartHandler = AceType::MakeRefPtr<JsEventFunction<SwiperChangeEvent, 1>>(
356 JSRef<JSFunc>::Cast(info[0]), SwiperChangeEventToJSValue);
357 auto onAnimationStart = [executionContext = info.GetExecutionContext(), func = std::move(animationStartHandler)](
358 const BaseEventInfo* info) {
359 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext);
360 const auto* swiperInfo = TypeInfoHelper::DynamicCast<SwiperChangeEvent>(info);
361 if (!swiperInfo) {
362 LOGE("onAnimationStart swiperInfo is nullptr");
363 return;
364 }
365 ACE_SCORING_EVENT("Swiper.onAnimationStart");
366 func->Execute(*swiperInfo);
367 };
368
369 SwiperModel::GetInstance()->SetOnAnimationStart(std::move(onAnimationStart));
370 }
371
SetOnAnimationEnd(const JSCallbackInfo & info)372 void JSSwiper::SetOnAnimationEnd(const JSCallbackInfo& info)
373 {
374 if (!info[0]->IsFunction()) {
375 return;
376 }
377
378 auto animationEndHandler = AceType::MakeRefPtr<JsEventFunction<SwiperChangeEvent, 1>>(
379 JSRef<JSFunc>::Cast(info[0]), SwiperChangeEventToJSValue);
380 auto onAnimationEnd = [executionContext = info.GetExecutionContext(), func = std::move(animationEndHandler)](
381 const BaseEventInfo* info) {
382 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(executionContext);
383 const auto* swiperInfo = TypeInfoHelper::DynamicCast<SwiperChangeEvent>(info);
384 if (!swiperInfo) {
385 LOGE("onAnimationEnd swiperInfo is nullptr");
386 return;
387 }
388 ACE_SCORING_EVENT("Swiper.onAnimationEnd");
389 func->Execute(*swiperInfo);
390 };
391
392 SwiperModel::GetInstance()->SetOnAnimationEnd(std::move(onAnimationEnd));
393 }
394
SetOnClick(const JSCallbackInfo & info)395 void JSSwiper::SetOnClick(const JSCallbackInfo& info)
396 {
397 if (Container::IsCurrentUseNewPipeline()) {
398 JSInteractableView::JsOnClick(info);
399 return;
400 }
401
402 if (!info[0]->IsFunction()) {
403 LOGW("JSSwiper::SetOnClick the info is not click function");
404 return;
405 }
406
407 RefPtr<JsClickFunction> jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
408 auto onClick = [execCtx = info.GetExecutionContext(), func = std::move(jsOnClickFunc)](
409 const BaseEventInfo* info, const RefPtr<V2::InspectorFunctionImpl>& impl) {
410 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
411 const auto* clickInfo = TypeInfoHelper::DynamicCast<ClickInfo>(info);
412 auto newInfo = *clickInfo;
413 if (impl) {
414 impl->UpdateEventInfo(newInfo);
415 }
416 ACE_SCORING_EVENT("onClick");
417 func->Execute(newInfo);
418 };
419
420 SwiperModel::GetInstance()->SetOnClick(onClick);
421 }
422
SetWidth(const JSCallbackInfo & info)423 void JSSwiper::SetWidth(const JSCallbackInfo& info)
424 {
425 if (info.Length() < 1) {
426 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
427 return;
428 }
429
430 SetWidth(info[0]);
431 }
432
SetWidth(const JSRef<JSVal> & jsValue)433 void JSSwiper::SetWidth(const JSRef<JSVal>& jsValue)
434 {
435 if (Container::IsCurrentUseNewPipeline()) {
436 JSViewAbstract::JsWidth(jsValue);
437 return;
438 }
439
440 JSViewAbstract::JsWidth(jsValue);
441 SwiperModel::GetInstance()->SetMainSwiperSizeWidth();
442 }
443
SetHeight(const JSRef<JSVal> & jsValue)444 void JSSwiper::SetHeight(const JSRef<JSVal>& jsValue)
445 {
446 if (Container::IsCurrentUseNewPipeline()) {
447 JSViewAbstract::JsHeight(jsValue);
448 return;
449 }
450
451 JSViewAbstract::JsHeight(jsValue);
452 SwiperModel::GetInstance()->SetMainSwiperSizeHeight();
453 }
454
SetHeight(const JSCallbackInfo & info)455 void JSSwiper::SetHeight(const JSCallbackInfo& info)
456 {
457 if (info.Length() < 1) {
458 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
459 return;
460 }
461
462 SetHeight(info[0]);
463 }
464
SetSize(const JSCallbackInfo & info)465 void JSSwiper::SetSize(const JSCallbackInfo& info)
466 {
467 if (info.Length() < 1) {
468 LOGE("The arg is wrong, it is supposed to have atleast 1 arguments");
469 return;
470 }
471
472 if (!info[0]->IsObject()) {
473 LOGE("arg is not Object or String.");
474 return;
475 }
476
477 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
478 SetWidth(sizeObj->GetProperty("width"));
479 SetHeight(sizeObj->GetProperty("height"));
480 }
481
JSBind(BindingTarget globalObj)482 void JSSwiperController::JSBind(BindingTarget globalObj)
483 {
484 JSClass<JSSwiperController>::Declare("SwiperController");
485 JSClass<JSSwiperController>::CustomMethod("swipeTo", &JSSwiperController::SwipeTo);
486 JSClass<JSSwiperController>::CustomMethod("showNext", &JSSwiperController::ShowNext);
487 JSClass<JSSwiperController>::CustomMethod("showPrevious", &JSSwiperController::ShowPrevious);
488 JSClass<JSSwiperController>::CustomMethod("finishAnimation", &JSSwiperController::FinishAnimation);
489 JSClass<JSSwiperController>::Bind(globalObj, JSSwiperController::Constructor, JSSwiperController::Destructor);
490 }
491
Constructor(const JSCallbackInfo & args)492 void JSSwiperController::Constructor(const JSCallbackInfo& args)
493 {
494 auto scroller = Referenced::MakeRefPtr<JSSwiperController>();
495 scroller->IncRefCount();
496 args.SetReturnValue(Referenced::RawPtr(scroller));
497 }
498
Destructor(JSSwiperController * scroller)499 void JSSwiperController::Destructor(JSSwiperController* scroller)
500 {
501 if (scroller != nullptr) {
502 scroller->DecRefCount();
503 }
504 }
505
FinishAnimation(const JSCallbackInfo & args)506 void JSSwiperController::FinishAnimation(const JSCallbackInfo& args)
507 {
508 if (!controller_) {
509 return;
510 }
511
512 if (args.Length() > 0 && args[0]->IsFunction()) {
513 RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(args[0]));
514 auto onFinish = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc)]() {
515 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
516 ACE_SCORING_EVENT("Swiper.finishAnimation");
517 func->Execute();
518 };
519
520 controller_->SetFinishCallback(onFinish);
521 controller_->FinishAnimation();
522 return;
523 }
524
525 controller_->FinishAnimation();
526 }
527
528 } // namespace OHOS::Ace::Framework
529