1 /*
2 * Copyright (c) 2022-2025 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 <cstdint>
19 #include <vector>
20
21 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
22
23 #include "bridge/declarative_frontend/jsview/js_scrollable.h"
24 #include "bridge/declarative_frontend/jsview/js_scroller.h"
25 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
26 #include "bridge/declarative_frontend/jsview/js_water_flow_sections.h"
27 #include "bridge/declarative_frontend/jsview/models/water_flow_model_impl.h"
28 #include "core/common/container.h"
29 #include "core/components_ng/pattern/waterflow/water_flow_model.h"
30 #include "core/components_ng/pattern/waterflow/water_flow_model_ng.h"
31 #include "core/components_ng/pattern/waterflow/water_flow_sections.h"
32
33 namespace OHOS::Ace {
34 std::unique_ptr<WaterFlowModel> WaterFlowModel::instance_ = nullptr;
35 std::mutex WaterFlowModel::mutex_;
36
GetInstance()37 WaterFlowModel* WaterFlowModel::GetInstance()
38 {
39 if (!instance_) {
40 std::lock_guard<std::mutex> lock(mutex_);
41 if (!instance_) {
42 #ifdef NG_BUILD
43 instance_.reset(new NG::WaterFlowModelNG());
44 #else
45 if (Container::IsCurrentUseNewPipeline()) {
46 instance_.reset(new NG::WaterFlowModelNG());
47 } else {
48 instance_.reset(new Framework::WaterFlowModelImpl());
49 }
50 #endif
51 }
52 }
53 return instance_.get();
54 }
55 } // namespace OHOS::Ace
56 namespace OHOS::Ace::Framework {
57 namespace {
58 const std::vector<FlexDirection> LAYOUT_DIRECTION = { FlexDirection::ROW, FlexDirection::COLUMN,
59 FlexDirection::ROW_REVERSE, FlexDirection::COLUMN_REVERSE };
60
61 namespace {
ParseChanges(const JSCallbackInfo & args,const JSRef<JSArray> & changeArray,RefPtr<NG::WaterFlowSections> & waterFlowSections)62 void ParseChanges(
63 const JSCallbackInfo& args, const JSRef<JSArray>& changeArray, RefPtr<NG::WaterFlowSections>& waterFlowSections)
64 {
65 auto length = changeArray->Length();
66 for (size_t i = 0; i < length; ++i) {
67 auto change = changeArray->GetValueAt(i);
68 if (!change->IsObject()) {
69 continue;
70 }
71 auto changeObject = JSRef<JSObject>::Cast(change);
72 auto sectionValue = changeObject->GetProperty("sections");
73 if (!sectionValue->IsArray()) {
74 continue;
75 }
76 auto sectionArray = JSRef<JSArray>::Cast(sectionValue);
77 auto sectionsCount = sectionArray->Length();
78 std::vector<NG::WaterFlowSections::Section> newSections;
79 for (size_t j = 0; j < sectionsCount; ++j) {
80 NG::WaterFlowSections::Section section;
81 auto newSection = sectionArray->GetValueAt(j);
82 if (JSWaterFlowSections::ParseSectionOptions(args, newSection, section)) {
83 newSections.emplace_back(section);
84 }
85 }
86 waterFlowSections->ChangeData(changeObject->GetProperty("start")->ToNumber<int32_t>(),
87 changeObject->GetProperty("deleteCount")->ToNumber<int32_t>(), newSections);
88 }
89 }
90
ParseSections(const JSCallbackInfo & args,const JSRef<JSArray> & sectionArray,RefPtr<NG::WaterFlowSections> & waterFlowSections)91 void ParseSections(
92 const JSCallbackInfo& args, const JSRef<JSArray>& sectionArray, RefPtr<NG::WaterFlowSections>& waterFlowSections)
93 {
94 auto length = sectionArray->Length();
95 std::vector<NG::WaterFlowSections::Section> newSections;
96 for (size_t j = 0; j < length; ++j) {
97 NG::WaterFlowSections::Section section;
98 auto newSection = sectionArray->GetValueAt(j);
99 if (JSWaterFlowSections::ParseSectionOptions(args, newSection, section)) {
100 newSections.emplace_back(section);
101 }
102 }
103 waterFlowSections->ChangeData(0, waterFlowSections->GetSectionInfo().size(), newSections);
104 }
105
ParseScroller(const JSRef<JSObject> & obj)106 void ParseScroller(const JSRef<JSObject>& obj)
107 {
108 auto scroller = obj->GetProperty("scroller");
109 if (scroller->IsObject()) {
110 auto* jsScroller = JSRef<JSObject>::Cast(scroller)->Unwrap<JSScroller>();
111 CHECK_NULL_VOID(jsScroller);
112 jsScroller->SetInstanceId(Container::CurrentId());
113 auto positionController = WaterFlowModel::GetInstance()->CreateScrollController();
114 jsScroller->SetController(positionController);
115
116 // Init scroll bar proxy.
117 auto proxy = jsScroller->GetScrollBarProxy();
118 if (!proxy) {
119 proxy = WaterFlowModel::GetInstance()->CreateScrollBarProxy();
120 jsScroller->SetScrollBarProxy(proxy);
121 }
122 WaterFlowModel::GetInstance()->SetScroller(positionController, proxy);
123 }
124 }
125 } // namespace
126 } // namespace
127
UpdateSections(const JSCallbackInfo & args,const JSRef<JSVal> & sections,RefPtr<NG::WaterFlowSections> & waterFlowSections)128 void UpdateSections(
129 const JSCallbackInfo& args, const JSRef<JSVal>& sections, RefPtr<NG::WaterFlowSections>& waterFlowSections)
130 {
131 CHECK_NULL_VOID(waterFlowSections);
132 auto sectionsObject = JSRef<JSObject>::Cast(sections);
133 auto changes = sectionsObject->GetProperty("changeArray");
134 CHECK_NULL_VOID(changes->IsArray());
135 auto changeArray = JSRef<JSArray>::Cast(changes);
136 ParseChanges(args, changeArray, waterFlowSections);
137
138 auto lengthFunc = sectionsObject->GetProperty("length");
139 CHECK_NULL_VOID(lengthFunc->IsFunction());
140 auto sectionLength = (JSRef<JSFunc>::Cast(lengthFunc))->Call(sectionsObject);
141 if (waterFlowSections->GetSectionInfo().size() != sectionLength->ToNumber<uint32_t>()) {
142 auto allSections = sectionsObject->GetProperty("sectionArray");
143 CHECK_NULL_VOID(allSections->IsArray());
144 ParseSections(args, JSRef<JSArray>::Cast(allSections), waterFlowSections);
145 }
146
147 auto clearFunc = sectionsObject->GetProperty("clearChanges");
148 CHECK_NULL_VOID(clearFunc->IsFunction());
149 auto func = JSRef<JSFunc>::Cast(clearFunc);
150 func->Call(sectionsObject);
151 }
152
UpdateWaterFlowSections(const JSCallbackInfo & args,const JSRef<JSVal> & sections)153 void UpdateWaterFlowSections(const JSCallbackInfo& args, const JSRef<JSVal>& sections)
154 {
155 auto waterFlowSections = WaterFlowModel::GetInstance()->GetOrCreateWaterFlowSections();
156 CHECK_NULL_VOID(waterFlowSections);
157 UpdateSections(args, sections, waterFlowSections);
158 }
159
UpdateWaterFlowSectionsByFrameNode(NG::FrameNode * frameNode,const JSCallbackInfo & args,const JSRef<JSVal> & sections)160 void JSWaterFlow::UpdateWaterFlowSectionsByFrameNode(
161 NG::FrameNode* frameNode, const JSCallbackInfo& args, const JSRef<JSVal>& sections)
162 {
163 auto waterFlowSections = NG::WaterFlowModelNG::GetOrCreateWaterFlowSections(frameNode);
164 CHECK_NULL_VOID(waterFlowSections);
165 UpdateSections(args, sections, waterFlowSections);
166 }
167
SetWaterFlowBuilderNode(const JSRef<JSObject> & footerJsObject)168 RefPtr<NG::UINode> SetWaterFlowBuilderNode(const JSRef<JSObject>& footerJsObject)
169 {
170 JSRef<JSVal> builderNodeParam = footerJsObject->GetProperty("builderNode_");
171 if (builderNodeParam->IsObject()) {
172 auto builderNodeObject = JSRef<JSObject>::Cast(builderNodeParam);
173 JSRef<JSVal> nodePtr = builderNodeObject->GetProperty("nodePtr_");
174 if (!nodePtr.IsEmpty()) {
175 const auto* vm = nodePtr->GetEcmaVM();
176 auto* node = nodePtr->GetLocalHandle()->ToNativePointer(vm)->Value();
177 auto* myUINode = reinterpret_cast<NG::UINode*>(node);
178 if (!myUINode) {
179 return nullptr;
180 }
181 auto refPtrUINode = AceType::Claim(myUINode);
182 return refPtrUINode;
183 }
184 }
185 return nullptr;
186 }
187
UpdateWaterFlowFooterContent(NG::FrameNode * frameNode,const JSRef<JSVal> & args)188 void JSWaterFlow::UpdateWaterFlowFooterContent(NG::FrameNode* frameNode, const JSRef<JSVal>& args)
189 {
190 CHECK_NULL_VOID(args->IsObject());
191 JSRef<JSObject> footerJsObject = JSRef<JSObject>::Cast(args); // 4 is the index of footerContent
192 if (footerJsObject->HasProperty("builderNode_")) {
193 RefPtr<NG::UINode> refPtrUINode = SetWaterFlowBuilderNode(footerJsObject);
194 NG::WaterFlowModelNG::SetWaterflowFooterWithFrameNode(frameNode, refPtrUINode);
195 }
196 }
197
Create(const JSCallbackInfo & args)198 void JSWaterFlow::Create(const JSCallbackInfo& args)
199 {
200 if (args.Length() > 1) {
201 LOGW("Arg is wrong, it is supposed to have at most one argument");
202 return;
203 }
204
205 WaterFlowModel::GetInstance()->Create();
206
207 if (args.Length() == 0) {
208 return;
209 }
210
211 if (!args[0]->IsObject()) {
212 LOGE("The arg must be object");
213 return;
214 }
215 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
216
217 // set layout mode first. SetFooter is dependent to it
218 using LayoutMode = NG::WaterFlowLayoutMode;
219 auto mode = LayoutMode::TOP_DOWN;
220 auto jsMode = obj->GetProperty("layoutMode");
221 if (jsMode->IsNumber()) {
222 mode = static_cast<LayoutMode>(jsMode->ToNumber<int32_t>());
223 if (mode < LayoutMode::TOP_DOWN || mode > LayoutMode::SLIDING_WINDOW) {
224 mode = LayoutMode::TOP_DOWN;
225 }
226 }
227 WaterFlowModel::GetInstance()->SetLayoutMode(mode);
228
229 ParseScroller(obj);
230
231 auto sections = obj->GetProperty("sections");
232 auto footerObject = obj->GetProperty("footer");
233 if (sections->IsObject()) {
234 UpdateWaterFlowSections(args, sections);
235 } else {
236 WaterFlowModel::GetInstance()->ResetSections();
237
238 if (obj->HasProperty("footerContent")) {
239 RefPtr<NG::UINode> refPtrUINode = nullptr;
240 auto footerContentObject = obj->GetProperty("footerContent");
241 if (footerContentObject->IsObject()) {
242 auto footerJsObject = JSRef<JSObject>::Cast(footerContentObject);
243 refPtrUINode = SetWaterFlowBuilderNode(footerJsObject);
244 }
245 WaterFlowModel::GetInstance()->SetFooterWithFrameNode(refPtrUINode);
246 return;
247 }
248 if (footerObject->IsFunction()) {
249 // ignore footer if sections are present
250 auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(footerObject));
251 auto footerAction = [builderFunc]() { builderFunc->Execute(); };
252 WaterFlowModel::GetInstance()->SetFooter(footerAction);
253 }
254 }
255 }
256
JSBind(BindingTarget globalObj)257 void JSWaterFlow::JSBind(BindingTarget globalObj)
258 {
259 JSClass<JSWaterFlow>::Declare("WaterFlow");
260
261 MethodOptions opt = MethodOptions::NONE;
262 JSClass<JSWaterFlow>::StaticMethod("create", &JSWaterFlow::Create, opt);
263 JSClass<JSWaterFlow>::StaticMethod("columnsGap", &JSWaterFlow::SetColumnsGap, opt);
264 JSClass<JSWaterFlow>::StaticMethod("rowsGap", &JSWaterFlow::SetRowsGap, opt);
265 JSClass<JSWaterFlow>::StaticMethod("layoutDirection", &JSWaterFlow::SetLayoutDirection, opt);
266 JSClass<JSWaterFlow>::StaticMethod("columnsTemplate", &JSWaterFlow::SetColumnsTemplate, opt);
267 JSClass<JSWaterFlow>::StaticMethod("itemConstraintSize", &JSWaterFlow::SetItemConstraintSize, opt);
268 JSClass<JSWaterFlow>::StaticMethod("rowsTemplate", &JSWaterFlow::SetRowsTemplate, opt);
269 JSClass<JSWaterFlow>::StaticMethod("nestedScroll", &JSWaterFlow::SetNestedScroll);
270 JSClass<JSWaterFlow>::StaticMethod("enableScrollInteraction", &JSWaterFlow::SetScrollEnabled);
271 JSClass<JSWaterFlow>::StaticMethod("onReachStart", &JSWaterFlow::ReachStartCallback);
272 JSClass<JSWaterFlow>::StaticMethod("onReachEnd", &JSWaterFlow::ReachEndCallback);
273 JSClass<JSWaterFlow>::StaticMethod("onScrollFrameBegin", &JSWaterFlow::ScrollFrameBeginCallback);
274 JSClass<JSWaterFlow>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
275 JSClass<JSWaterFlow>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
276 JSClass<JSWaterFlow>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
277 JSClass<JSWaterFlow>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
278 JSClass<JSWaterFlow>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
279 JSClass<JSWaterFlow>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
280 JSClass<JSWaterFlow>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
281 JSClass<JSWaterFlow>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
282 JSClass<JSWaterFlow>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
283 JSClass<JSWaterFlow>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
284 JSClass<JSWaterFlow>::StaticMethod("friction", &JSWaterFlow::SetFriction);
285 JSClass<JSWaterFlow>::StaticMethod("clip", &JSScrollable::JsClip);
286 JSClass<JSWaterFlow>::StaticMethod("cachedCount", &JSWaterFlow::SetCachedCount);
287 JSClass<JSWaterFlow>::StaticMethod("edgeEffect", &JSWaterFlow::SetEdgeEffect);
288 JSClass<JSWaterFlow>::StaticMethod("syncLoad", &JSWaterFlow::SetSyncLoad);
289
290 JSClass<JSWaterFlow>::StaticMethod("onScroll", &JSWaterFlow::JsOnScroll);
291 JSClass<JSWaterFlow>::StaticMethod("onScrollStart", &JSWaterFlow::JsOnScrollStart);
292 JSClass<JSWaterFlow>::StaticMethod("onScrollStop", &JSWaterFlow::JsOnScrollStop);
293 JSClass<JSWaterFlow>::StaticMethod("onScrollIndex", &JSWaterFlow::JsOnScrollIndex);
294
295 JSClass<JSWaterFlow>::StaticMethod("scrollBar", &JSWaterFlow::SetScrollBar, opt);
296 JSClass<JSWaterFlow>::StaticMethod("scrollBarWidth", &JSWaterFlow::SetScrollBarWidth, opt);
297 JSClass<JSWaterFlow>::StaticMethod("scrollBarColor", &JSWaterFlow::SetScrollBarColor, opt);
298
299 JSClass<JSWaterFlow>::InheritAndBind<JSScrollableBase>(globalObj);
300 }
301
SetColumnsGap(const JSCallbackInfo & info)302 void JSWaterFlow::SetColumnsGap(const JSCallbackInfo& info)
303 {
304 if (info.Length() < 1) {
305 return;
306 }
307 CalcDimension colGap;
308 if (!ParseJsDimensionVp(info[0], colGap) || colGap.Value() < 0) {
309 colGap.SetValue(0.0);
310 }
311 WaterFlowModel::GetInstance()->SetColumnsGap(colGap);
312 }
313
SetRowsGap(const JSCallbackInfo & info)314 void JSWaterFlow::SetRowsGap(const JSCallbackInfo& info)
315 {
316 if (info.Length() < 1) {
317 return;
318 }
319 CalcDimension rowGap;
320 if (!ParseJsDimensionVp(info[0], rowGap) || rowGap.Value() < 0) {
321 rowGap.SetValue(0.0);
322 }
323 WaterFlowModel::GetInstance()->SetRowsGap(rowGap);
324 }
325
SetLayoutDirection(const JSCallbackInfo & info)326 void JSWaterFlow::SetLayoutDirection(const JSCallbackInfo& info)
327 {
328 if (info.Length() < 1) {
329 return;
330 }
331 auto value = static_cast<int32_t>(FlexDirection::COLUMN);
332 auto jsValue = info[0];
333 if (!jsValue->IsUndefined()) {
334 ParseJsInteger<int32_t>(jsValue, value);
335 }
336 if (value >= 0 && value < static_cast<int32_t>(LAYOUT_DIRECTION.size())) {
337 WaterFlowModel::GetInstance()->SetLayoutDirection(LAYOUT_DIRECTION[value]);
338 } else {
339 WaterFlowModel::GetInstance()->SetLayoutDirection(FlexDirection::COLUMN);
340 }
341 }
342
SetColumnsTemplate(const std::string & value)343 void JSWaterFlow::SetColumnsTemplate(const std::string& value)
344 {
345 WaterFlowModel::GetInstance()->SetColumnsTemplate(value);
346 }
347
SetItemConstraintSize(const JSCallbackInfo & info)348 void JSWaterFlow::SetItemConstraintSize(const JSCallbackInfo& info)
349 {
350 if (info.Length() < 1 || !info[0]->IsObject()) {
351 return;
352 }
353
354 JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
355
356 JSRef<JSVal> minWidthValue = sizeObj->GetProperty("minWidth");
357 CalcDimension minWidth;
358 if (ParseJsDimensionVp(minWidthValue, minWidth)) {
359 WaterFlowModel::GetInstance()->SetItemMinWidth(minWidth);
360 }
361
362 JSRef<JSVal> maxWidthValue = sizeObj->GetProperty("maxWidth");
363 CalcDimension maxWidth;
364 if (ParseJsDimensionVp(maxWidthValue, maxWidth)) {
365 WaterFlowModel::GetInstance()->SetItemMaxWidth(maxWidth);
366 }
367
368 JSRef<JSVal> minHeightValue = sizeObj->GetProperty("minHeight");
369 CalcDimension minHeight;
370 if (ParseJsDimensionVp(minHeightValue, minHeight)) {
371 WaterFlowModel::GetInstance()->SetItemMinHeight(minHeight);
372 }
373
374 JSRef<JSVal> maxHeightValue = sizeObj->GetProperty("maxHeight");
375 CalcDimension maxHeight;
376 if (ParseJsDimensionVp(maxHeightValue, maxHeight)) {
377 WaterFlowModel::GetInstance()->SetItemMaxHeight(maxHeight);
378 }
379 }
380
SetRowsTemplate(const std::string & value)381 void JSWaterFlow::SetRowsTemplate(const std::string& value)
382 {
383 WaterFlowModel::GetInstance()->SetRowsTemplate(value);
384 }
385
SetNestedScroll(const JSCallbackInfo & args)386 void JSWaterFlow::SetNestedScroll(const JSCallbackInfo& args)
387 {
388 NestedScrollOptions nestedOpt = {
389 .forward = NestedScrollMode::SELF_ONLY,
390 .backward = NestedScrollMode::SELF_ONLY,
391 };
392 if (args.Length() < 1 || !args[0]->IsObject()) {
393 WaterFlowModel::GetInstance()->SetNestedScroll(nestedOpt);
394 LOGW("Invalid params");
395 return;
396 }
397 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
398 int32_t froward = 0;
399 JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollForward"), froward);
400 if (froward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
401 froward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
402 LOGW("ScrollFroward params invalid");
403 froward = 0;
404 }
405 int32_t backward = 0;
406 JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollBackward"), backward);
407 if (backward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
408 backward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
409 LOGW("ScrollFroward params invalid");
410 backward = 0;
411 }
412 nestedOpt.forward = static_cast<NestedScrollMode>(froward);
413 nestedOpt.backward = static_cast<NestedScrollMode>(backward);
414 WaterFlowModel::GetInstance()->SetNestedScroll(nestedOpt);
415 args.ReturnSelf();
416 }
417
SetScrollEnabled(const JSCallbackInfo & args)418 void JSWaterFlow::SetScrollEnabled(const JSCallbackInfo& args)
419 {
420 WaterFlowModel::GetInstance()->SetScrollEnabled(args[0]->IsBoolean() ? args[0]->ToBoolean() : true);
421 }
422
SetFriction(const JSCallbackInfo & info)423 void JSWaterFlow::SetFriction(const JSCallbackInfo& info)
424 {
425 double friction = -1.0;
426 if (SystemProperties::ConfigChangePerform()) {
427 RefPtr<ResourceObject> resObj;
428 if (!JSViewAbstract::ParseJsDouble(info[0], friction, resObj)) {
429 friction = -1.0;
430 }
431 WaterFlowModel::GetInstance()->ParseResObjFriction(resObj);
432 } else if (!JSViewAbstract::ParseJsDouble(info[0], friction)) {
433 LOGW("Friction params invalid,can not convert to double");
434 friction = -1.0;
435 }
436 WaterFlowModel::GetInstance()->SetFriction(friction);
437 }
438
ReachStartCallback(const JSCallbackInfo & args)439 void JSWaterFlow::ReachStartCallback(const JSCallbackInfo& args)
440 {
441 if (args[0]->IsFunction()) {
442 auto onReachStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
443 func->Call(JSRef<JSObject>());
444 UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "onReachStart");
445 return;
446 };
447 WaterFlowModel::GetInstance()->SetOnReachStart(std::move(onReachStart));
448 }
449 args.ReturnSelf();
450 }
451
ReachEndCallback(const JSCallbackInfo & args)452 void JSWaterFlow::ReachEndCallback(const JSCallbackInfo& args)
453 {
454 if (args[0]->IsFunction()) {
455 auto onReachEnd = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
456 func->Call(JSRef<JSObject>());
457 UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "onReachEnd");
458 return;
459 };
460 WaterFlowModel::GetInstance()->SetOnReachEnd(std::move(onReachEnd));
461 }
462 args.ReturnSelf();
463 }
464
ScrollFrameBeginCallback(const JSCallbackInfo & args)465 void JSWaterFlow::ScrollFrameBeginCallback(const JSCallbackInfo& args)
466 {
467 if (args[0]->IsFunction()) {
468 auto onScrollBegin = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
469 const Dimension& offset, const ScrollState& state) -> ScrollFrameResult {
470 ScrollFrameResult scrollRes { .offset = offset };
471 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollRes);
472 auto params = ConvertToJSValues(offset, state);
473 auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
474 if (result.IsEmpty()) {
475 LOGE("Error calling onScrollFrameBegin, result is empty.");
476 return scrollRes;
477 }
478
479 if (!result->IsObject()) {
480 LOGE("Error calling onScrollFrameBegin, result is not object.");
481 return scrollRes;
482 }
483
484 auto resObj = JSRef<JSObject>::Cast(result);
485 auto dxRemainValue = resObj->GetProperty("offsetRemain");
486 if (dxRemainValue->IsNumber()) {
487 scrollRes.offset = Dimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
488 }
489 return scrollRes;
490 };
491 WaterFlowModel::GetInstance()->SetOnScrollFrameBegin(std::move(onScrollBegin));
492 }
493 }
494
SetCachedCount(const JSCallbackInfo & info)495 void JSWaterFlow::SetCachedCount(const JSCallbackInfo& info)
496 {
497 int32_t cachedCount = 1;
498 auto jsValue = info[0];
499
500 if (!jsValue->IsUndefined() && jsValue->IsNumber()) {
501 ParseJsInt32(jsValue, cachedCount);
502 if (cachedCount < 0) {
503 cachedCount = 1;
504 }
505 }
506 bool show = false;
507 if (info.Length() > 1) {
508 show = info[1]->ToBoolean();
509 }
510 WaterFlowModel::GetInstance()->SetCachedCount(cachedCount, show);
511 }
512
SetEdgeEffect(const JSCallbackInfo & info)513 void JSWaterFlow::SetEdgeEffect(const JSCallbackInfo& info)
514 {
515 auto edgeEffect = WaterFlowModel::GetInstance()->GetEdgeEffect();
516 auto effectEdge = EffectEdge::ALL;
517 if (info.Length() > 0) {
518 edgeEffect = JSScrollable::ParseEdgeEffect(info[0], edgeEffect);
519 }
520 auto alwaysEnabled = WaterFlowModel::GetInstance()->GetAlwaysEnableEdgeEffect();
521 if (info.Length() > 1) {
522 alwaysEnabled = JSScrollable::ParseAlwaysEnable(info[1], alwaysEnabled);
523 effectEdge = JSScrollable::ParseEffectEdge(info[1]);
524 }
525 WaterFlowModel::GetInstance()->SetEdgeEffect(edgeEffect, alwaysEnabled, effectEdge);
526 }
527
SetSyncLoad(const JSCallbackInfo & info)528 void JSWaterFlow::SetSyncLoad(const JSCallbackInfo& info)
529 {
530 bool syncLoad = true;
531 if (info.Length() >= 1) {
532 auto value = info[0];
533 if (value->IsBoolean()) {
534 syncLoad = value->ToBoolean();
535 }
536 }
537 WaterFlowModel::GetInstance()->SetSyncLoad(syncLoad);
538 }
539
JsOnScroll(const JSCallbackInfo & args)540 void JSWaterFlow::JsOnScroll(const JSCallbackInfo& args)
541 {
542 if (args[0]->IsFunction()) {
543 auto onScroll = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
544 const CalcDimension& scrollOffset, const ScrollState& scrollState) {
545 auto params = ConvertToJSValues(scrollOffset, scrollState);
546 func->Call(JSRef<JSObject>(), params.size(), params.data());
547 return;
548 };
549 WaterFlowModel::GetInstance()->SetOnScroll(std::move(onScroll));
550 }
551 args.ReturnSelf();
552 }
553
JsOnScrollStart(const JSCallbackInfo & args)554 void JSWaterFlow::JsOnScrollStart(const JSCallbackInfo& args)
555 {
556 if (args[0]->IsFunction()) {
557 auto onScrollStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
558 func->Call(JSRef<JSObject>());
559 return;
560 };
561 WaterFlowModel::GetInstance()->SetOnScrollStart(std::move(onScrollStart));
562 }
563 args.ReturnSelf();
564 }
565
JsOnScrollStop(const JSCallbackInfo & args)566 void JSWaterFlow::JsOnScrollStop(const JSCallbackInfo& args)
567 {
568 if (args[0]->IsFunction()) {
569 auto onScrollStop = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
570 func->Call(JSRef<JSObject>());
571 return;
572 };
573 WaterFlowModel::GetInstance()->SetOnScrollStop(std::move(onScrollStop));
574 }
575 args.ReturnSelf();
576 }
577
JsOnScrollIndex(const JSCallbackInfo & args)578 void JSWaterFlow::JsOnScrollIndex(const JSCallbackInfo& args)
579 {
580 if (args[0]->IsFunction()) {
581 auto onScrollIndex = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
582 const int32_t first, const int32_t last) {
583 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
584 auto params = ConvertToJSValues(first, last);
585 func->Call(JSRef<JSObject>(), params.size(), params.data());
586 UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "onScrollIndex");
587 return;
588 };
589 WaterFlowModel::GetInstance()->SetOnScrollIndex(std::move(onScrollIndex));
590 }
591 args.ReturnSelf();
592 }
593
SetScrollBar(const JSCallbackInfo & info)594 void JSWaterFlow::SetScrollBar(const JSCallbackInfo& info)
595 {
596 auto displayMode = JSScrollable::ParseDisplayMode(info, WaterFlowModel::GetDisplayMode());
597 WaterFlowModel::GetInstance()->SetScrollBarMode(displayMode);
598 }
599
SetScrollBarColor(const JSCallbackInfo & info)600 void JSWaterFlow::SetScrollBarColor(const JSCallbackInfo& info)
601 {
602 RefPtr<ResourceObject> resObj;
603 auto scrollBarColor = JSScrollable::ParseBarColor(info, resObj);
604 if (!scrollBarColor.empty()) {
605 WaterFlowModel::GetInstance()->SetScrollBarColor(scrollBarColor);
606 }
607 if (SystemProperties::ConfigChangePerform()) {
608 WaterFlowModel::GetInstance()->ParseResObjScrollBarColor(resObj);
609 }
610 }
611
SetScrollBarWidth(const JSCallbackInfo & scrollWidth)612 void JSWaterFlow::SetScrollBarWidth(const JSCallbackInfo& scrollWidth)
613 {
614 auto scrollBarWidth = JSScrollable::ParseBarWidth(scrollWidth);
615 if (!scrollBarWidth.empty()) {
616 WaterFlowModel::GetInstance()->SetScrollBarWidth(scrollBarWidth);
617 }
618 }
619 } // namespace OHOS::Ace::Framework
620