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 "bridge/declarative_frontend/jsview/js_scroll.h"
17
18 #include "base/utils/utils.h"
19 #include "bridge/declarative_frontend/jsview/js_scroller.h"
20 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
21 #include "bridge/declarative_frontend/jsview/models/scroll_model_impl.h"
22 #include "core/components/common/layout/constants.h"
23 #include "core/components/scroll/scrollable.h"
24 #include "core/components_ng/pattern/scroll/inner/scroll_bar.h"
25 #include "core/components_ng/pattern/scroll/scroll_model.h"
26 #include "core/components_ng/pattern/scroll/scroll_model_ng.h"
27
28 namespace OHOS::Ace {
29
30 std::unique_ptr<ScrollModel> ScrollModel::instance_ = nullptr;
31
GetInstance()32 ScrollModel* ScrollModel::GetInstance()
33 {
34 if (!instance_) {
35 #ifdef NG_BUILD
36 instance_.reset(new NG::ScrollModelNG());
37 #else
38 if (Container::IsCurrentUseNewPipeline()) {
39 instance_.reset(new NG::ScrollModelNG());
40 } else {
41 instance_.reset(new Framework::ScrollModelImpl());
42 }
43 #endif
44 }
45 return instance_.get();
46 }
47
48 } // namespace OHOS::Ace
49
50 namespace OHOS::Ace::Framework {
51 namespace {
52 const std::vector<Axis> AXIS = { Axis::VERTICAL, Axis::HORIZONTAL, Axis::FREE, Axis::NONE };
53 } // namespace
54
Create(const JSCallbackInfo & info)55 void JSScroll::Create(const JSCallbackInfo& info)
56 {
57 ScrollModel::GetInstance()->Create();
58 if (info.Length() > 0 && info[0]->IsObject()) {
59 JSScroller* jsScroller = JSRef<JSObject>::Cast(info[0])->Unwrap<JSScroller>();
60 if (jsScroller) {
61 auto positionController = ScrollModel::GetInstance()->GetOrCreateController();
62 jsScroller->SetController(positionController);
63 // Init scroll bar proxy.
64 auto proxy = jsScroller->GetScrollBarProxy();
65 if (!proxy) {
66 proxy = ScrollModel::GetInstance()->CreateScrollBarProxy();
67 jsScroller->SetScrollBarProxy(proxy);
68 }
69 ScrollModel::GetInstance()->SetScrollBarProxy(proxy);
70 }
71 }
72 // init scroll bar
73 std::pair<bool, Color> barColor;
74 barColor.first = false;
75 std::pair<bool, Dimension> barWidth;
76 barWidth.first = false;
77 ScrollModel::GetInstance()->InitScrollBar(GetTheme<ScrollBarTheme>(), barColor, barWidth, EdgeEffect::NONE);
78 }
79
SetScrollable(int32_t value)80 void JSScroll::SetScrollable(int32_t value)
81 {
82 if (value < 0 || value >= static_cast<int32_t>(AXIS.size())) {
83 LOGE("value is not valid: %{public}d", value);
84 return;
85 }
86 ScrollModel::GetInstance()->SetAxis(AXIS[value]);
87 }
88
OnScrollBeginCallback(const JSCallbackInfo & args)89 void JSScroll::OnScrollBeginCallback(const JSCallbackInfo& args)
90 {
91 if (args[0]->IsFunction()) {
92 auto onScrollBegin = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
93 const Dimension& dx, const Dimension& dy) -> ScrollInfo {
94 ScrollInfo scrollInfo { .dx = dx, .dy = dy };
95 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollInfo);
96 auto params = ConvertToJSValues(dx, dy);
97 auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
98 if (result.IsEmpty()) {
99 LOGE("Error calling onScrollBegin, result is empty.");
100 return scrollInfo;
101 }
102
103 if (!result->IsObject()) {
104 LOGE("Error calling onScrollBegin, result is not object.");
105 return scrollInfo;
106 }
107
108 auto resObj = JSRef<JSObject>::Cast(result);
109 auto dxRemainValue = resObj->GetProperty("dxRemain");
110 if (dxRemainValue->IsNumber()) {
111 scrollInfo.dx = Dimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
112 }
113 auto dyRemainValue = resObj->GetProperty("dyRemain");
114 if (dyRemainValue->IsNumber()) {
115 scrollInfo.dy = Dimension(dyRemainValue->ToNumber<float>(), DimensionUnit::VP);
116 }
117 return scrollInfo;
118 };
119 ScrollModel::GetInstance()->SetOnScrollBegin(std::move(onScrollBegin));
120 }
121 args.SetReturnValue(args.This());
122 }
123
OnScrollFrameBeginCallback(const JSCallbackInfo & args)124 void JSScroll::OnScrollFrameBeginCallback(const JSCallbackInfo& args)
125 {
126 if (args[0]->IsFunction()) {
127 auto onScrollFrameBegin = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
128 const Dimension& offset, ScrollState state) -> ScrollFrameResult {
129 OHOS::Ace::ScrollFrameResult scrollRes { .offset = offset };
130 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollRes);
131 auto params = ConvertToJSValues(offset, state);
132 auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
133 if (result.IsEmpty()) {
134 LOGE("Error calling onScrollBegin, result is empty.");
135 return scrollRes;
136 }
137
138 if (!result->IsObject()) {
139 LOGE("Error calling onScrollBegin, result is not object.");
140 return scrollRes;
141 }
142
143 auto resObj = JSRef<JSObject>::Cast(result);
144 auto dxRemainValue = resObj->GetProperty("offsetRemain");
145 if (dxRemainValue->IsNumber()) {
146 scrollRes.offset = Dimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
147 }
148 return scrollRes;
149 };
150 ScrollModel::GetInstance()->SetOnScrollFrameBegin(std::move(onScrollFrameBegin));
151 }
152 args.SetReturnValue(args.This());
153 }
154
OnScrollCallback(const JSCallbackInfo & args)155 void JSScroll::OnScrollCallback(const JSCallbackInfo& args)
156 {
157 if (args[0]->IsFunction()) {
158 auto onScroll = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
159 const Dimension& xOffset, const Dimension& yOffset) {
160 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
161 auto params = ConvertToJSValues(xOffset, yOffset);
162 func->Call(JSRef<JSObject>(), params.size(), params.data());
163 };
164 ScrollModel::GetInstance()->SetOnScroll(std::move(onScroll));
165 }
166 args.SetReturnValue(args.This());
167 }
168
OnScrollEdgeCallback(const JSCallbackInfo & args)169 void JSScroll::OnScrollEdgeCallback(const JSCallbackInfo& args)
170 {
171 if (args[0]->IsFunction()) {
172 auto scrollEdge = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
173 const NG::ScrollEdge& side) {
174 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
175 auto params = ConvertToJSValues(side);
176 func->Call(JSRef<JSObject>(), 1, params.data());
177 };
178 ScrollModel::GetInstance()->SetOnScrollEdge(std::move(scrollEdge));
179 }
180 args.SetReturnValue(args.This());
181 }
182
OnScrollEndCallback(const JSCallbackInfo & args)183 void JSScroll::OnScrollEndCallback(const JSCallbackInfo& args)
184 {
185 if (args[0]->IsFunction()) {
186 auto scrollEnd = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
187 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
188 func->Call(JSRef<JSObject>(), 0, nullptr);
189 };
190 ScrollModel::GetInstance()->SetOnScrollEnd(std::move(scrollEnd));
191 }
192 args.SetReturnValue(args.This());
193 }
194
OnScrollStartCallback(const JSCallbackInfo & args)195 void JSScroll::OnScrollStartCallback(const JSCallbackInfo& args)
196 {
197 if (args[0]->IsFunction()) {
198 auto scrollStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
199 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
200 func->Call(JSRef<JSObject>(), 0, nullptr);
201 };
202 ScrollModel::GetInstance()->SetOnScrollStart(std::move(scrollStart));
203 }
204 args.SetReturnValue(args.This());
205 }
206
OnScrollStopCallback(const JSCallbackInfo & args)207 void JSScroll::OnScrollStopCallback(const JSCallbackInfo& args)
208 {
209 if (args[0]->IsFunction()) {
210 auto scrollStop = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
211 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
212 func->Call(JSRef<JSObject>(), 0, nullptr);
213 };
214 ScrollModel::GetInstance()->SetOnScrollStop(std::move(scrollStop));
215 }
216 args.SetReturnValue(args.This());
217 }
218
JSBind(BindingTarget globalObj)219 void JSScroll::JSBind(BindingTarget globalObj)
220 {
221 JSClass<JSScroll>::Declare("Scroll");
222 MethodOptions opt = MethodOptions::NONE;
223 JSClass<JSScroll>::StaticMethod("create", &JSScroll::Create, opt);
224 JSClass<JSScroll>::StaticMethod("scrollable", &JSScroll::SetScrollable, opt);
225 JSClass<JSScroll>::StaticMethod("onScrollBegin", &JSScroll::OnScrollBeginCallback, opt);
226 JSClass<JSScroll>::StaticMethod("onScrollFrameBegin", &JSScroll::OnScrollFrameBeginCallback, opt);
227 JSClass<JSScroll>::StaticMethod("onScroll", &JSScroll::OnScrollCallback, opt);
228 JSClass<JSScroll>::StaticMethod("onScrollEdge", &JSScroll::OnScrollEdgeCallback, opt);
229 JSClass<JSScroll>::StaticMethod("onScrollEnd", &JSScroll::OnScrollEndCallback, opt);
230 JSClass<JSScroll>::StaticMethod("onScrollStart", &JSScroll::OnScrollStartCallback, opt);
231 JSClass<JSScroll>::StaticMethod("onScrollStop", &JSScroll::OnScrollStopCallback, opt);
232 JSClass<JSScroll>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
233 JSClass<JSScroll>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
234 JSClass<JSScroll>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
235 JSClass<JSScroll>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
236 JSClass<JSScroll>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
237 JSClass<JSScroll>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
238 JSClass<JSScroll>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
239 JSClass<JSScroll>::StaticMethod("edgeEffect", &JSScroll::SetEdgeEffect, opt);
240 JSClass<JSScroll>::StaticMethod("scrollBar", &JSScroll::SetScrollBar, opt);
241 JSClass<JSScroll>::StaticMethod("scrollBarColor", &JSScroll::SetScrollBarColor, opt);
242 JSClass<JSScroll>::StaticMethod("scrollBarWidth", &JSScroll::SetScrollBarWidth, opt);
243 JSClass<JSScroll>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
244 JSClass<JSScroll>::StaticMethod("width", &JSScroll::JsWidth);
245 JSClass<JSScroll>::StaticMethod("height", &JSScroll::JsHeight);
246 JSClass<JSScroll>::Inherit<JSContainerBase>();
247 JSClass<JSScroll>::Inherit<JSViewAbstract>();
248 JSClass<JSScroll>::Bind<>(globalObj);
249 }
250
SetScrollBar(const JSCallbackInfo & args)251 void JSScroll::SetScrollBar(const JSCallbackInfo& args)
252 {
253 if (args.Length() < 1) {
254 LOGE("args is invalid");
255 return;
256 }
257 int32_t displayMode;
258 if (args[0]->IsNull() || args[0]->IsUndefined() || !ParseJsInt32(args[0], displayMode)) {
259 displayMode = static_cast<int32_t>(NG::DisplayMode::AUTO);
260 }
261 ScrollModel::GetInstance()->SetDisplayMode(displayMode);
262 }
263
SetScrollBarWidth(const JSCallbackInfo & args)264 void JSScroll::SetScrollBarWidth(const JSCallbackInfo& args)
265 {
266 auto pipelineContext = PipelineContext::GetCurrentContext();
267 CHECK_NULL_VOID_NOLOG(pipelineContext);
268 auto theme = pipelineContext->GetTheme<ScrollBarTheme>();
269 CHECK_NULL_VOID_NOLOG(theme);
270 Dimension scrollBarWidth;
271 if (args.Length() < 1) {
272 LOGE("args is invalid");
273 return;
274 }
275 if (!ParseJsDimensionVp(args[0], scrollBarWidth) || args[0]->IsNull() || args[0]->IsUndefined() ||
276 (args[0]->IsString() && args[0]->ToString().empty()) || LessNotEqual(scrollBarWidth.Value(), 0.0)) {
277 scrollBarWidth = theme->GetNormalWidth();
278 }
279 ScrollModel::GetInstance()->SetScrollBarWidth(scrollBarWidth);
280 }
281
SetScrollBarColor(const std::string & scrollBarColor)282 void JSScroll::SetScrollBarColor(const std::string& scrollBarColor)
283 {
284 if (scrollBarColor.empty()) {
285 return;
286 }
287 ScrollModel::GetInstance()->SetScrollBarColor(Color::FromString(scrollBarColor));
288 }
289
SetEdgeEffect(int edgeEffect)290 void JSScroll::SetEdgeEffect(int edgeEffect)
291 {
292 ScrollModel::GetInstance()->SetEdgeEffect(static_cast<EdgeEffect>(edgeEffect));
293 }
294
JsWidth(const JSCallbackInfo & info)295 void JSScroll::JsWidth(const JSCallbackInfo& info)
296 {
297 JSViewAbstract::JsWidth(info);
298 ScrollModel::GetInstance()->SetHasWidth(true);
299 }
300
JsHeight(const JSCallbackInfo & info)301 void JSScroll::JsHeight(const JSCallbackInfo& info)
302 {
303 JSViewAbstract::JsHeight(info);
304 ScrollModel::GetInstance()->SetHasHeight(true);
305 }
306
307 } // namespace OHOS::Ace::Framework
308