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_list.h"
17
18 #include "base/geometry/axis.h"
19 #include "base/log/ace_scoring_log.h"
20 #include "bridge/declarative_frontend/engine/functions/js_drag_function.h"
21 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
22 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
23 #include "bridge/declarative_frontend/jsview/models/list_model_impl.h"
24 #include "core/components_ng/base/view_stack_model.h"
25 #include "core/components_ng/pattern/list/list_model.h"
26 #include "core/components_ng/pattern/list/list_model_ng.h"
27 #include "core/components_ng/pattern/list/list_position_controller.h"
28 #include "core/components_ng/pattern/scroll_bar/proxy/scroll_bar_proxy.h"
29
30 namespace OHOS::Ace {
31
32 std::unique_ptr<ListModel> ListModel::instance_ = nullptr;
33 std::mutex ListModel::mutex_;
34
GetInstance()35 ListModel* ListModel::GetInstance()
36 {
37 if (!instance_) {
38 std::lock_guard<std::mutex> lock(mutex_);
39 if (!instance_) {
40 #ifdef NG_BUILD
41 instance_.reset(new NG::ListModelNG());
42 #else
43 if (Container::IsCurrentUseNewPipeline()) {
44 instance_.reset(new NG::ListModelNG());
45 } else {
46 instance_.reset(new Framework::ListModelImpl());
47 }
48 #endif
49 }
50 }
51 return instance_.get();
52 }
53
54 } // namespace OHOS::Ace
55
56 namespace OHOS::Ace::Framework {
57
58 const std::vector<V2::ScrollSnapAlign> SCROLL_SNAP_ALIGN = { V2::ScrollSnapAlign::NONE, V2::ScrollSnapAlign::START,
59 V2::ScrollSnapAlign::CENTER, V2::ScrollSnapAlign::END };
60
SetDirection(int32_t direction)61 void JSList::SetDirection(int32_t direction)
62 {
63 ListModel::GetInstance()->SetListDirection(static_cast<Axis>(direction));
64 }
65
SetScrollBar(const JSCallbackInfo & info)66 void JSList::SetScrollBar(const JSCallbackInfo& info)
67 {
68 // default value 1 represents scrollBar DisplayMode::AUTO.
69 int32_t scrollBar = 1;
70 ParseJsInteger<int32_t>(info[0], scrollBar);
71 scrollBar = scrollBar < 0 ? 1 : scrollBar;
72 ListModel::GetInstance()->SetScrollBar(static_cast<DisplayMode>(scrollBar));
73 }
74
SetEdgeEffect(int32_t edgeEffect)75 void JSList::SetEdgeEffect(int32_t edgeEffect)
76 {
77 ListModel::GetInstance()->SetEdgeEffect(static_cast<EdgeEffect>(edgeEffect));
78 }
79
SetEditMode(bool editMode)80 void JSList::SetEditMode(bool editMode)
81 {
82 ListModel::GetInstance()->SetEditMode(editMode);
83 }
84
SetCachedCount(const JSCallbackInfo & info)85 void JSList::SetCachedCount(const JSCallbackInfo& info)
86 {
87 int32_t cachedCount = 1;
88 ParseJsInteger<int32_t>(info[0], cachedCount);
89 cachedCount = cachedCount < 0 ? 1 : cachedCount;
90 ListModel::GetInstance()->SetCachedCount(cachedCount);
91 }
92
SetScroller(RefPtr<JSScroller> scroller)93 void JSList::SetScroller(RefPtr<JSScroller> scroller)
94 {
95 if (scroller) {
96 RefPtr<ScrollControllerBase> listController = ListModel::GetInstance()->CreateScrollController();
97 scroller->SetController(listController);
98
99 // Init scroll bar proxy.
100 auto proxy = scroller->GetScrollBarProxy();
101 if (!proxy) {
102 if (Container::IsCurrentUseNewPipeline()) {
103 proxy = AceType::MakeRefPtr<NG::ScrollBarProxy>();
104 } else {
105 proxy = AceType::MakeRefPtr<ScrollBarProxy>();
106 }
107 scroller->SetScrollBarProxy(proxy);
108 }
109 ListModel::GetInstance()->SetScroller(listController, proxy);
110 }
111 }
112
Create(const JSCallbackInfo & args)113 void JSList::Create(const JSCallbackInfo& args)
114 {
115 ListModel::GetInstance()->Create();
116 if (args.Length() >= 1 && args[0]->IsObject()) {
117 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
118 JSRef<JSVal> spaceValue = obj->GetProperty("space");
119 if (!spaceValue->IsNull()) {
120 CalcDimension space;
121 ConvertFromJSValue(spaceValue, space);
122 ListModel::GetInstance()->SetSpace(space);
123 }
124 int32_t initialIndex = 0;
125 if (ConvertFromJSValue(obj->GetProperty("initialIndex"), initialIndex) && initialIndex >= 0) {
126 ListModel::GetInstance()->SetInitialIndex(initialIndex);
127 }
128 JSRef<JSVal> scrollerValue = obj->GetProperty("scroller");
129 if (scrollerValue->IsObject()) {
130 void* scroller = JSRef<JSObject>::Cast(scrollerValue)->Unwrap<JSScroller>();
131 RefPtr<JSScroller> jsScroller = Referenced::Claim(reinterpret_cast<JSScroller*>(scroller));
132 SetScroller(jsScroller);
133 }
134 }
135
136 args.ReturnSelf();
137 }
138
SetChainAnimation(bool enableChainAnimation)139 void JSList::SetChainAnimation(bool enableChainAnimation)
140 {
141 ListModel::GetInstance()->SetChainAnimation(enableChainAnimation);
142 }
143
SetChainAnimationOptions(const JSCallbackInfo & info)144 void JSList::SetChainAnimationOptions(const JSCallbackInfo& info)
145 {
146 if (info.Length() < 1) {
147 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
148 return;
149 }
150
151 if (info[0]->IsObject()) {
152 RefPtr<ListTheme> listTheme = GetTheme<ListTheme>();
153 CHECK_NULL_VOID(listTheme);
154 ChainAnimationOptions options = {
155 .minSpace = listTheme->GetChainMinSpace(),
156 .maxSpace = listTheme->GetChainMaxSpace(),
157 .conductivity = listTheme->GetChainConductivity(),
158 .intensity = listTheme->GetChainIntensity(),
159 .edgeEffect = 0,
160 .stiffness = listTheme->GetChainStiffness(),
161 .damping = listTheme->GetChainDamping(),
162 };
163 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
164 ParseJsDimensionVp(jsObj->GetProperty("minSpace"), options.minSpace);
165 ParseJsDimensionVp(jsObj->GetProperty("maxSpace"), options.maxSpace);
166 JSViewAbstract::ParseJsDouble(jsObj->GetProperty("conductivity"), options.conductivity);
167 JSViewAbstract::ParseJsDouble(jsObj->GetProperty("intensity"), options.intensity);
168 JSViewAbstract::ParseJsInt32(jsObj->GetProperty("edgeEffect"), options.edgeEffect);
169 JSViewAbstract::ParseJsDouble(jsObj->GetProperty("stiffness"), options.stiffness);
170 JSViewAbstract::ParseJsDouble(jsObj->GetProperty("damping"), options.damping);
171 ListModel::GetInstance()->SetChainAnimationOptions(options);
172 }
173 }
174
JsWidth(const JSCallbackInfo & info)175 void JSList::JsWidth(const JSCallbackInfo& info)
176 {
177 JSViewAbstract::JsWidth(info);
178 ListModel::GetInstance()->SetHasWidth(true);
179 }
180
JsHeight(const JSCallbackInfo & info)181 void JSList::JsHeight(const JSCallbackInfo& info)
182 {
183 JSViewAbstract::JsHeight(info);
184 ListModel::GetInstance()->SetHasHeight(true);
185 }
186
SetListItemAlign(int32_t itemAlignment)187 void JSList::SetListItemAlign(int32_t itemAlignment)
188 {
189 ListModel::GetInstance()->SetListItemAlign(static_cast<V2::ListItemAlign>(itemAlignment));
190 }
191
SetLanes(const JSCallbackInfo & info)192 void JSList::SetLanes(const JSCallbackInfo& info)
193 {
194 if (info.Length() < 1) {
195 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
196 return;
197 }
198
199 if (info.Length() >= 2 && !(info[1]->IsNull())) { /* 2: parameter count */
200 CalcDimension laneGutter;
201 if (JSViewAbstract::ParseJsDimensionVp(info[1], laneGutter)) {
202 if (laneGutter.IsNegative()) {
203 laneGutter.Reset();
204 }
205 }
206 ListModel::GetInstance()->SetLaneGutter(laneGutter);
207 }
208
209 int32_t laneNum = 1;
210 if (ParseJsInteger<int32_t>(info[0], laneNum)) {
211 // when [lanes] is set, [laneConstrain_] of list component will be reset to std::nullopt
212 ListModel::GetInstance()->SetLanes(laneNum);
213 ListModel::GetInstance()->SetLaneConstrain(-1.0_vp, -1.0_vp);
214 return;
215 }
216 if (info[0]->IsObject()) {
217 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
218 auto minLengthParam = jsObj->GetProperty("minLength");
219 auto maxLengthParam = jsObj->GetProperty("maxLength");
220 if (minLengthParam->IsNull() || maxLengthParam->IsNull()) {
221 LOGW("minLength and maxLength are not both set");
222 return;
223 }
224 CalcDimension minLengthValue;
225 CalcDimension maxLengthValue;
226 if (!ParseJsDimensionVp(minLengthParam, minLengthValue)
227 || !ParseJsDimensionVp(maxLengthParam, maxLengthValue)) {
228 LOGW("minLength param or maxLength param is invalid");
229 ListModel::GetInstance()->SetLanes(1);
230 ListModel::GetInstance()->SetLaneConstrain(-1.0_vp, -1.0_vp);
231 return;
232 }
233 ListModel::GetInstance()->SetLaneConstrain(minLengthValue, maxLengthValue);
234 ListModel::GetInstance()->SetLanes(1);
235 }
236 }
237
SetSticky(int32_t sticky)238 void JSList::SetSticky(int32_t sticky)
239 {
240 ListModel::GetInstance()->SetSticky(static_cast<V2::StickyStyle>(sticky));
241 }
242
SetContentStartOffset(float startOffset)243 void JSList::SetContentStartOffset(float startOffset)
244 {
245 ListModel::GetInstance()->SetContentStartOffset(startOffset);
246 }
247
SetContentEndOffset(float endOffset)248 void JSList::SetContentEndOffset(float endOffset)
249 {
250 ListModel::GetInstance()->SetContentEndOffset(endOffset);
251 }
252
SetScrollSnapAlign(int32_t scrollSnapAlign)253 void JSList::SetScrollSnapAlign(int32_t scrollSnapAlign)
254 {
255 V2::ScrollSnapAlign param;
256 if (scrollSnapAlign < 0 || scrollSnapAlign >= static_cast<int32_t>(SCROLL_SNAP_ALIGN.size())) {
257 param = V2::ScrollSnapAlign::NONE;
258 } else {
259 param = V2::ScrollSnapAlign(scrollSnapAlign);
260 }
261 ListModel::GetInstance()->SetScrollSnapAlign(param);
262 }
263
SetDivider(const JSCallbackInfo & args)264 void JSList::SetDivider(const JSCallbackInfo& args)
265 {
266 if (args.Length() < 1 || !args[0]->IsObject()) {
267 LOGW("Invalid params");
268 return;
269 }
270
271 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
272 V2::ItemDivider divider;
273 if (!ConvertFromJSValue(obj->GetProperty("strokeWidth"), divider.strokeWidth)) {
274 LOGW("Invalid strokeWidth of divider");
275 divider.strokeWidth.Reset();
276 }
277 if (!ConvertFromJSValue(obj->GetProperty("color"), divider.color)) {
278 // Failed to get color from param, using default color defined in theme
279 RefPtr<ListTheme> listTheme = GetTheme<ListTheme>();
280 if (listTheme) {
281 divider.color = listTheme->GetDividerColor();
282 }
283 }
284 ConvertFromJSValue(obj->GetProperty("startMargin"), divider.startMargin);
285 ConvertFromJSValue(obj->GetProperty("endMargin"), divider.endMargin);
286 ListModel::GetInstance()->SetDivider(divider);
287
288 args.ReturnSelf();
289 }
290
SetNestedScroll(const JSCallbackInfo & args)291 void JSList::SetNestedScroll(const JSCallbackInfo& args)
292 {
293 NestedScrollOptions nestedOpt = {
294 .forward = NestedScrollMode::SELF_ONLY,
295 .backward = NestedScrollMode::SELF_ONLY,
296 };
297 if (args.Length() < 1 || !args[0]->IsObject()) {
298 ListModel::GetInstance()->SetNestedScroll(nestedOpt);
299 LOGW("Invalid params");
300 return;
301 }
302 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
303 int32_t froward = 0;
304 JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollForward"), froward);
305 if (froward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
306 froward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
307 LOGW("ScrollFroward params invalid");
308 froward = 0;
309 }
310 int32_t backward = 0;
311 JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollBackward"), backward);
312 if (backward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
313 backward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
314 LOGW("ScrollFroward params invalid");
315 backward = 0;
316 }
317 nestedOpt.forward = static_cast<NestedScrollMode>(froward);
318 nestedOpt.backward = static_cast<NestedScrollMode>(backward);
319 ListModel::GetInstance()->SetNestedScroll(nestedOpt);
320 args.ReturnSelf();
321 }
322
SetScrollEnabled(const JSCallbackInfo & args)323 void JSList::SetScrollEnabled(const JSCallbackInfo& args)
324 {
325 ListModel::GetInstance()->SetScrollEnabled(args[0]->IsBoolean() ? args[0]->ToBoolean() : true);
326 }
327
ScrollCallback(const JSCallbackInfo & args)328 void JSList::ScrollCallback(const JSCallbackInfo& args)
329 {
330 if (args[0]->IsFunction()) {
331 auto onScroll = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
332 const CalcDimension& scrollOffset, const ScrollState& scrollState) {
333 auto params = ConvertToJSValues(scrollOffset, scrollState);
334 func->Call(JSRef<JSObject>(), params.size(), params.data());
335 return;
336 };
337 ListModel::GetInstance()->SetOnScroll(std::move(onScroll));
338 }
339 args.ReturnSelf();
340 }
341
SetFriction(const JSCallbackInfo & info)342 void JSList::SetFriction(const JSCallbackInfo& info)
343 {
344 double friction = -1.0;
345 if (!JSViewAbstract::ParseJsDouble(info[0], friction)) {
346 LOGW("Friction params invalid,can not convert to double");
347 friction = -1.0;
348 }
349 ListModel::GetInstance()->SetFriction(friction);
350 }
351
ReachStartCallback(const JSCallbackInfo & args)352 void JSList::ReachStartCallback(const JSCallbackInfo& args)
353 {
354 if (args[0]->IsFunction()) {
355 auto onReachStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
356 func->Call(JSRef<JSObject>());
357 return;
358 };
359 ListModel::GetInstance()->SetOnReachStart(std::move(onReachStart));
360 }
361 args.ReturnSelf();
362 }
363
ReachEndCallback(const JSCallbackInfo & args)364 void JSList::ReachEndCallback(const JSCallbackInfo& args)
365 {
366 if (args[0]->IsFunction()) {
367 auto onReachEnd = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
368 func->Call(JSRef<JSObject>());
369 return;
370 };
371 ListModel::GetInstance()->SetOnReachEnd(std::move(onReachEnd));
372 }
373 args.ReturnSelf();
374 }
375
ScrollStartCallback(const JSCallbackInfo & args)376 void JSList::ScrollStartCallback(const JSCallbackInfo& args)
377 {
378 if (args[0]->IsFunction()) {
379 auto onScrollStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
380 func->Call(JSRef<JSObject>());
381 return;
382 };
383 ListModel::GetInstance()->SetOnScrollStart(std::move(onScrollStart));
384 }
385 args.ReturnSelf();
386 }
387
ScrollStopCallback(const JSCallbackInfo & args)388 void JSList::ScrollStopCallback(const JSCallbackInfo& args)
389 {
390 if (args[0]->IsFunction()) {
391 auto onScrollStop = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
392 func->Call(JSRef<JSObject>());
393 return;
394 };
395 ListModel::GetInstance()->SetOnScrollStop(std::move(onScrollStop));
396 }
397 args.ReturnSelf();
398 }
399
ItemDeleteCallback(const JSCallbackInfo & args)400 void JSList::ItemDeleteCallback(const JSCallbackInfo& args)
401 {
402 if (args[0]->IsFunction()) {
403 auto onItemDelete = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
404 int32_t index) -> bool {
405 auto params = ConvertToJSValues(index);
406 func->Call(JSRef<JSObject>(), params.size(), params.data());
407 return true;
408 };
409 ListModel::GetInstance()->SetOnItemDelete(std::move(onItemDelete));
410 }
411 args.ReturnSelf();
412 }
413
ItemMoveCallback(const JSCallbackInfo & args)414 void JSList::ItemMoveCallback(const JSCallbackInfo& args)
415 {
416 if (args[0]->IsFunction()) {
417 auto onItemMove = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
418 int32_t start, int32_t end) -> bool {
419 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, false);
420 auto params = ConvertToJSValues(start, end);
421 auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
422 if (!result.IsEmpty() && result->IsBoolean()) {
423 return result->ToBoolean();
424 }
425 return true;
426 };
427 ListModel::GetInstance()->SetOnItemMove(std::move(onItemMove));
428 }
429 args.ReturnSelf();
430 }
431
ScrollIndexCallback(const JSCallbackInfo & args)432 void JSList::ScrollIndexCallback(const JSCallbackInfo& args)
433 {
434 if (args[0]->IsFunction()) {
435 auto onScrollIndex = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
436 const int32_t start, const int32_t end, const int32_t center) {
437 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
438 auto params = ConvertToJSValues(start, end, center);
439 func->Call(JSRef<JSObject>(), params.size(), params.data());
440 return;
441 };
442 ListModel::GetInstance()->SetOnScrollIndex(std::move(onScrollIndex));
443 }
444 args.ReturnSelf();
445 }
446
ItemDragStartCallback(const JSCallbackInfo & info)447 void JSList::ItemDragStartCallback(const JSCallbackInfo& info)
448 {
449 if (!info[0]->IsFunction()) {
450 LOGE("fail to bind onItemDragStart event due to info is not function");
451 return;
452 }
453
454 RefPtr<JsDragFunction> jsOnDragFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
455 auto onItemDragStart = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragFunc)](
456 const ItemDragInfo& dragInfo, int32_t itemIndex) -> RefPtr<AceType> {
457 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, nullptr);
458 auto ret = func->ItemDragStartExecute(dragInfo, itemIndex);
459 if (!ret->IsObject()) {
460 LOGE("builder param is not an object.");
461 return nullptr;
462 }
463
464 auto builderObj = JSRef<JSObject>::Cast(ret);
465 auto builder = builderObj->GetProperty("builder");
466 if (!builder->IsFunction()) {
467 LOGE("builder param is not a function.");
468 return nullptr;
469 }
470 auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(builder));
471 if (!builderFunc) {
472 LOGE("builder function is null.");
473 return nullptr;
474 }
475 // use another VSP instance while executing the builder function
476 ViewStackModel::GetInstance()->NewScope();
477 {
478 ACE_SCORING_EVENT("List.onItemDragStart.builder");
479 builderFunc->Execute();
480 }
481 return ViewStackModel::GetInstance()->Finish();
482 };
483 ListModel::GetInstance()->SetOnItemDragStart(std::move(onItemDragStart));
484 }
485
ItemDragEnterCallback(const JSCallbackInfo & info)486 void JSList::ItemDragEnterCallback(const JSCallbackInfo& info)
487 {
488 if (!info[0]->IsFunction()) {
489 LOGE("fail to bind onItemDragEnter event due to info is not function");
490 return;
491 }
492
493 RefPtr<JsDragFunction> jsOnDragEnterFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
494 auto onItemDragEnter = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragEnterFunc)](
495 const ItemDragInfo& dragInfo) {
496 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
497 ACE_SCORING_EVENT("List.onItemDragEnter");
498 func->ItemDragEnterExecute(dragInfo);
499 };
500 ListModel::GetInstance()->SetOnItemDragEnter(std::move(onItemDragEnter));
501 }
502
ItemDragMoveCallback(const JSCallbackInfo & info)503 void JSList::ItemDragMoveCallback(const JSCallbackInfo& info)
504 {
505 if (!info[0]->IsFunction()) {
506 LOGE("fail to bind onItemDragMove event due to info is not function");
507 return;
508 }
509
510 RefPtr<JsDragFunction> jsOnDragMoveFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
511 auto onItemDragMove = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragMoveFunc)](
512 const ItemDragInfo& dragInfo, int32_t itemIndex, int32_t insertIndex) {
513 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
514 ACE_SCORING_EVENT("List.onItemDragMove");
515 func->ItemDragMoveExecute(dragInfo, itemIndex, insertIndex);
516 };
517 ListModel::GetInstance()->SetOnItemDragMove(std::move(onItemDragMove));
518 }
519
ItemDragLeaveCallback(const JSCallbackInfo & info)520 void JSList::ItemDragLeaveCallback(const JSCallbackInfo& info)
521 {
522 if (!info[0]->IsFunction()) {
523 LOGE("fail to bind onItemDragLeave event due to info is not function");
524 return;
525 }
526
527 RefPtr<JsDragFunction> jsOnDragLeaveFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
528 auto onItemDragLeave = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragLeaveFunc)](
529 const ItemDragInfo& dragInfo, int32_t itemIndex) {
530 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
531 ACE_SCORING_EVENT("List.onItemDragLeave");
532 func->ItemDragLeaveExecute(dragInfo, itemIndex);
533 };
534 ListModel::GetInstance()->SetOnItemDragLeave(std::move(onItemDragLeave));
535 }
536
ItemDropCallback(const JSCallbackInfo & info)537 void JSList::ItemDropCallback(const JSCallbackInfo& info)
538 {
539 if (!info[0]->IsFunction()) {
540 LOGE("fail to bind onItemDrop event due to info is not function");
541 return;
542 }
543
544 RefPtr<JsDragFunction> jsOnDropFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
545 auto onItemDrop = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDropFunc)](
546 const ItemDragInfo& dragInfo, int32_t itemIndex, int32_t insertIndex, bool isSuccess) {
547 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
548 ACE_SCORING_EVENT("List.onItemDrop");
549 func->ItemDropExecute(dragInfo, itemIndex, insertIndex, isSuccess);
550 };
551 ListModel::GetInstance()->SetOnItemDrop(onItemDrop);
552 }
553
SetMultiSelectable(bool multiSelectable)554 void JSList::SetMultiSelectable(bool multiSelectable)
555 {
556 ListModel::GetInstance()->SetMultiSelectable(multiSelectable);
557 }
558
ScrollBeginCallback(const JSCallbackInfo & args)559 void JSList::ScrollBeginCallback(const JSCallbackInfo& args)
560 {
561 if (args[0]->IsFunction()) {
562 auto onScrollBegin = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
563 const CalcDimension& dx, const CalcDimension& dy) -> ScrollInfo {
564 ScrollInfo scrollInfo { .dx = dx, .dy = dy };
565 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollInfo);
566 auto params = ConvertToJSValues(dx, dy);
567 auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
568 if (result.IsEmpty()) {
569 LOGE("Error calling onScrollBegin, result is empty.");
570 return scrollInfo;
571 }
572
573 if (!result->IsObject()) {
574 LOGE("Error calling onScrollBegin, result is not object.");
575 return scrollInfo;
576 }
577
578 auto resObj = JSRef<JSObject>::Cast(result);
579 auto dxRemainValue = resObj->GetProperty("dxRemain");
580 if (dxRemainValue->IsNumber()) {
581 scrollInfo.dx = CalcDimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
582 }
583 auto dyRemainValue = resObj->GetProperty("dyRemain");
584 if (dyRemainValue->IsNumber()) {
585 scrollInfo.dy = CalcDimension(dyRemainValue->ToNumber<float>(), DimensionUnit::VP);
586 }
587 return scrollInfo;
588 };
589 ListModel::GetInstance()->SetOnScrollBegin(std::move(onScrollBegin));
590 }
591 }
592
ScrollFrameBeginCallback(const JSCallbackInfo & args)593 void JSList::ScrollFrameBeginCallback(const JSCallbackInfo& args)
594 {
595 if (args[0]->IsFunction()) {
596 auto onScrollBegin = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
597 const Dimension& offset, const ScrollState& state) -> ScrollFrameResult {
598 ScrollFrameResult scrollRes { .offset = offset };
599 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollRes);
600 auto params = ConvertToJSValues(offset, state);
601 auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
602 if (result.IsEmpty()) {
603 LOGE("Error calling onScrollFrameBegin, result is empty.");
604 return scrollRes;
605 }
606
607 if (!result->IsObject()) {
608 LOGE("Error calling onScrollFrameBegin, result is not object.");
609 return scrollRes;
610 }
611
612 auto resObj = JSRef<JSObject>::Cast(result);
613 auto dxRemainValue = resObj->GetProperty("offsetRemain");
614 if (dxRemainValue->IsNumber()) {
615 scrollRes.offset = Dimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
616 }
617 return scrollRes;
618 };
619 ListModel::GetInstance()->SetOnScrollFrameBegin(std::move(onScrollBegin));
620 }
621 }
622
JSBind(BindingTarget globalObj)623 void JSList::JSBind(BindingTarget globalObj)
624 {
625 JSClass<JSList>::Declare("List");
626 JSClass<JSList>::StaticMethod("create", &JSList::Create);
627
628 JSClass<JSList>::StaticMethod("width", &JSList::JsWidth);
629 JSClass<JSList>::StaticMethod("height", &JSList::JsHeight);
630 JSClass<JSList>::StaticMethod("listDirection", &JSList::SetDirection);
631 JSClass<JSList>::StaticMethod("scrollBar", &JSList::SetScrollBar);
632 JSClass<JSList>::StaticMethod("edgeEffect", &JSList::SetEdgeEffect);
633 JSClass<JSList>::StaticMethod("divider", &JSList::SetDivider);
634 JSClass<JSList>::StaticMethod("editMode", &JSList::SetEditMode);
635 JSClass<JSList>::StaticMethod("cachedCount", &JSList::SetCachedCount);
636 JSClass<JSList>::StaticMethod("chainAnimation", &JSList::SetChainAnimation);
637 JSClass<JSList>::StaticMethod("chainAnimationOptions", &JSList::SetChainAnimationOptions);
638 JSClass<JSList>::StaticMethod("multiSelectable", &JSList::SetMultiSelectable);
639 JSClass<JSList>::StaticMethod("alignListItem", &JSList::SetListItemAlign);
640 JSClass<JSList>::StaticMethod("lanes", &JSList::SetLanes);
641 JSClass<JSList>::StaticMethod("sticky", &JSList::SetSticky);
642 JSClass<JSList>::StaticMethod("contentStartOffset", &JSList::SetContentStartOffset);
643 JSClass<JSList>::StaticMethod("contentEndOffset", &JSList::SetContentEndOffset);
644 JSClass<JSList>::StaticMethod("nestedScroll", &JSList::SetNestedScroll);
645 JSClass<JSList>::StaticMethod("enableScrollInteraction", &JSList::SetScrollEnabled);
646 JSClass<JSList>::StaticMethod("scrollSnapAlign", &JSList::SetScrollSnapAlign);
647 JSClass<JSList>::StaticMethod("friction", &JSList::SetFriction);
648
649 JSClass<JSList>::StaticMethod("onScroll", &JSList::ScrollCallback);
650 JSClass<JSList>::StaticMethod("onReachStart", &JSList::ReachStartCallback);
651 JSClass<JSList>::StaticMethod("onReachEnd", &JSList::ReachEndCallback);
652 JSClass<JSList>::StaticMethod("onScrollStart", &JSList::ScrollStartCallback);
653 JSClass<JSList>::StaticMethod("onScrollStop", &JSList::ScrollStopCallback);
654 JSClass<JSList>::StaticMethod("onItemDelete", &JSList::ItemDeleteCallback);
655 JSClass<JSList>::StaticMethod("onItemMove", &JSList::ItemMoveCallback);
656 JSClass<JSList>::StaticMethod("onScrollIndex", &JSList::ScrollIndexCallback);
657 JSClass<JSList>::StaticMethod("onScrollBegin", &JSList::ScrollBeginCallback);
658 JSClass<JSList>::StaticMethod("onScrollFrameBegin", &JSList::ScrollFrameBeginCallback);
659
660 JSClass<JSList>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
661 JSClass<JSList>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
662 JSClass<JSList>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
663 JSClass<JSList>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
664 JSClass<JSList>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
665 JSClass<JSList>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
666 JSClass<JSList>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
667
668 JSClass<JSList>::StaticMethod("onItemDragStart", &JSList::ItemDragStartCallback);
669 JSClass<JSList>::StaticMethod("onItemDragEnter", &JSList::ItemDragEnterCallback);
670 JSClass<JSList>::StaticMethod("onItemDragMove", &JSList::ItemDragMoveCallback);
671 JSClass<JSList>::StaticMethod("onItemDragLeave", &JSList::ItemDragLeaveCallback);
672 JSClass<JSList>::StaticMethod("onItemDrop", &JSList::ItemDropCallback);
673 JSClass<JSList>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
674
675 JSClass<JSList>::InheritAndBind<JSContainerBase>(globalObj);
676 }
677
678 } // namespace OHOS::Ace::Framework
679