1 /*
2  * Copyright (c) 2021-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 "bridge/declarative_frontend/jsview/js_scroll.h"
17 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
18 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
19 #endif
20 
21 #include "base/utils/utils.h"
22 #include "bridge/declarative_frontend/jsview/js_scrollable.h"
23 #include "bridge/declarative_frontend/jsview/js_scroller.h"
24 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
25 #include "bridge/declarative_frontend/jsview/models/scroll_model_impl.h"
26 #include "core/common/container.h"
27 #include "core/components/common/layout/constants.h"
28 #include "core/components/scroll/scrollable.h"
29 #include "core/components_ng/pattern/scroll/inner/scroll_bar.h"
30 #include "core/components_ng/pattern/scroll/scroll_model_ng.h"
31 #include "bridge/declarative_frontend/ark_theme/theme_apply/js_scroll_theme.h"
32 
33 namespace OHOS::Ace {
34 
35 std::unique_ptr<ScrollModel> ScrollModel::instance_ = nullptr;
36 std::mutex ScrollModel::mutex_;
37 
GetInstance()38 ScrollModel* ScrollModel::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::ScrollModelNG());
45 #else
46             if (Container::IsCurrentUseNewPipeline()) {
47                 instance_.reset(new NG::ScrollModelNG());
48             } else {
49                 instance_.reset(new Framework::ScrollModelImpl());
50             }
51 #endif
52         }
53     }
54     return instance_.get();
55 }
56 
57 } // namespace OHOS::Ace
58 
59 namespace OHOS::Ace::Framework {
60 namespace {
61 const std::vector<Axis> AXIS = { Axis::VERTICAL, Axis::HORIZONTAL, Axis::FREE, Axis::NONE };
62 
ParseJsDimensionArray(const JSRef<JSVal> & jsValue,std::vector<Dimension> & result)63 bool ParseJsDimensionArray(const JSRef<JSVal>& jsValue, std::vector<Dimension>& result)
64 {
65     if (!jsValue->IsArray()) {
66         return false;
67     }
68     JSRef<JSArray> array = JSRef<JSArray>::Cast(jsValue);
69     for (size_t i = 0; i < array->Length(); i++) {
70         JSRef<JSVal> value = array->GetValueAt(i);
71         CalcDimension dimension;
72         if (JSViewAbstract::ParseJsDimensionVp(value, dimension)) {
73             result.emplace_back(static_cast<Dimension>(dimension));
74         } else {
75             return false;
76         }
77     }
78     return true;
79 }
80 
CheckSnapPaginations(std::vector<Dimension> snapPaginations)81 bool CheckSnapPaginations(std::vector<Dimension> snapPaginations)
82 {
83     CHECK_NULL_RETURN(!snapPaginations.empty(), false);
84     float preValue = (*snapPaginations.begin()).Value();
85     CHECK_NULL_RETURN(!Negative(preValue), false);
86     auto unit = (*snapPaginations.begin()).Unit();
87     for (auto iter = snapPaginations.begin() + 1; iter < snapPaginations.end(); ++iter) {
88         if (Negative((*iter).Value()) || (*iter).Unit() != unit || LessOrEqual((*iter).Value(), preValue)) {
89             return false;
90         }
91         preValue = (*iter).Value();
92     }
93     return true;
94 }
95 } // namespace
96 
Create(const JSCallbackInfo & info)97 void JSScroll::Create(const JSCallbackInfo& info)
98 {
99     ScrollModel::GetInstance()->Create();
100     if (info.Length() > 0 && info[0]->IsObject()) {
101         JSScroller* jsScroller = JSRef<JSObject>::Cast(info[0])->Unwrap<JSScroller>();
102         if (jsScroller) {
103             jsScroller->SetInstanceId(Container::CurrentId());
104             auto positionController = ScrollModel::GetInstance()->GetOrCreateController();
105             jsScroller->SetController(positionController);
106             // Init scroll bar proxy.
107             auto proxy = jsScroller->GetScrollBarProxy();
108             if (!proxy) {
109                 proxy = ScrollModel::GetInstance()->CreateScrollBarProxy();
110                 jsScroller->SetScrollBarProxy(proxy);
111             }
112             ScrollModel::GetInstance()->SetScrollBarProxy(proxy);
113         }
114     }
115     // init scroll bar
116     std::pair<bool, Color> barColor;
117     barColor.first = false;
118     std::pair<bool, Dimension> barWidth;
119     barWidth.first = false;
120     ScrollModel::GetInstance()->InitScrollBar(GetTheme<ScrollBarTheme>(), barColor, barWidth, EdgeEffect::NONE);
121     JSScrollTheme::ApplyTheme();
122 }
123 
SetScrollable(int32_t value)124 void JSScroll::SetScrollable(int32_t value)
125 {
126     if (value < 0 || value >= static_cast<int32_t>(AXIS.size())) {
127         return;
128     }
129     ScrollModel::GetInstance()->SetAxis(AXIS[value]);
130 }
131 
SetScrollEnabled(const JSCallbackInfo & args)132 void JSScroll::SetScrollEnabled(const JSCallbackInfo& args)
133 {
134     ScrollModel::GetInstance()->SetScrollEnabled(args[0]->IsBoolean() ? args[0]->ToBoolean() : true);
135 }
136 
OnScrollBeginCallback(const JSCallbackInfo & args)137 void JSScroll::OnScrollBeginCallback(const JSCallbackInfo& args)
138 {
139     if (args[0]->IsFunction()) {
140         auto onScrollBegin = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
141                                  const Dimension& dx, const Dimension& dy) -> ScrollInfo {
142             ScrollInfo scrollInfo { .dx = dx, .dy = dy };
143             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollInfo);
144             auto params = ConvertToJSValues(dx, dy);
145             auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
146             if (result.IsEmpty()) {
147                 return scrollInfo;
148             }
149 
150             if (!result->IsObject()) {
151                 return scrollInfo;
152             }
153 
154             auto resObj = JSRef<JSObject>::Cast(result);
155             auto dxRemainValue = resObj->GetProperty("dxRemain");
156             if (dxRemainValue->IsNumber()) {
157                 scrollInfo.dx = Dimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
158             }
159             auto dyRemainValue = resObj->GetProperty("dyRemain");
160             if (dyRemainValue->IsNumber()) {
161                 scrollInfo.dy = Dimension(dyRemainValue->ToNumber<float>(), DimensionUnit::VP);
162             }
163             return scrollInfo;
164         };
165         ScrollModel::GetInstance()->SetOnScrollBegin(std::move(onScrollBegin));
166     }
167     args.SetReturnValue(args.This());
168 }
169 
OnScrollFrameBeginCallback(const JSCallbackInfo & args)170 void JSScroll::OnScrollFrameBeginCallback(const JSCallbackInfo& args)
171 {
172     if (args[0]->IsFunction()) {
173         auto onScrollFrameBegin = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
174                                       const Dimension& offset, ScrollState state) -> ScrollFrameResult {
175             OHOS::Ace::ScrollFrameResult scrollRes { .offset = offset };
176             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollRes);
177             auto params = ConvertToJSValues(offset, state);
178             auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
179             if (result.IsEmpty()) {
180                 return scrollRes;
181             }
182 
183             if (!result->IsObject()) {
184                 return scrollRes;
185             }
186 
187             auto resObj = JSRef<JSObject>::Cast(result);
188             auto dxRemainValue = resObj->GetProperty("offsetRemain");
189             if (dxRemainValue->IsNumber()) {
190                 scrollRes.offset = Dimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
191             }
192             return scrollRes;
193         };
194         ScrollModel::GetInstance()->SetOnScrollFrameBegin(std::move(onScrollFrameBegin));
195     }
196     args.SetReturnValue(args.This());
197 }
198 
OnScrollCallback(const JSCallbackInfo & args)199 void JSScroll::OnScrollCallback(const JSCallbackInfo& args)
200 {
201     auto callbackInfo = args[0];
202     if (callbackInfo->IsFunction()) {
203         auto onScroll = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(callbackInfo)](
204                             const Dimension& xOffset, const Dimension& yOffset) {
205             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
206             auto params = ConvertToJSValues(xOffset, yOffset);
207             func->Call(JSRef<JSObject>(), params.size(), params.data());
208         };
209         ScrollModel::GetInstance()->SetOnScroll(std::move(onScroll));
210     }
211 }
212 
OnWillScrollCallback(const JSCallbackInfo & args)213 void JSScroll::OnWillScrollCallback(const JSCallbackInfo& args)
214 {
215     if (args.Length() <= 0) {
216         return;
217     }
218 
219     if (args[0]->IsFunction()) {
220         auto onScroll = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
221                             const Dimension& xOffset, const Dimension& yOffset, const ScrollState& scrollState,
222                             ScrollSource scrollSource) {
223             auto params = ConvertToJSValues(xOffset, yOffset, scrollState, scrollSource);
224             NG::TwoDimensionScrollResult scrollRes { .xOffset = xOffset, .yOffset = yOffset };
225             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollRes);
226             auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
227             if (result.IsEmpty()) {
228                 return scrollRes;
229             }
230 
231             if (!result->IsObject()) {
232                 return scrollRes;
233             }
234 
235             auto resObj = JSRef<JSObject>::Cast(result);
236             auto dxRemainValue = resObj->GetProperty("xOffset");
237             if (dxRemainValue->IsNumber()) {
238                 scrollRes.xOffset = Dimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
239             }
240             auto dyRemainValue = resObj->GetProperty("yOffset");
241             if (dyRemainValue->IsNumber()) {
242                 scrollRes.yOffset = Dimension(dyRemainValue->ToNumber<float>(), DimensionUnit::VP);
243             }
244             return scrollRes;
245         };
246         ScrollModel::GetInstance()->SetOnWillScroll(std::move(onScroll));
247     } else {
248         ScrollModel::GetInstance()->SetOnWillScroll(nullptr);
249     }
250 }
251 
OnDidScrollCallback(const JSCallbackInfo & args)252 void JSScroll::OnDidScrollCallback(const JSCallbackInfo& args)
253 {
254     if (args.Length() > 0 && args[0]->IsFunction()) {
255         auto onScroll = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
256                             const Dimension& xOffset, const Dimension& yOffset, const ScrollState& scrollState) {
257             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
258             auto params = ConvertToJSValues(xOffset, yOffset, scrollState);
259             func->Call(JSRef<JSObject>(), params.size(), params.data());
260         };
261         ScrollModel::GetInstance()->SetOnDidScroll(std::move(onScroll));
262     }
263 }
264 
OnScrollEdgeCallback(const JSCallbackInfo & args)265 void JSScroll::OnScrollEdgeCallback(const JSCallbackInfo& args)
266 {
267     if (args[0]->IsFunction()) {
268         auto scrollEdge = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
269                               const NG::ScrollEdge& side) {
270             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
271             auto params = ConvertToJSValues(side);
272             func->Call(JSRef<JSObject>(), 1, params.data());
273 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
274             UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "onScrollEdge");
275 #endif
276         };
277         ScrollModel::GetInstance()->SetOnScrollEdge(std::move(scrollEdge));
278     }
279     args.SetReturnValue(args.This());
280 }
281 
OnScrollEndCallback(const JSCallbackInfo & args)282 void JSScroll::OnScrollEndCallback(const JSCallbackInfo& args)
283 {
284     if (args[0]->IsFunction()) {
285         auto scrollEnd = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
286             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
287             func->Call(JSRef<JSObject>(), 0, nullptr);
288 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
289             UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "onScrollEnd");
290 #endif
291         };
292         ScrollModel::GetInstance()->SetOnScrollEnd(std::move(scrollEnd));
293     }
294     args.SetReturnValue(args.This());
295 }
296 
OnScrollStartCallback(const JSCallbackInfo & args)297 void JSScroll::OnScrollStartCallback(const JSCallbackInfo& args)
298 {
299     if (args[0]->IsFunction()) {
300         auto scrollStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
301             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
302             func->Call(JSRef<JSObject>(), 0, nullptr);
303         };
304         ScrollModel::GetInstance()->SetOnScrollStart(std::move(scrollStart));
305     }
306     args.SetReturnValue(args.This());
307 }
308 
OnScrollStopCallback(const JSCallbackInfo & args)309 void JSScroll::OnScrollStopCallback(const JSCallbackInfo& args)
310 {
311     if (args[0]->IsFunction()) {
312         auto scrollStop = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
313             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
314             func->Call(JSRef<JSObject>(), 0, nullptr);
315 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
316             UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "onScrollStop");
317 #endif
318         };
319         ScrollModel::GetInstance()->SetOnScrollStop(std::move(scrollStop));
320     }
321     args.SetReturnValue(args.This());
322 }
323 
ReachStartCallback(const JSCallbackInfo & args)324 void JSScroll::ReachStartCallback(const JSCallbackInfo& args)
325 {
326     if (args[0]->IsFunction()) {
327         auto onReachStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
328             func->Call(JSRef<JSObject>());
329             return;
330         };
331         ScrollModel::GetInstance()->SetOnReachStart(std::move(onReachStart));
332     }
333     args.ReturnSelf();
334 }
335 
ReachEndCallback(const JSCallbackInfo & args)336 void JSScroll::ReachEndCallback(const JSCallbackInfo& args)
337 {
338     if (args[0]->IsFunction()) {
339         auto onReachEnd = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
340             func->Call(JSRef<JSObject>());
341             return;
342         };
343         ScrollModel::GetInstance()->SetOnReachEnd(std::move(onReachEnd));
344     }
345     args.ReturnSelf();
346 }
347 
JSBind(BindingTarget globalObj)348 void JSScroll::JSBind(BindingTarget globalObj)
349 {
350     JSClass<JSScroll>::Declare("Scroll");
351     MethodOptions opt = MethodOptions::NONE;
352     JSClass<JSScroll>::StaticMethod("create", &JSScroll::Create, opt);
353     JSClass<JSScroll>::StaticMethod("scrollable", &JSScroll::SetScrollable, opt);
354     JSClass<JSScroll>::StaticMethod("onScrollBegin", &JSScroll::OnScrollBeginCallback, opt);
355     JSClass<JSScroll>::StaticMethod("onScrollFrameBegin", &JSScroll::OnScrollFrameBeginCallback, opt);
356     JSClass<JSScroll>::StaticMethod("onScroll", &JSScroll::OnScrollCallback, opt);
357     JSClass<JSScroll>::StaticMethod("onWillScroll", &JSScroll::OnWillScrollCallback, opt);
358     JSClass<JSScroll>::StaticMethod("onDidScroll", &JSScroll::OnDidScrollCallback, opt);
359     JSClass<JSScroll>::StaticMethod("onScrollEdge", &JSScroll::OnScrollEdgeCallback, opt);
360     JSClass<JSScroll>::StaticMethod("onScrollEnd", &JSScroll::OnScrollEndCallback, opt);
361     JSClass<JSScroll>::StaticMethod("onScrollStart", &JSScroll::OnScrollStartCallback, opt);
362     JSClass<JSScroll>::StaticMethod("onScrollStop", &JSScroll::OnScrollStopCallback, opt);
363     JSClass<JSScroll>::StaticMethod("onReachStart", &JSScroll::ReachStartCallback);
364     JSClass<JSScroll>::StaticMethod("onReachEnd", &JSScroll::ReachEndCallback);
365     JSClass<JSScroll>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
366     JSClass<JSScroll>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
367     JSClass<JSScroll>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
368     JSClass<JSScroll>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
369     JSClass<JSScroll>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
370     JSClass<JSScroll>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
371     JSClass<JSScroll>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
372     JSClass<JSScroll>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
373     JSClass<JSScroll>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
374     JSClass<JSScroll>::StaticMethod("edgeEffect", &JSScroll::SetEdgeEffect, opt);
375     JSClass<JSScroll>::StaticMethod("scrollBar", &JSScroll::SetScrollBar, opt);
376     JSClass<JSScroll>::StaticMethod("scrollBarColor", &JSScroll::SetScrollBarColor, opt);
377     JSClass<JSScroll>::StaticMethod("scrollBarWidth", &JSScroll::SetScrollBarWidth, opt);
378     JSClass<JSScroll>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
379     JSClass<JSScroll>::StaticMethod("width", &JSScroll::JsWidth);
380     JSClass<JSScroll>::StaticMethod("height", &JSScroll::JsHeight);
381     JSClass<JSScroll>::StaticMethod("nestedScroll", &JSScroll::SetNestedScroll);
382     JSClass<JSScroll>::StaticMethod("enableScrollInteraction", &JSScroll::SetScrollEnabled);
383     JSClass<JSScroll>::StaticMethod("friction", &JSScroll::SetFriction);
384     JSClass<JSScroll>::StaticMethod("scrollSnap", &JSScroll::SetScrollSnap);
385     JSClass<JSScroll>::StaticMethod("enablePaging", &JSScroll::SetEnablePaging);
386     JSClass<JSScroll>::StaticMethod("clip", &JSScrollable::JsClip);
387     JSClass<JSScroll>::StaticMethod("initialOffset", &JSScroll::SetInitialOffset);
388     JSClass<JSScroll>::InheritAndBind<JSScrollableBase>(globalObj);
389 }
390 
SetScrollBar(const JSCallbackInfo & args)391 void JSScroll::SetScrollBar(const JSCallbackInfo& args)
392 {
393     if (args.Length() < 1) {
394         return;
395     }
396     int32_t displayMode;
397     if (args[0]->IsNull() || args[0]->IsUndefined() || !ParseJsInt32(args[0], displayMode)) {
398         displayMode = static_cast<int32_t>(DisplayMode::AUTO);
399     }
400     ScrollModel::GetInstance()->SetDisplayMode(displayMode);
401 }
402 
SetScrollBarWidth(const JSCallbackInfo & args)403 void JSScroll::SetScrollBarWidth(const JSCallbackInfo& args)
404 {
405     auto pipelineContext = PipelineContext::GetCurrentContext();
406     CHECK_NULL_VOID(pipelineContext);
407     auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
408     CHECK_NULL_VOID(theme);
409     CalcDimension scrollBarWidth;
410     if (args.Length() < 1) {
411         return;
412     }
413     if (!ParseJsDimensionVp(args[0], scrollBarWidth) || args[0]->IsNull() || args[0]->IsUndefined() ||
414         (args[0]->IsString() && args[0]->ToString().empty()) || LessNotEqual(scrollBarWidth.Value(), 0.0) ||
415         scrollBarWidth.Unit() == DimensionUnit::PERCENT) {
416         scrollBarWidth = theme->GetNormalWidth();
417     }
418     ScrollModel::GetInstance()->SetScrollBarWidth(scrollBarWidth);
419 }
420 
SetScrollBarColor(const JSCallbackInfo & args)421 void JSScroll::SetScrollBarColor(const JSCallbackInfo& args)
422 {
423     auto pipelineContext = PipelineContext::GetCurrentContext();
424     CHECK_NULL_VOID(pipelineContext);
425     auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
426     CHECK_NULL_VOID(theme);
427     Color color(theme->GetForegroundColor());
428     JSViewAbstract::ParseJsColor(args[0], color);
429     ScrollModel::GetInstance()->SetScrollBarColor(color);
430 }
431 
SetEdgeEffect(const JSCallbackInfo & args)432 void JSScroll::SetEdgeEffect(const JSCallbackInfo& args)
433 {
434     auto edgeEffect = EdgeEffect::NONE;
435     if (args.Length() > 0) {
436         edgeEffect = JSScrollable::ParseEdgeEffect(args[0], EdgeEffect::NONE);
437     }
438     auto alwaysEnabled = true;
439     if (args.Length() > 1) {
440         alwaysEnabled = JSScrollable::ParseAlwaysEnable(args[1], true);
441     }
442     ScrollModel::GetInstance()->SetEdgeEffect(edgeEffect, alwaysEnabled);
443 }
444 
JsWidth(const JSCallbackInfo & info)445 void JSScroll::JsWidth(const JSCallbackInfo& info)
446 {
447     JSViewAbstract::JsWidth(info);
448     ScrollModel::GetInstance()->SetHasWidth(true);
449 }
450 
JsHeight(const JSCallbackInfo & info)451 void JSScroll::JsHeight(const JSCallbackInfo& info)
452 {
453     JSViewAbstract::JsHeight(info);
454     ScrollModel::GetInstance()->SetHasHeight(true);
455 }
456 
SetNestedScroll(const JSCallbackInfo & args)457 void JSScroll::SetNestedScroll(const JSCallbackInfo& args)
458 {
459     NestedScrollOptions nestedOpt = {
460         .forward = NestedScrollMode::SELF_ONLY,
461         .backward = NestedScrollMode::SELF_ONLY,
462     };
463     if (args.Length() < 1 || !args[0]->IsObject()) {
464         ScrollModel::GetInstance()->SetNestedScroll(nestedOpt);
465         return;
466     }
467     JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
468     int32_t froward = 0;
469     JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollForward"), froward);
470     if (froward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
471         froward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
472         froward = 0;
473     }
474     int32_t backward = 0;
475     JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollBackward"), backward);
476     if (backward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
477         backward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
478         backward = 0;
479     }
480     nestedOpt.forward = static_cast<NestedScrollMode>(froward);
481     nestedOpt.backward = static_cast<NestedScrollMode>(backward);
482     ScrollModel::GetInstance()->SetNestedScroll(nestedOpt);
483     args.ReturnSelf();
484 }
485 
SetFriction(const JSCallbackInfo & info)486 void JSScroll::SetFriction(const JSCallbackInfo& info)
487 {
488     double friction = -1.0;
489     if (!JSViewAbstract::ParseJsDouble(info[0], friction)) {
490         friction = -1.0;
491     }
492     ScrollModel::GetInstance()->SetFriction(friction);
493 }
494 
SetScrollSnap(const JSCallbackInfo & args)495 void JSScroll::SetScrollSnap(const JSCallbackInfo& args)
496 {
497     if (args.Length() < 1 || !args[0]->IsObject()) {
498         return;
499     }
500     JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
501     auto snapAlignValue = obj->GetProperty("snapAlign");
502     int32_t snapAlign = static_cast<int32_t>(ScrollSnapAlign::NONE);
503     if (snapAlignValue->IsNull() || snapAlignValue->IsUndefined() || !ParseJsInt32(snapAlignValue, snapAlign) ||
504         snapAlign < static_cast<int32_t>(ScrollSnapAlign::NONE) ||
505         snapAlign > static_cast<int32_t>(ScrollSnapAlign::END)) {
506         snapAlign = static_cast<int32_t>(ScrollSnapAlign::NONE);
507     }
508 
509     auto paginationValue = obj->GetProperty("snapPagination");
510     CalcDimension intervalSize;
511     std::vector<Dimension> snapPaginations;
512     if (!ParseJsDimensionVp(paginationValue, intervalSize) || intervalSize.IsNegative()) {
513         intervalSize = CalcDimension(0.0);
514     }
515     if (!ParseJsDimensionArray(paginationValue, snapPaginations) || !CheckSnapPaginations(snapPaginations)) {
516         std::vector<Dimension>().swap(snapPaginations);
517     }
518 
519     bool enableSnapToStart = true;
520     bool enableSnapToEnd = true;
521     ParseJsBool(obj->GetProperty("enableSnapToStart"), enableSnapToStart);
522     ParseJsBool(obj->GetProperty("enableSnapToEnd"), enableSnapToEnd);
523     std::pair<bool, bool> enableSnapToSide = { enableSnapToStart, enableSnapToEnd };
524     ScrollModel::GetInstance()->SetScrollSnap(
525         static_cast<ScrollSnapAlign>(snapAlign), intervalSize, snapPaginations, enableSnapToSide);
526 }
527 
SetEnablePaging(const JSCallbackInfo & args)528 void JSScroll::SetEnablePaging(const JSCallbackInfo& args)
529 {
530     if (args.Length() < 1 || !args[0]->IsBoolean()) {
531         return;
532     }
533     ScrollModel::GetInstance()->SetEnablePaging(args[0]->ToBoolean());
534 }
535 
SetInitialOffset(const JSCallbackInfo & args)536 void JSScroll::SetInitialOffset(const JSCallbackInfo& args)
537 {
538     if (args.Length() < 1 || !args[0]->IsObject()) {
539         return;
540     }
541 
542     JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
543     CalcDimension xOffset;
544     ParseJsDimensionVp(obj->GetProperty("xOffset"), xOffset);
545     CalcDimension yOffset;
546     ParseJsDimensionVp(obj->GetProperty("yOffset"), yOffset);
547     ScrollModel::GetInstance()->SetInitialOffset(NG::OffsetT(xOffset, yOffset));
548 }
549 } // namespace OHOS::Ace::Framework
550