• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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