1 /*
2 * Copyright (c) 2021-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_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_scrollable.h"
23 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
24 #include "bridge/declarative_frontend/jsview/models/list_model_impl.h"
25 #include "core/common/container.h"
26 #include "core/components_ng/base/view_stack_model.h"
27 #include "core/components_ng/base/view_stack_processor.h"
28 #include "core/components_ng/pattern/list/list_model.h"
29 #include "core/components_ng/pattern/list/list_model_ng.h"
30 #include "core/components_ng/pattern/list/list_position_controller.h"
31 #include "core/components_ng/pattern/scroll_bar/proxy/scroll_bar_proxy.h"
32
33 namespace OHOS::Ace {
34
35 std::unique_ptr<ListModel> ListModel::instance_ = nullptr;
36 std::mutex ListModel::mutex_;
37
GetInstance()38 ListModel* ListModel::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::ListModelNG());
45 #else
46 if (Container::IsCurrentUseNewPipeline()) {
47 instance_.reset(new NG::ListModelNG());
48 } else {
49 instance_.reset(new Framework::ListModelImpl());
50 }
51 #endif
52 }
53 }
54 return instance_.get();
55 }
56
57 } // namespace OHOS::Ace
58
59 namespace OHOS::Ace::Framework {
60
61 const std::vector<V2::ScrollSnapAlign> SCROLL_SNAP_ALIGN = { V2::ScrollSnapAlign::NONE, V2::ScrollSnapAlign::START,
62 V2::ScrollSnapAlign::CENTER, V2::ScrollSnapAlign::END };
63
64 namespace {
65 const std::regex DIMENSION_REGEX(R"(^[-+]?\d+(?:\.\d+)?(?:px|vp|fp|lpx)?$)", std::regex::icase);
66 constexpr ScrollAlign ALIGN_TABLE[] = {
67 ScrollAlign::START,
68 ScrollAlign::CENTER,
69 ScrollAlign::END,
70 ScrollAlign::AUTO,
71 };
72 }
73
SetDirection(int32_t direction)74 void JSList::SetDirection(int32_t direction)
75 {
76 ListModel::GetInstance()->SetListDirection(static_cast<Axis>(direction));
77 }
78
SetScrollBar(const JSCallbackInfo & info)79 void JSList::SetScrollBar(const JSCallbackInfo& info)
80 {
81 auto displayMode = JSScrollable::ParseDisplayMode(info, ListModel::GetInstance()->GetDisplayMode());
82 ListModel::GetInstance()->SetScrollBar(displayMode);
83 }
84
SetScrollBarColor(const std::string & color)85 void JSList::SetScrollBarColor(const std::string& color)
86 {
87 auto scrollBarColor = JSScrollable::ParseBarColor(color);
88 if (!scrollBarColor.empty()) {
89 ListModel::GetInstance()->SetScrollBarColor(scrollBarColor);
90 }
91 }
92
SetScrollBarWidth(const JSCallbackInfo & scrollWidth)93 void JSList::SetScrollBarWidth(const JSCallbackInfo& scrollWidth)
94 {
95 auto scrollBarWidth = JSScrollable::ParseBarWidth(scrollWidth);
96 if (!scrollBarWidth.empty()) {
97 ListModel::GetInstance()->SetScrollBarWidth(scrollBarWidth);
98 }
99 }
100
SetEdgeEffect(const JSCallbackInfo & info)101 void JSList::SetEdgeEffect(const JSCallbackInfo& info)
102 {
103 auto edgeEffect = JSScrollable::ParseEdgeEffect(info, EdgeEffect::SPRING);
104 auto alwaysEnabled = JSScrollable::ParseAlwaysEnable(info, false);
105 ListModel::GetInstance()->SetEdgeEffect(edgeEffect, alwaysEnabled);
106 }
107
SetEditMode(bool editMode)108 void JSList::SetEditMode(bool editMode)
109 {
110 ListModel::GetInstance()->SetEditMode(editMode);
111 }
112
SetCachedCount(const JSCallbackInfo & info)113 void JSList::SetCachedCount(const JSCallbackInfo& info)
114 {
115 int32_t cachedCount = 1;
116 ParseJsInteger<int32_t>(info[0], cachedCount);
117 cachedCount = cachedCount < 0 ? 1 : cachedCount;
118 ListModel::GetInstance()->SetCachedCount(cachedCount);
119 }
120
SetScroller(RefPtr<JSScroller> scroller)121 void JSList::SetScroller(RefPtr<JSScroller> scroller)
122 {
123 if (scroller) {
124 RefPtr<ScrollControllerBase> listController = ListModel::GetInstance()->CreateScrollController();
125 scroller->SetController(listController);
126
127 // Init scroll bar proxy.
128 auto proxy = scroller->GetScrollBarProxy();
129 if (!proxy) {
130 if (Container::IsCurrentUseNewPipeline()) {
131 proxy = AceType::MakeRefPtr<NG::ScrollBarProxy>();
132 } else {
133 proxy = AceType::MakeRefPtr<ScrollBarProxy>();
134 }
135 scroller->SetScrollBarProxy(proxy);
136 }
137 ListModel::GetInstance()->SetScroller(listController, proxy);
138 }
139 }
140
Create(const JSCallbackInfo & args)141 void JSList::Create(const JSCallbackInfo& args)
142 {
143 ListModel::GetInstance()->Create();
144 if (args.Length() >= 1 && args[0]->IsObject()) {
145 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
146 JSRef<JSVal> spaceValue = obj->GetProperty("space");
147 if (!spaceValue->IsNull()) {
148 CalcDimension space;
149 ConvertFromJSValue(spaceValue, space);
150 ListModel::GetInstance()->SetSpace(space);
151 }
152 int32_t initialIndex = 0;
153 if (ConvertFromJSValue(obj->GetProperty("initialIndex"), initialIndex) && initialIndex >= 0) {
154 ListModel::GetInstance()->SetInitialIndex(initialIndex);
155 }
156 JSRef<JSVal> scrollerValue = obj->GetProperty("scroller");
157 if (scrollerValue->IsObject()) {
158 void* scroller = JSRef<JSObject>::Cast(scrollerValue)->Unwrap<JSScroller>();
159 RefPtr<JSScroller> jsScroller = Referenced::Claim(reinterpret_cast<JSScroller*>(scroller));
160 jsScroller->SetInstanceId(Container::CurrentId());
161 SetScroller(jsScroller);
162 }
163 }
164
165 args.ReturnSelf();
166 }
167
SetChainAnimation(bool enableChainAnimation)168 void JSList::SetChainAnimation(bool enableChainAnimation)
169 {
170 ListModel::GetInstance()->SetChainAnimation(enableChainAnimation);
171 }
172
SetChainAnimationOptions(const JSCallbackInfo & info)173 void JSList::SetChainAnimationOptions(const JSCallbackInfo& info)
174 {
175 if (info.Length() < 1) {
176 return;
177 }
178
179 if (info[0]->IsObject()) {
180 RefPtr<ListTheme> listTheme = GetTheme<ListTheme>();
181 CHECK_NULL_VOID(listTheme);
182 ChainAnimationOptions options = {
183 .minSpace = listTheme->GetChainMinSpace(),
184 .maxSpace = listTheme->GetChainMaxSpace(),
185 .conductivity = listTheme->GetChainConductivity(),
186 .intensity = listTheme->GetChainIntensity(),
187 .edgeEffect = 0,
188 .stiffness = listTheme->GetChainStiffness(),
189 .damping = listTheme->GetChainDamping(),
190 };
191 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
192 ParseJsDimensionVp(jsObj->GetProperty("minSpace"), options.minSpace);
193 ParseJsDimensionVp(jsObj->GetProperty("maxSpace"), options.maxSpace);
194 JSViewAbstract::ParseJsDouble(jsObj->GetProperty("conductivity"), options.conductivity);
195 JSViewAbstract::ParseJsDouble(jsObj->GetProperty("intensity"), options.intensity);
196 JSViewAbstract::ParseJsInt32(jsObj->GetProperty("edgeEffect"), options.edgeEffect);
197 JSViewAbstract::ParseJsDouble(jsObj->GetProperty("stiffness"), options.stiffness);
198 JSViewAbstract::ParseJsDouble(jsObj->GetProperty("damping"), options.damping);
199 ListModel::GetInstance()->SetChainAnimationOptions(options);
200 }
201 }
202
JsWidth(const JSCallbackInfo & info)203 void JSList::JsWidth(const JSCallbackInfo& info)
204 {
205 JSViewAbstract::JsWidth(info);
206 ListModel::GetInstance()->SetHasWidth(true);
207 }
208
JsHeight(const JSCallbackInfo & info)209 void JSList::JsHeight(const JSCallbackInfo& info)
210 {
211 JSViewAbstract::JsHeight(info);
212 ListModel::GetInstance()->SetHasHeight(true);
213 }
214
SetListItemAlign(int32_t itemAlignment)215 void JSList::SetListItemAlign(int32_t itemAlignment)
216 {
217 ListModel::GetInstance()->SetListItemAlign(static_cast<V2::ListItemAlign>(itemAlignment));
218 }
219
SetLanes(const JSCallbackInfo & info)220 void JSList::SetLanes(const JSCallbackInfo& info)
221 {
222 if (info.Length() < 1) {
223 return;
224 }
225
226 if (info.Length() >= 2 && !(info[1]->IsNull())) { /* 2: parameter count */
227 CalcDimension laneGutter;
228 if (JSViewAbstract::ParseJsDimensionVp(info[1], laneGutter)) {
229 if (laneGutter.IsNegative()) {
230 laneGutter.Reset();
231 }
232 }
233 ListModel::GetInstance()->SetLaneGutter(laneGutter);
234 }
235
236 int32_t laneNum = 1;
237 if (ParseJsInteger<int32_t>(info[0], laneNum)) {
238 // when [lanes] is set, [laneConstrain_] of list component will be reset to std::nullopt
239 ListModel::GetInstance()->SetLanes(laneNum);
240 ListModel::GetInstance()->SetLaneConstrain(-1.0_vp, -1.0_vp);
241 return;
242 }
243 if (info[0]->IsObject()) {
244 JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
245 auto minLengthParam = jsObj->GetProperty("minLength");
246 auto maxLengthParam = jsObj->GetProperty("maxLength");
247 if (minLengthParam->IsNull() || maxLengthParam->IsNull()) {
248 LOGW("minLength and maxLength are not both set");
249 return;
250 }
251 CalcDimension minLengthValue;
252 CalcDimension maxLengthValue;
253 if (!ParseJsDimensionVp(minLengthParam, minLengthValue)
254 || !ParseJsDimensionVp(maxLengthParam, maxLengthValue)) {
255 ListModel::GetInstance()->SetLanes(1);
256 ListModel::GetInstance()->SetLaneConstrain(-1.0_vp, -1.0_vp);
257 return;
258 }
259 ListModel::GetInstance()->SetLaneConstrain(minLengthValue, maxLengthValue);
260 ListModel::GetInstance()->SetLanes(1);
261 }
262 }
263
SetSticky(int32_t sticky)264 void JSList::SetSticky(int32_t sticky)
265 {
266 ListModel::GetInstance()->SetSticky(static_cast<V2::StickyStyle>(sticky));
267 }
268
SetContentStartOffset(const JSCallbackInfo & info)269 void JSList::SetContentStartOffset(const JSCallbackInfo& info)
270 {
271 double value = 0.0;
272 ParseJsDouble(info[0], value);
273 ListModel::GetInstance()->SetContentStartOffset(value);
274 }
275
SetContentEndOffset(const JSCallbackInfo & info)276 void JSList::SetContentEndOffset(const JSCallbackInfo& info)
277 {
278 double value = 0.0;
279 ParseJsDouble(info[0], value);
280 ListModel::GetInstance()->SetContentEndOffset(value);
281 }
282
SetScrollSnapAlign(int32_t scrollSnapAlign)283 void JSList::SetScrollSnapAlign(int32_t scrollSnapAlign)
284 {
285 V2::ScrollSnapAlign param;
286 if (scrollSnapAlign < 0 || scrollSnapAlign >= static_cast<int32_t>(SCROLL_SNAP_ALIGN.size())) {
287 param = V2::ScrollSnapAlign::NONE;
288 } else {
289 param = V2::ScrollSnapAlign(scrollSnapAlign);
290 }
291 ListModel::GetInstance()->SetScrollSnapAlign(param);
292 }
293
SetDivider(const JSCallbackInfo & args)294 void JSList::SetDivider(const JSCallbackInfo& args)
295 {
296 V2::ItemDivider divider;
297 if (args.Length() >= 1 && args[0]->IsObject()) {
298 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
299 bool needReset = obj->GetProperty("strokeWidth")->IsString() &&
300 !std::regex_match(obj->GetProperty("strokeWidth")->ToString(), DIMENSION_REGEX);
301 if (needReset || !ConvertFromJSValue(obj->GetProperty("strokeWidth"), divider.strokeWidth)) {
302 divider.strokeWidth = 0.0_vp;
303 }
304 if (!ConvertFromJSValue(obj->GetProperty("color"), divider.color)) {
305 // Failed to get color from param, using default color defined in theme
306 RefPtr<ListTheme> listTheme = GetTheme<ListTheme>();
307 if (listTheme) {
308 divider.color = listTheme->GetDividerColor();
309 }
310 }
311
312 needReset = obj->GetProperty("startMargin")->IsString() &&
313 !std::regex_match(obj->GetProperty("startMargin")->ToString(), DIMENSION_REGEX);
314 if (needReset || !ConvertFromJSValue(obj->GetProperty("startMargin"), divider.startMargin)) {
315 divider.startMargin = 0.0_vp;
316 }
317
318 needReset = obj->GetProperty("endMargin")->IsString() &&
319 !std::regex_match(obj->GetProperty("endMargin")->ToString(), DIMENSION_REGEX);
320 if (needReset || !ConvertFromJSValue(obj->GetProperty("endMargin"), divider.endMargin)) {
321 divider.endMargin = 0.0_vp;
322 }
323 }
324 ListModel::GetInstance()->SetDivider(divider);
325 args.ReturnSelf();
326 }
327
SetNestedScroll(const JSCallbackInfo & args)328 void JSList::SetNestedScroll(const JSCallbackInfo& args)
329 {
330 NestedScrollOptions nestedOpt = {
331 .forward = NestedScrollMode::SELF_ONLY,
332 .backward = NestedScrollMode::SELF_ONLY,
333 };
334 if (args.Length() < 1 || !args[0]->IsObject()) {
335 ListModel::GetInstance()->SetNestedScroll(nestedOpt);
336 return;
337 }
338 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
339 int32_t froward = 0;
340 JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollForward"), froward);
341 if (froward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
342 froward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
343 froward = 0;
344 }
345 int32_t backward = 0;
346 JSViewAbstract::ParseJsInt32(obj->GetProperty("scrollBackward"), backward);
347 if (backward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
348 backward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
349 backward = 0;
350 }
351 nestedOpt.forward = static_cast<NestedScrollMode>(froward);
352 nestedOpt.backward = static_cast<NestedScrollMode>(backward);
353 ListModel::GetInstance()->SetNestedScroll(nestedOpt);
354 args.ReturnSelf();
355 }
356
SetScrollEnabled(const JSCallbackInfo & args)357 void JSList::SetScrollEnabled(const JSCallbackInfo& args)
358 {
359 ListModel::GetInstance()->SetScrollEnabled(args[0]->IsBoolean() ? args[0]->ToBoolean() : true);
360 }
361
ScrollCallback(const JSCallbackInfo & args)362 void JSList::ScrollCallback(const JSCallbackInfo& args)
363 {
364 if (args[0]->IsFunction()) {
365 auto onScroll = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
366 const CalcDimension& scrollOffset, const ScrollState& scrollState) {
367 auto params = ConvertToJSValues(scrollOffset, scrollState);
368 func->Call(JSRef<JSObject>(), params.size(), params.data());
369 return;
370 };
371 ListModel::GetInstance()->SetOnScroll(std::move(onScroll));
372 }
373 args.ReturnSelf();
374 }
375
SetFriction(const JSCallbackInfo & info)376 void JSList::SetFriction(const JSCallbackInfo& info)
377 {
378 double friction = -1.0;
379 if (!JSViewAbstract::ParseJsDouble(info[0], friction)) {
380 friction = -1.0;
381 }
382 ListModel::GetInstance()->SetFriction(friction);
383 }
384
ReachStartCallback(const JSCallbackInfo & args)385 void JSList::ReachStartCallback(const JSCallbackInfo& args)
386 {
387 if (args[0]->IsFunction()) {
388 auto onReachStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
389 func->Call(JSRef<JSObject>());
390 return;
391 };
392 ListModel::GetInstance()->SetOnReachStart(std::move(onReachStart));
393 }
394 args.ReturnSelf();
395 }
396
ReachEndCallback(const JSCallbackInfo & args)397 void JSList::ReachEndCallback(const JSCallbackInfo& args)
398 {
399 if (args[0]->IsFunction()) {
400 auto onReachEnd = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
401 func->Call(JSRef<JSObject>());
402 return;
403 };
404 ListModel::GetInstance()->SetOnReachEnd(std::move(onReachEnd));
405 }
406 args.ReturnSelf();
407 }
408
ScrollStartCallback(const JSCallbackInfo & args)409 void JSList::ScrollStartCallback(const JSCallbackInfo& args)
410 {
411 if (args[0]->IsFunction()) {
412 auto onScrollStart = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
413 func->Call(JSRef<JSObject>());
414 return;
415 };
416 ListModel::GetInstance()->SetOnScrollStart(std::move(onScrollStart));
417 }
418 args.ReturnSelf();
419 }
420
ScrollStopCallback(const JSCallbackInfo & args)421 void JSList::ScrollStopCallback(const JSCallbackInfo& args)
422 {
423 if (args[0]->IsFunction()) {
424 auto onScrollStop = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]() {
425 func->Call(JSRef<JSObject>());
426 return;
427 };
428 ListModel::GetInstance()->SetOnScrollStop(std::move(onScrollStop));
429 }
430 args.ReturnSelf();
431 }
432
ItemDeleteCallback(const JSCallbackInfo & args)433 void JSList::ItemDeleteCallback(const JSCallbackInfo& args)
434 {
435 if (args[0]->IsFunction()) {
436 auto onItemDelete = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
437 int32_t index) -> bool {
438 auto params = ConvertToJSValues(index);
439 func->Call(JSRef<JSObject>(), params.size(), params.data());
440 return true;
441 };
442 ListModel::GetInstance()->SetOnItemDelete(std::move(onItemDelete));
443 }
444 args.ReturnSelf();
445 }
446
ItemMoveCallback(const JSCallbackInfo & args)447 void JSList::ItemMoveCallback(const JSCallbackInfo& args)
448 {
449 if (args[0]->IsFunction()) {
450 auto onItemMove = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
451 int32_t start, int32_t end) -> bool {
452 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, false);
453 auto params = ConvertToJSValues(start, end);
454 auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
455 if (!result.IsEmpty() && result->IsBoolean()) {
456 return result->ToBoolean();
457 }
458 return true;
459 };
460 ListModel::GetInstance()->SetOnItemMove(std::move(onItemMove));
461 }
462 args.ReturnSelf();
463 }
464
ScrollIndexCallback(const JSCallbackInfo & args)465 void JSList::ScrollIndexCallback(const JSCallbackInfo& args)
466 {
467 if (args[0]->IsFunction()) {
468 auto onScrollIndex = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
469 const int32_t start, const int32_t end, const int32_t center) {
470 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
471 auto params = ConvertToJSValues(start, end, center);
472 func->Call(JSRef<JSObject>(), params.size(), params.data());
473 return;
474 };
475 ListModel::GetInstance()->SetOnScrollIndex(std::move(onScrollIndex));
476 }
477 args.ReturnSelf();
478 }
479
ItemDragStartCallback(const JSCallbackInfo & info)480 void JSList::ItemDragStartCallback(const JSCallbackInfo& info)
481 {
482 if (!info[0]->IsFunction()) {
483 return;
484 }
485
486 RefPtr<JsDragFunction> jsOnDragFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
487 WeakPtr<NG::FrameNode> frameNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
488 auto onItemDragStart = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragFunc), node = frameNode](
489 const ItemDragInfo& dragInfo, int32_t itemIndex) -> RefPtr<AceType> {
490 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, nullptr);
491 auto ret = func->ItemDragStartExecute(dragInfo, itemIndex);
492 if (!ret->IsObject()) {
493 return nullptr;
494 }
495
496 auto builderObj = JSRef<JSObject>::Cast(ret);
497 auto builder = builderObj->GetProperty("builder");
498 if (!builder->IsFunction()) {
499 return nullptr;
500 }
501 auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(builder));
502 if (!builderFunc) {
503 return nullptr;
504 }
505 // use another VSP instance while executing the builder function
506 ViewStackModel::GetInstance()->NewScope();
507 {
508 ACE_SCORING_EVENT("List.onItemDragStart.builder");
509 PipelineContext::SetCallBackNode(node);
510 builderFunc->Execute();
511 }
512 return ViewStackModel::GetInstance()->Finish();
513 };
514 ListModel::GetInstance()->SetOnItemDragStart(std::move(onItemDragStart));
515 }
516
ItemDragEnterCallback(const JSCallbackInfo & info)517 void JSList::ItemDragEnterCallback(const JSCallbackInfo& info)
518 {
519 if (!info[0]->IsFunction()) {
520 return;
521 }
522 WeakPtr<NG::FrameNode> frameNode = NG::ViewStackProcessor::GetInstance()->GetMainFrameNode();
523 RefPtr<JsDragFunction> jsOnDragEnterFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
524 auto onItemDragEnter = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragEnterFunc),
525 node = frameNode](const ItemDragInfo& dragInfo) {
526 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
527 ACE_SCORING_EVENT("List.onItemDragEnter");
528 PipelineContext::SetCallBackNode(node);
529 func->ItemDragEnterExecute(dragInfo);
530 };
531 ListModel::GetInstance()->SetOnItemDragEnter(std::move(onItemDragEnter));
532 }
533
ItemDragMoveCallback(const JSCallbackInfo & info)534 void JSList::ItemDragMoveCallback(const JSCallbackInfo& info)
535 {
536 if (!info[0]->IsFunction()) {
537 return;
538 }
539
540 RefPtr<JsDragFunction> jsOnDragMoveFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
541 auto onItemDragMove = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragMoveFunc)](
542 const ItemDragInfo& dragInfo, int32_t itemIndex, int32_t insertIndex) {
543 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
544 ACE_SCORING_EVENT("List.onItemDragMove");
545 func->ItemDragMoveExecute(dragInfo, itemIndex, insertIndex);
546 };
547 ListModel::GetInstance()->SetOnItemDragMove(std::move(onItemDragMove));
548 }
549
ItemDragLeaveCallback(const JSCallbackInfo & info)550 void JSList::ItemDragLeaveCallback(const JSCallbackInfo& info)
551 {
552 if (!info[0]->IsFunction()) {
553 return;
554 }
555
556 RefPtr<JsDragFunction> jsOnDragLeaveFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
557 auto onItemDragLeave = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragLeaveFunc)](
558 const ItemDragInfo& dragInfo, int32_t itemIndex) {
559 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
560 ACE_SCORING_EVENT("List.onItemDragLeave");
561 func->ItemDragLeaveExecute(dragInfo, itemIndex);
562 };
563 ListModel::GetInstance()->SetOnItemDragLeave(std::move(onItemDragLeave));
564 }
565
ItemDropCallback(const JSCallbackInfo & info)566 void JSList::ItemDropCallback(const JSCallbackInfo& info)
567 {
568 if (!info[0]->IsFunction()) {
569 return;
570 }
571
572 RefPtr<JsDragFunction> jsOnDropFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
573 auto onItemDrop = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDropFunc)](
574 const ItemDragInfo& dragInfo, int32_t itemIndex, int32_t insertIndex, bool isSuccess) {
575 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
576 ACE_SCORING_EVENT("List.onItemDrop");
577 func->ItemDropExecute(dragInfo, itemIndex, insertIndex, isSuccess);
578 };
579 ListModel::GetInstance()->SetOnItemDrop(onItemDrop);
580 }
581
SetMultiSelectable(bool multiSelectable)582 void JSList::SetMultiSelectable(bool multiSelectable)
583 {
584 ListModel::GetInstance()->SetMultiSelectable(multiSelectable);
585 }
586
ScrollBeginCallback(const JSCallbackInfo & args)587 void JSList::ScrollBeginCallback(const JSCallbackInfo& args)
588 {
589 if (args[0]->IsFunction()) {
590 auto onScrollBegin = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
591 const CalcDimension& dx, const CalcDimension& dy) -> ScrollInfo {
592 ScrollInfo scrollInfo { .dx = dx, .dy = dy };
593 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollInfo);
594 auto params = ConvertToJSValues(dx, dy);
595 auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
596 if (result.IsEmpty()) {
597 return scrollInfo;
598 }
599
600 if (!result->IsObject()) {
601 return scrollInfo;
602 }
603
604 auto resObj = JSRef<JSObject>::Cast(result);
605 auto dxRemainValue = resObj->GetProperty("dxRemain");
606 if (dxRemainValue->IsNumber()) {
607 scrollInfo.dx = CalcDimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
608 }
609 auto dyRemainValue = resObj->GetProperty("dyRemain");
610 if (dyRemainValue->IsNumber()) {
611 scrollInfo.dy = CalcDimension(dyRemainValue->ToNumber<float>(), DimensionUnit::VP);
612 }
613 return scrollInfo;
614 };
615 ListModel::GetInstance()->SetOnScrollBegin(std::move(onScrollBegin));
616 }
617 }
618
ScrollFrameBeginCallback(const JSCallbackInfo & args)619 void JSList::ScrollFrameBeginCallback(const JSCallbackInfo& args)
620 {
621 if (args[0]->IsFunction()) {
622 auto onScrollBegin = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
623 const Dimension& offset, const ScrollState& state) -> ScrollFrameResult {
624 ScrollFrameResult scrollRes { .offset = offset };
625 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, scrollRes);
626 auto params = ConvertToJSValues(offset, state);
627 auto result = func->Call(JSRef<JSObject>(), params.size(), params.data());
628 if (result.IsEmpty()) {
629 return scrollRes;
630 }
631
632 if (!result->IsObject()) {
633 return scrollRes;
634 }
635
636 auto resObj = JSRef<JSObject>::Cast(result);
637 auto dxRemainValue = resObj->GetProperty("offsetRemain");
638 if (dxRemainValue->IsNumber()) {
639 scrollRes.offset = Dimension(dxRemainValue->ToNumber<float>(), DimensionUnit::VP);
640 }
641 return scrollRes;
642 };
643 ListModel::GetInstance()->SetOnScrollFrameBegin(std::move(onScrollBegin));
644 }
645 }
646
JSBind(BindingTarget globalObj)647 void JSList::JSBind(BindingTarget globalObj)
648 {
649 JSClass<JSList>::Declare("List");
650 JSClass<JSList>::StaticMethod("create", &JSList::Create);
651
652 JSClass<JSList>::StaticMethod("width", &JSList::JsWidth);
653 JSClass<JSList>::StaticMethod("height", &JSList::JsHeight);
654 JSClass<JSList>::StaticMethod("clip", &JSScrollable::JsClip);
655 JSClass<JSList>::StaticMethod("listDirection", &JSList::SetDirection);
656 JSClass<JSList>::StaticMethod("scrollBar", &JSList::SetScrollBar);
657 JSClass<JSList>::StaticMethod("scrollBarWidth", &JSList::SetScrollBarWidth);
658 JSClass<JSList>::StaticMethod("scrollBarColor", &JSList::SetScrollBarColor);
659 JSClass<JSList>::StaticMethod("edgeEffect", &JSList::SetEdgeEffect);
660 JSClass<JSList>::StaticMethod("divider", &JSList::SetDivider);
661 JSClass<JSList>::StaticMethod("editMode", &JSList::SetEditMode);
662 JSClass<JSList>::StaticMethod("cachedCount", &JSList::SetCachedCount);
663 JSClass<JSList>::StaticMethod("chainAnimation", &JSList::SetChainAnimation);
664 JSClass<JSList>::StaticMethod("chainAnimationOptions", &JSList::SetChainAnimationOptions);
665 JSClass<JSList>::StaticMethod("multiSelectable", &JSList::SetMultiSelectable);
666 JSClass<JSList>::StaticMethod("alignListItem", &JSList::SetListItemAlign);
667 JSClass<JSList>::StaticMethod("lanes", &JSList::SetLanes);
668 JSClass<JSList>::StaticMethod("sticky", &JSList::SetSticky);
669 JSClass<JSList>::StaticMethod("contentStartOffset", &JSList::SetContentStartOffset);
670 JSClass<JSList>::StaticMethod("contentEndOffset", &JSList::SetContentEndOffset);
671 JSClass<JSList>::StaticMethod("nestedScroll", &JSList::SetNestedScroll);
672 JSClass<JSList>::StaticMethod("enableScrollInteraction", &JSList::SetScrollEnabled);
673 JSClass<JSList>::StaticMethod("scrollSnapAlign", &JSList::SetScrollSnapAlign);
674 JSClass<JSList>::StaticMethod("friction", &JSList::SetFriction);
675
676 JSClass<JSList>::StaticMethod("onScroll", &JSList::ScrollCallback);
677 JSClass<JSList>::StaticMethod("onReachStart", &JSList::ReachStartCallback);
678 JSClass<JSList>::StaticMethod("onReachEnd", &JSList::ReachEndCallback);
679 JSClass<JSList>::StaticMethod("onScrollStart", &JSList::ScrollStartCallback);
680 JSClass<JSList>::StaticMethod("onScrollStop", &JSList::ScrollStopCallback);
681 JSClass<JSList>::StaticMethod("onItemDelete", &JSList::ItemDeleteCallback);
682 JSClass<JSList>::StaticMethod("onItemMove", &JSList::ItemMoveCallback);
683 JSClass<JSList>::StaticMethod("onScrollIndex", &JSList::ScrollIndexCallback);
684 JSClass<JSList>::StaticMethod("onScrollBegin", &JSList::ScrollBeginCallback);
685 JSClass<JSList>::StaticMethod("onScrollFrameBegin", &JSList::ScrollFrameBeginCallback);
686
687 JSClass<JSList>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
688 JSClass<JSList>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
689 JSClass<JSList>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
690 JSClass<JSList>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
691 JSClass<JSList>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
692 JSClass<JSList>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
693 JSClass<JSList>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
694
695 JSClass<JSList>::StaticMethod("onItemDragStart", &JSList::ItemDragStartCallback);
696 JSClass<JSList>::StaticMethod("onItemDragEnter", &JSList::ItemDragEnterCallback);
697 JSClass<JSList>::StaticMethod("onItemDragMove", &JSList::ItemDragMoveCallback);
698 JSClass<JSList>::StaticMethod("onItemDragLeave", &JSList::ItemDragLeaveCallback);
699 JSClass<JSList>::StaticMethod("onItemDrop", &JSList::ItemDropCallback);
700 JSClass<JSList>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
701
702 JSClass<JSList>::InheritAndBind<JSScrollableBase>(globalObj);
703 }
704
JSBind(BindingTarget globalObj)705 void JSListScroller::JSBind(BindingTarget globalObj)
706 {
707 JSClass<JSListScroller>::Declare("ListScroller");
708 JSClass<JSListScroller>::CustomMethod("getItemRectInGroup", &JSListScroller::GetItemRectInGroup);
709 JSClass<JSListScroller>::CustomMethod("scrollToItemInGroup", &JSListScroller::ScrollToItemInGroup);
710 JSClass<JSListScroller>::CustomMethod("closeAllSwipeActions", &JSListScroller::CloseAllSwipeActions);
711 JSClass<JSListScroller>::InheritAndBind<JSScroller>(globalObj, JSListScroller::Constructor,
712 JSListScroller::Destructor);
713 }
714
Constructor(const JSCallbackInfo & args)715 void JSListScroller::Constructor(const JSCallbackInfo& args)
716 {
717 auto scroller = Referenced::MakeRefPtr<JSListScroller>();
718 scroller->IncRefCount();
719 args.SetReturnValue(Referenced::RawPtr(scroller));
720 }
721
Destructor(JSListScroller * scroller)722 void JSListScroller::Destructor(JSListScroller* scroller)
723 {
724 if (scroller != nullptr) {
725 scroller->DecRefCount();
726 }
727 }
728
GetItemRectInGroup(const JSCallbackInfo & args)729 void JSListScroller::GetItemRectInGroup(const JSCallbackInfo& args)
730 {
731 int32_t index = -1;
732 int32_t indexInGroup = -1;
733 // Parameter passed into function must be 2.
734 if (args.Length() != 2 || !ConvertFromJSValue(args[0], index) || !ConvertFromJSValue(args[1], indexInGroup)) {
735 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
736 return;
737 }
738 auto scrollController = GetController().Upgrade();
739 if (scrollController) {
740 ContainerScope scope(GetInstanceId());
741 auto rectObj = CreateRectangle(scrollController->GetItemRectInGroup(index, indexInGroup));
742 JSRef<JSVal> rect = JSRef<JSObject>::Cast(rectObj);
743 args.SetReturnValue(rect);
744 } else {
745 JSException::Throw(ERROR_CODE_NAMED_ROUTE_ERROR, "%s", "Controller not bound to component.");
746 }
747 }
748
ScrollToItemInGroup(const JSCallbackInfo & args)749 void JSListScroller::ScrollToItemInGroup(const JSCallbackInfo& args)
750 {
751 int32_t index = 0;
752 int32_t indexInGroup = 0;
753 bool smooth = false;
754 ScrollAlign align = ScrollAlign::NONE;
755
756 if (args.Length() < 1) {
757 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
758 return;
759 }
760
761 auto scrollController = GetController().Upgrade();
762 if (!scrollController) {
763 JSException::Throw(ERROR_CODE_NAMED_ROUTE_ERROR, "%s", "Controller not bound to component.");
764 return;
765 }
766
767 if (!ConvertFromJSValue(args[0], index)) {
768 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
769 return;
770 }
771 if (index < 0) {
772 return;
773 }
774
775 if (args.Length() >= 2) { // 2 is param count
776 if (!ConvertFromJSValue(args[1], indexInGroup)) {
777 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
778 return;
779 }
780 if (indexInGroup < 0) {
781 return;
782 }
783 }
784
785 if (args.Length() >= 3) { // 3 is param count
786 if (!args[2]->IsBoolean()) { // 2 is the param index of smooth
787 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
788 return;
789 }
790 smooth = args[2]->ToBoolean(); // 2 is the param index of smooth
791 }
792
793 if (args.Length() == 4) { // 4 is param count
794 if (!ConvertFromJSValue(args[3], ALIGN_TABLE, align)) { // 3 is param count of align
795 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed.");
796 return;
797 }
798 }
799
800 ContainerScope scope(GetInstanceId());
801 scrollController->JumpToItemInGroup(index, indexInGroup, smooth, align, SCROLL_FROM_JUMP);
802 }
803
CloseAllSwipeActions(const JSCallbackInfo & args)804 void JSListScroller::CloseAllSwipeActions(const JSCallbackInfo& args)
805 {
806 if (args.Length() != 0 && args.Length() != 1) {
807 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "too many parameters.");
808 return;
809 }
810 OnFinishFunc onFinishCallBack;
811 if (args.Length() == 1) {
812 if (!args[0]->IsObject()) {
813 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "options param must be object.");
814 return;
815 }
816 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
817 auto onFinishProperty = obj->GetProperty("onFinish");
818 if (onFinishProperty->IsFunction()) {
819 RefPtr<JsFunction> jsOnFinishFunc =
820 AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinishProperty));
821 onFinishCallBack = [execCtx = args.GetExecutionContext(),
822 func = std::move(jsOnFinishFunc)]() {
823 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
824 func->Execute();
825 return;
826 };
827 } else {
828 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "onFinish param must be function.");
829 return;
830 }
831 }
832 auto scrollController = GetController().Upgrade();
833 if (!scrollController) {
834 JSException::Throw(ERROR_CODE_NAMED_ROUTE_ERROR, "%s", "Controller not bound to component.");
835 return;
836 }
837 ContainerScope scope(GetInstanceId());
838 scrollController->CloseAllSwipeActions(std::move(onFinishCallBack));
839 }
840 } // namespace OHOS::Ace::Framework
841