1 /*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "bridge/declarative_frontend/jsview/js_water_flow.h"
17
18 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
19 #include "bridge/declarative_frontend/jsview/js_scrollable.h"
20 #include "bridge/declarative_frontend/jsview/js_scroller.h"
21 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
22 #include "bridge/declarative_frontend/jsview/models/water_flow_model_impl.h"
23 #include "bridge/declarative_frontend/view_stack_processor.h"
24 #include "core/common/container.h"
25 #include "core/components_ng/pattern/waterflow/water_flow_model_ng.h"
26
27 namespace OHOS::Ace {
28 std::unique_ptr<WaterFlowModel> WaterFlowModel::instance_ = nullptr;
29 std::mutex WaterFlowModel::mutex_;
30
GetInstance()31 WaterFlowModel* WaterFlowModel::GetInstance()
32 {
33 if (!instance_) {
34 std::lock_guard<std::mutex> lock(mutex_);
35 if (!instance_) {
36 #ifdef NG_BUILD
37 instance_.reset(new NG::WaterFlowModelNG());
38 #else
39 if (Container::IsCurrentUseNewPipeline()) {
40 instance_.reset(new NG::WaterFlowModelNG());
41 } else {
42 instance_.reset(new Framework::WaterFlowModelImpl());
43 }
44 #endif
45 }
46 }
47 return instance_.get();
48 }
49 } // namespace OHOS::Ace
50 namespace OHOS::Ace::Framework {
51 namespace {
52 const std::vector<FlexDirection> LAYOUT_DIRECTION = { FlexDirection::ROW, FlexDirection::COLUMN,
53 FlexDirection::ROW_REVERSE, FlexDirection::COLUMN_REVERSE };
54 } // namespace
55
Create(const JSCallbackInfo & args)56 void JSWaterFlow::Create(const JSCallbackInfo& args)
57 {
58 if (args.Length() > 1) {
59 LOGW("Arg is wrong, it is supposed to have at most one argument");
60 return;
61 }
62
63 WaterFlowModel::GetInstance()->Create();
64
65 if (args.Length() == 1) {
66 if (!args[0]->IsObject()) {
67 LOGE("The arg must be object");
68 return;
69 }
70 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
71 auto footerObject = obj->GetProperty("footer");
72 if (footerObject->IsFunction()) {
73 auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(footerObject));
74 auto footerAction = [builderFunc]() { builderFunc->Execute(); };
75 WaterFlowModel::GetInstance()->SetFooter(footerAction);
76 }
77 auto scroller = obj->GetProperty("scroller");
78 if (scroller->IsObject()) {
79 auto* jsScroller = JSRef<JSObject>::Cast(scroller)->Unwrap<JSScroller>();
80 CHECK_NULL_VOID(jsScroller);
81 jsScroller->SetInstanceId(Container::CurrentId());
82 auto positionController = WaterFlowModel::GetInstance()->CreateScrollController();
83 jsScroller->SetController(positionController);
84
85 // Init scroll bar proxy.
86 auto proxy = jsScroller->GetScrollBarProxy();
87 if (!proxy) {
88 proxy = WaterFlowModel::GetInstance()->CreateScrollBarProxy();
89 jsScroller->SetScrollBarProxy(proxy);
90 }
91 WaterFlowModel::GetInstance()->SetScroller(positionController, proxy);
92 }
93 }
94 }
95
JSBind(BindingTarget globalObj)96 void JSWaterFlow::JSBind(BindingTarget globalObj)
97 {
98 JSClass<JSWaterFlow>::Declare("WaterFlow");
99
100 MethodOptions opt = MethodOptions::NONE;
101 JSClass<JSWaterFlow>::StaticMethod("create", &JSWaterFlow::Create, opt);
102 JSClass<JSWaterFlow>::StaticMethod("columnsGap", &JSWaterFlow::SetColumnsGap, opt);
103 JSClass<JSWaterFlow>::StaticMethod("rowsGap", &JSWaterFlow::SetRowsGap, opt);
104 JSClass<JSWaterFlow>::StaticMethod("layoutDirection", &JSWaterFlow::SetLayoutDirection, opt);
105 JSClass<JSWaterFlow>::StaticMethod("columnsTemplate", &JSWaterFlow::SetColumnsTemplate, opt);
106 JSClass<JSWaterFlow>::StaticMethod("itemConstraintSize", &JSWaterFlow::SetItemConstraintSize, opt);
107 JSClass<JSWaterFlow>::StaticMethod("rowsTemplate", &JSWaterFlow::SetRowsTemplate, opt);
108 JSClass<JSWaterFlow>::StaticMethod("nestedScroll", &JSWaterFlow::SetNestedScroll);
109 JSClass<JSWaterFlow>::StaticMethod("enableScrollInteraction", &JSWaterFlow::SetScrollEnabled);
110 JSClass<JSWaterFlow>::StaticMethod("onReachStart", &JSWaterFlow::ReachStartCallback);
111 JSClass<JSWaterFlow>::StaticMethod("onReachEnd", &JSWaterFlow::ReachEndCallback);
112 JSClass<JSWaterFlow>::StaticMethod("onScrollFrameBegin", &JSWaterFlow::ScrollFrameBeginCallback);
113 JSClass<JSWaterFlow>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
114 JSClass<JSWaterFlow>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
115 JSClass<JSWaterFlow>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
116 JSClass<JSWaterFlow>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
117 JSClass<JSWaterFlow>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
118 JSClass<JSWaterFlow>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
119 JSClass<JSWaterFlow>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
120 JSClass<JSWaterFlow>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
121 JSClass<JSWaterFlow>::StaticMethod("friction", &JSWaterFlow::SetFriction);
122 JSClass<JSWaterFlow>::StaticMethod("clip", &JSScrollable::JsClip);
123 JSClass<JSWaterFlow>::StaticMethod("cachedCount", &JSWaterFlow::SetCachedCount);
124 JSClass<JSWaterFlow>::StaticMethod("edgeEffect", &JSWaterFlow::SetEdgeEffect);
125
126 JSClass<JSWaterFlow>::StaticMethod("onScroll", &JSWaterFlow::JsOnScroll);
127 JSClass<JSWaterFlow>::StaticMethod("onScrollStart", &JSWaterFlow::JsOnScrollStart);
128 JSClass<JSWaterFlow>::StaticMethod("onScrollStop", &JSWaterFlow::JsOnScrollStop);
129 JSClass<JSWaterFlow>::StaticMethod("onScrollIndex", &JSWaterFlow::JsOnScrollIndex);
130
131 JSClass<JSWaterFlow>::StaticMethod("scrollBar", &JSWaterFlow::SetScrollBar, opt);
132 JSClass<JSWaterFlow>::StaticMethod("scrollBarWidth", &JSWaterFlow::SetScrollBarWidth, opt);
133 JSClass<JSWaterFlow>::StaticMethod("scrollBarColor", &JSWaterFlow::SetScrollBarColor, opt);
134
135 JSClass<JSWaterFlow>::InheritAndBind<JSScrollableBase>(globalObj);
136 }
137
SetColumnsGap(const JSCallbackInfo & info)138 void JSWaterFlow::SetColumnsGap(const JSCallbackInfo& info)
139 {
140 if (info.Length() < 1) {
141 return;
142 }
143 CalcDimension colGap;
144 if (!ParseJsDimensionVp(info[0], colGap) || colGap.Value() < 0) {
145 colGap.SetValue(0.0);
146 }
147 WaterFlowModel::GetInstance()->SetColumnsGap(colGap);
148 }
149
SetRowsGap(const JSCallbackInfo & info)150 void JSWaterFlow::SetRowsGap(const JSCallbackInfo& info)
151 {
152 if (info.Length() < 1) {
153 return;
154 }
155 CalcDimension rowGap;
156 if (!ParseJsDimensionVp(info[0], rowGap) || rowGap.Value() < 0) {
157 rowGap.SetValue(0.0);
158 }
159 WaterFlowModel::GetInstance()->SetRowsGap(rowGap);
160 }
161
SetLayoutDirection(const JSCallbackInfo & info)162 void JSWaterFlow::SetLayoutDirection(const JSCallbackInfo& info)
163 {
164 if (info.Length() < 1) {
165 return;
166 }
167 auto value = static_cast<int32_t>(FlexDirection::COLUMN);
168 auto jsValue = info[0];
169 if (!jsValue->IsUndefined()) {
170 ParseJsInteger<int32_t>(jsValue, value);
171 }
172 if (value >= 0 && value < static_cast<int32_t>(LAYOUT_DIRECTION.size())) {
173 WaterFlowModel::GetInstance()->SetLayoutDirection(LAYOUT_DIRECTION[value]);
174 } else {
175 WaterFlowModel::GetInstance()->SetLayoutDirection(FlexDirection::COLUMN);
176 }
177 }
178
SetColumnsTemplate(const std::string & value)179 void JSWaterFlow::SetColumnsTemplate(const std::string& value)
180 {
181 WaterFlowModel::GetInstance()->SetColumnsTemplate(value);
182 }
183
SetItemConstraintSize(const JSCallbackInfo & info)184 void JSWaterFlow::SetItemConstraintSize(const JSCallbackInfo& info)
185 {
186 if (info.Length() < 1 || !info[0]->IsObject()) {
187 return;
188 }
189
190 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
191
192 JSRef<JSVal> minWidthValue = sizeObj->GetProperty("minWidth");
193 CalcDimension minWidth;
194 if (ParseJsDimensionVp(minWidthValue, minWidth)) {
195 WaterFlowModel::GetInstance()->SetItemMinWidth(minWidth);
196 }
197
198 JSRef<JSVal> maxWidthValue = sizeObj->GetProperty("maxWidth");
199 CalcDimension maxWidth;
200 if (ParseJsDimensionVp(maxWidthValue, maxWidth)) {
201 WaterFlowModel::GetInstance()->SetItemMaxWidth(maxWidth);
202 }
203
204 JSRef<JSVal> minHeightValue = sizeObj->GetProperty("minHeight");
205 CalcDimension minHeight;
206 if (ParseJsDimensionVp(minHeightValue, minHeight)) {
207 WaterFlowModel::GetInstance()->SetItemMinHeight(minHeight);
208 }
209
210 JSRef<JSVal> maxHeightValue = sizeObj->GetProperty("maxHeight");
211 CalcDimension maxHeight;
212 if (ParseJsDimensionVp(maxHeightValue, maxHeight)) {
213 WaterFlowModel::GetInstance()->SetItemMaxHeight(maxHeight);
214 }
215 }
216
SetRowsTemplate(const std::string & value)217 void JSWaterFlow::SetRowsTemplate(const std::string& value)
218 {
219 WaterFlowModel::GetInstance()->SetRowsTemplate(value);
220 }
221
SetNestedScroll(const JSCallbackInfo & args)222 void JSWaterFlow::SetNestedScroll(const JSCallbackInfo& args)
223 {
224 NestedScrollOptions nestedOpt = {
225 .forward = NestedScrollMode::SELF_ONLY,
226 .backward = NestedScrollMode::SELF_ONLY,
227 };
228 if (args.Length() < 1 || !args[0]->IsObject()) {
229 WaterFlowModel::GetInstance()->SetNestedScroll(nestedOpt);
230 LOGW("Invalid params");
231 return;
232 }
233 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
234 int32_t froward = 0;
235 JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollForward"), froward);
236 if (froward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
237 froward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
238 LOGW("ScrollFroward params invalid");
239 froward = 0;
240 }
241 int32_t backward = 0;
242 JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollBackward"), backward);
243 if (backward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
244 backward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
245 LOGW("ScrollFroward params invalid");
246 backward = 0;
247 }
248 nestedOpt.forward = static_cast<NestedScrollMode>(froward);
249 nestedOpt.backward = static_cast<NestedScrollMode>(backward);
250 WaterFlowModel::GetInstance()->SetNestedScroll(nestedOpt);
251 args.ReturnSelf();
252 }
253
SetScrollEnabled(const JSCallbackInfo & args)254 void JSWaterFlow::SetScrollEnabled(const JSCallbackInfo& args)
255 {
256 WaterFlowModel::GetInstance()->SetScrollEnabled(args[0]->IsBoolean() ? args[0]->ToBoolean() : true);
257 }
258
SetFriction(const JSCallbackInfo & info)259 void JSWaterFlow::SetFriction(const JSCallbackInfo& info)
260 {
261 double friction = -1.0;
262 if (!JSViewAbstract::ParseJsDouble(info[0], friction)) {
263 LOGW("Friction params invalid,can not convert to double");
264 friction = -1.0;
265 }
266 WaterFlowModel::GetInstance()->SetFriction(friction);
267 }
268
ReachStartCallback(const JSCallbackInfo & args)269 void JSWaterFlow::ReachStartCallback(const JSCallbackInfo& args)
270 {
271 if (args[0]->IsFunction()) {
272 auto onReachStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
273 func->Call(JSRef<JSObject>());
274 return;
275 };
276 WaterFlowModel::GetInstance()->SetOnReachStart(std::move(onReachStart));
277 }
278 args.ReturnSelf();
279 }
280
ReachEndCallback(const JSCallbackInfo & args)281 void JSWaterFlow::ReachEndCallback(const JSCallbackInfo& args)
282 {
283 if (args[0]->IsFunction()) {
284 auto onReachEnd = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
285 func->Call(JSRef<JSObject>());
286 return;
287 };
288 WaterFlowModel::GetInstance()->SetOnReachEnd(std::move(onReachEnd));
289 }
290 args.ReturnSelf();
291 }
292
ScrollFrameBeginCallback(const JSCallbackInfo & args)293 void JSWaterFlow::ScrollFrameBeginCallback(const JSCallbackInfo& args)
294 {
295 if (args[0]->IsFunction()) {
296 auto onScrollBegin = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
297 const Dimension& offset, const ScrollState& state) -> ScrollFrameResult {
298 ScrollFrameResult scrollRes { .offset = offset };
299 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollRes);
300 auto params = ConvertToJSValues(offset, state);
301 auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
302 if (result.IsEmpty()) {
303 LOGE("Error calling onScrollFrameBegin, result is empty.");
304 return scrollRes;
305 }
306
307 if (!result->IsObject()) {
308 LOGE("Error calling onScrollFrameBegin, result is not object.");
309 return scrollRes;
310 }
311
312 auto resObj = JSRef<JSObject>::Cast(result);
313 auto dxRemainValue = resObj->GetProperty("offsetRemain");
314 if (dxRemainValue->IsNumber()) {
315 scrollRes.offset = Dimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
316 }
317 return scrollRes;
318 };
319 WaterFlowModel::GetInstance()->SetOnScrollFrameBegin(std::move(onScrollBegin));
320 }
321 }
322
SetCachedCount(const JSCallbackInfo & info)323 void JSWaterFlow::SetCachedCount(const JSCallbackInfo& info)
324 {
325 int32_t cachedCount = 1;
326 auto jsValue = info[0];
327
328 if (!jsValue->IsUndefined() && jsValue->IsNumber()) {
329 ParseJsInt32(jsValue, cachedCount);
330 if (cachedCount < 0) {
331 cachedCount = 1;
332 }
333 }
334
335 WaterFlowModel::GetInstance()->SetCachedCount(cachedCount);
336 }
337
SetEdgeEffect(const JSCallbackInfo & info)338 void JSWaterFlow::SetEdgeEffect(const JSCallbackInfo& info)
339 {
340 auto edgeEffect = JSScrollable::ParseEdgeEffect(info, WaterFlowModel::GetInstance()->GetEdgeEffect());
341 auto alwaysEnabled =
342 JSScrollable::ParseAlwaysEnable(info, WaterFlowModel::GetInstance()->GetAlwaysEnableEdgeEffect());
343 WaterFlowModel::GetInstance()->SetEdgeEffect(edgeEffect, alwaysEnabled);
344 }
345
JsOnScroll(const JSCallbackInfo & args)346 void JSWaterFlow::JsOnScroll(const JSCallbackInfo& args)
347 {
348 if (args[0]->IsFunction()) {
349 auto onScroll = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
350 const CalcDimension& scrollOffset, const ScrollState& scrollState) {
351 auto params = ConvertToJSValues(scrollOffset, scrollState);
352 func->Call(JSRef<JSObject>(), params.size(), params.data());
353 return;
354 };
355 WaterFlowModel::GetInstance()->SetOnScroll(std::move(onScroll));
356 }
357 args.ReturnSelf();
358 }
359
JsOnScrollStart(const JSCallbackInfo & args)360 void JSWaterFlow::JsOnScrollStart(const JSCallbackInfo& args)
361 {
362 if (args[0]->IsFunction()) {
363 auto onScrollStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
364 func->Call(JSRef<JSObject>());
365 return;
366 };
367 WaterFlowModel::GetInstance()->SetOnScrollStart(std::move(onScrollStart));
368 }
369 args.ReturnSelf();
370 }
371
JsOnScrollStop(const JSCallbackInfo & args)372 void JSWaterFlow::JsOnScrollStop(const JSCallbackInfo& args)
373 {
374 if (args[0]->IsFunction()) {
375 auto onScrollStop = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
376 func->Call(JSRef<JSObject>());
377 return;
378 };
379 WaterFlowModel::GetInstance()->SetOnScrollStop(std::move(onScrollStop));
380 }
381 args.ReturnSelf();
382 }
383
JsOnScrollIndex(const JSCallbackInfo & args)384 void JSWaterFlow::JsOnScrollIndex(const JSCallbackInfo& args)
385 {
386 if (args[0]->IsFunction()) {
387 auto onScrollIndex = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
388 const int32_t first, const int32_t last) {
389 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
390 auto params = ConvertToJSValues(first, last);
391 func->Call(JSRef<JSObject>(), params.size(), params.data());
392 return;
393 };
394 WaterFlowModel::GetInstance()->SetOnScrollIndex(std::move(onScrollIndex));
395 }
396 args.ReturnSelf();
397 }
398
SetScrollBar(const JSCallbackInfo & info)399 void JSWaterFlow::SetScrollBar(const JSCallbackInfo& info)
400 {
401 auto displayMode = JSScrollable::ParseDisplayMode(info, WaterFlowModel::GetDisplayMode());
402 WaterFlowModel::GetInstance()->SetScrollBarMode(displayMode);
403 }
404
SetScrollBarColor(const std::string & color)405 void JSWaterFlow::SetScrollBarColor(const std::string& color)
406 {
407 auto scrollBarColor = JSScrollable::ParseBarColor(color);
408 if (!scrollBarColor.empty()) {
409 WaterFlowModel::GetInstance()->SetScrollBarColor(scrollBarColor);
410 }
411 }
412
SetScrollBarWidth(const JSCallbackInfo & scrollWidth)413 void JSWaterFlow::SetScrollBarWidth(const JSCallbackInfo& scrollWidth)
414 {
415 auto scrollBarWidth = JSScrollable::ParseBarWidth(scrollWidth);
416 if (!scrollBarWidth.empty()) {
417 WaterFlowModel::GetInstance()->SetScrollBarWidth(scrollBarWidth);
418 }
419 }
420 } // namespace OHOS::Ace::Framework
421