• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "bridge/declarative_frontend/jsview/js_repeat_virtual_scroll_2.h"
17 
18 #include <string>
19 
20 #include "base/log/ace_trace.h"
21 #include "base/log/log_wrapper.h"
22 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
23 #include "core/components_ng/syntax/repeat_virtual_scroll_2_model_ng.h"
24 
25 namespace OHOS::Ace {
26 
27 std::unique_ptr<RepeatVirtualScroll2Model> RepeatVirtualScroll2Model::instance_ = nullptr;
28 #define UNUSED_CACHED_SIZE_PARAM 2
29 
GetInstance()30 RepeatVirtualScroll2Model* RepeatVirtualScroll2Model::GetInstance()
31 {
32     if (!instance_) {
33         instance_.reset(new NG::RepeatVirtualScroll2ModelNG());
34     }
35     return instance_.get();
36 }
37 } // namespace OHOS::Ace
38 
39 namespace OHOS::Ace::Framework {
40 
41 enum {
42     PARAM_ARR_LEN = 0,
43     PARAM_TOTAL_COUNT = 1,
44     PARAM_HANDLERS = 2,
45     PARAM_SIZE = 3,
46 };
47 
ParseAndVerifyParams(const JSCallbackInfo & info)48 static bool ParseAndVerifyParams(const JSCallbackInfo& info)
49 {
50     if (info.Length() != PARAM_SIZE || !info[PARAM_ARR_LEN]->IsNumber() || !info[PARAM_TOTAL_COUNT]->IsNumber() ||
51         !info[PARAM_HANDLERS]->IsObject()) {
52         return false;
53     }
54 
55     auto handlers = JSRef<JSObject>::Cast(info[PARAM_HANDLERS]);
56     return true;
57 }
58 
Create(const JSCallbackInfo & info)59 void JSRepeatVirtualScroll2::Create(const JSCallbackInfo& info)
60 {
61     if (!ParseAndVerifyParams(info)) {
62         TAG_LOGW(AceLogTag::ACE_REPEAT, "Invalid arguments for RepeatVirtualScroll2");
63         return;
64     }
65 
66     // arg 0 arrLen : number
67     auto arrLen = info[PARAM_ARR_LEN]->ToNumber<uint32_t>();
68 
69     // arg 1 totalCount : number
70     auto totalCount = info[PARAM_TOTAL_COUNT]->ToNumber<uint32_t>();
71 
72     // arg 2 onGetRid4Index(number int32_t) : number(uint32_t)
73     auto handlers = JSRef<JSObject>::Cast(info[PARAM_HANDLERS]);
74     auto onGetRid4IndexFunc = handlers->GetProperty("onGetRid4Index");
75     if (!onGetRid4IndexFunc->IsFunction()) {
76         return;
77     }
78     auto onGetRid4Index = [execCtx = info.GetExecutionContext(), func = JSRef<JSFunc>::Cast(onGetRid4IndexFunc)](
79                               int32_t forIndex) -> std::pair<uint32_t, uint32_t> {
80         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, std::pair<uint32_t, uint32_t>(0, 0));
81         auto params = ConvertToJSValues(forIndex);
82         JSRef<JSVal> jsVal = func->Call(JSRef<JSObject>(), params.size(), params.data());
83         // convert js-array to std::pair
84         if (!jsVal->IsArray() || JSRef<JSArray>::Cast(jsVal)->Length() != 2) {
85             TAG_LOGW(AceLogTag::ACE_REPEAT, "jsVal should be array.");
86             return std::pair<uint32_t, uint32_t>(0, 0);
87         }
88         JSRef<JSArray> jsArr = JSRef<JSArray>::Cast(jsVal);
89         return std::pair<uint32_t, uint32_t>(
90             jsArr->GetValueAt(0)->ToNumber<uint32_t>(), jsArr->GetValueAt(1)->ToNumber<uint32_t>());
91     };
92 
93     auto onRecycleItemsFunc = handlers->GetProperty("onRecycleItems");
94     if (!onRecycleItemsFunc->IsFunction()) {
95         return;
96     }
97     auto onRecycleItems = [execCtx = info.GetExecutionContext(), func = JSRef<JSFunc>::Cast(onRecycleItemsFunc)](
98                               int32_t fromIndex, int32_t toIndex) -> void {
99         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
100         auto params = ConvertToJSValues(fromIndex, toIndex);
101         func->Call(JSRef<JSObject>(), params.size(), params.data());
102     };
103 
104     auto onActiveRangeFunc = handlers->GetProperty("onActiveRange");
105     if (!onActiveRangeFunc->IsFunction()) {
106         return;
107     }
108     auto onActiveRange = [execCtx = info.GetExecutionContext(), func = JSRef<JSFunc>::Cast(onActiveRangeFunc)](
109         int32_t fromIndex, int32_t toIndex, int32_t vStart, int32_t vEnd, bool isLoop, bool forceUpdate) -> void {
110         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
111         auto params = ConvertToJSValues(
112             fromIndex != INT32_MAX ? fromIndex : std::numeric_limits<double>::quiet_NaN(),
113             toIndex != INT32_MAX ? toIndex : std::numeric_limits<double>::quiet_NaN(),
114             vStart != INT32_MAX ? vStart : std::numeric_limits<double>::quiet_NaN(),
115             vEnd != INT32_MAX ? vEnd : std::numeric_limits<double>::quiet_NaN(),
116             isLoop, forceUpdate);
117         func->Call(JSRef<JSObject>(), params.size(), params.data());
118     };
119 
120     auto onMoveFromToFunc = handlers->GetProperty("onMoveFromTo");
121     if (!onMoveFromToFunc->IsFunction()) {
122         return;
123     }
124     auto onMoveFromTo = [execCtx = info.GetExecutionContext(), func = JSRef<JSFunc>::Cast(onMoveFromToFunc)](
125                               int32_t moveFrom, int32_t moveTo) -> void {
126         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
127         auto params = ConvertToJSValues(moveFrom, moveTo);
128         func->Call(JSRef<JSObject>(), params.size(), params.data());
129     };
130 
131     auto onPurgeFunc = handlers->GetProperty("onPurge");
132     if (!onPurgeFunc->IsFunction()) {
133         return;
134     }
135     auto onPurge = [execCtx = info.GetExecutionContext(), func = JSRef<JSFunc>::Cast(onPurgeFunc)]() {
136         JSRef<JSVal> jsVal = func->Call(JSRef<JSObject>(), 0, nullptr);
137     };
138 
139     RepeatVirtualScroll2Model::GetInstance()->Create(
140         arrLen, totalCount, onGetRid4Index, onRecycleItems, onActiveRange, onMoveFromTo, onPurge);
141 }
142 
RemoveNode(const JSCallbackInfo & info)143 void JSRepeatVirtualScroll2::RemoveNode(const JSCallbackInfo& info)
144 {
145     ACE_SCOPED_TRACE("RepeatVirtualScroll:RemoveNode");
146     if (!info[0]->IsNumber()) {
147         TAG_LOGE(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll2::RemoveNode - invalid parameter ERROR.");
148         return;
149     }
150     TAG_LOGD(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll2::RemoveNode");
151     auto rid = info[0]->ToNumber<uint32_t>();
152     RepeatVirtualScroll2Model::GetInstance()->RemoveNode(rid);
153 }
154 
155 // setInvalid(repeatElmtId : number, fromIndex : number)
SetInvalid(const JSCallbackInfo & info)156 void JSRepeatVirtualScroll2::SetInvalid(const JSCallbackInfo& info)
157 {
158     ACE_SCOPED_TRACE("RepeatVirtualScroll:SetInvalid");
159     if (!info[0]->IsNumber() || !info[1]->IsNumber()) {
160         TAG_LOGE(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll2::SetInvalid - invalid parameter ERROR");
161         return;
162     }
163     TAG_LOGD(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll2::SetInvalid");
164     auto repeatElmtId = info[0]->ToNumber<int32_t>();
165     auto rid = info[1]->ToNumber<uint32_t>();
166     RepeatVirtualScroll2Model::GetInstance()->SetInvalid(repeatElmtId, rid);
167 }
168 
169 // requestContainerReLayout(repeatElmtId: number, totalCount: number, index: number): void;
RequestContainerReLayout(const JSCallbackInfo & info)170 void JSRepeatVirtualScroll2::RequestContainerReLayout(const JSCallbackInfo& info)
171 {
172     ACE_SCOPED_TRACE("RepeatVirtualScroll:RequestContainerReLayout");
173     enum {
174         PARAM_ELMTID = 0,
175         PARAM_ARR_LEN = 1,
176         PARAM_TOTAL_COUNT = 2,
177         PARAM_CHILD_INDEX = 3, // optional
178     };
179 
180     if (!info[PARAM_ELMTID]->IsNumber() || !info[PARAM_ARR_LEN]->IsNumber() || !info[PARAM_TOTAL_COUNT]->IsNumber()) {
181         TAG_LOGE(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll2::RequestContainerReLayout - invalid parameters ERROR");
182         return;
183     }
184 
185     TAG_LOGD(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll2::RequestContainerReLayout");
186     auto repeatElmtId = info[PARAM_ELMTID]->ToNumber<int32_t>();
187     auto arrLen = info[PARAM_ARR_LEN]->ToNumber<uint32_t>();
188     auto totalCount = info[PARAM_TOTAL_COUNT]->ToNumber<uint32_t>();
189 
190     if (!info[PARAM_CHILD_INDEX]->IsNumber()) {
191         RepeatVirtualScroll2Model::GetInstance()->RequestContainerReLayout(repeatElmtId, arrLen, totalCount);
192     } else {
193         auto invalidateContainerLayoutFromChildIndex = info[PARAM_CHILD_INDEX]->ToNumber<int32_t>();
194         RepeatVirtualScroll2Model::GetInstance()->RequestContainerReLayout(
195             repeatElmtId, arrLen, totalCount, invalidateContainerLayoutFromChildIndex);
196     }
197 }
198 
NotifyContainerLayoutChange(const JSCallbackInfo & info)199 void JSRepeatVirtualScroll2::NotifyContainerLayoutChange(const JSCallbackInfo& info)
200 {
201     ACE_SCOPED_TRACE("RepeatVirtualScroll:NotifyContainerLayoutChange");
202     enum {
203         PARAM_ELMTID = 0,
204         PARAM_ARR_LEN = 1,
205         PARAM_TOTAL_COUNT = 2,
206         PARAM_INDEX = 3,
207         PARAM_COUNT = 4,
208         PARAM_TYPE = 5,
209     };
210 
211     if (!info[PARAM_ELMTID]->IsNumber() || !info[PARAM_ARR_LEN]->IsNumber() || !info[PARAM_TOTAL_COUNT]->IsNumber() ||
212         !info[PARAM_INDEX]->IsNumber() || !info[PARAM_COUNT]->IsNumber() || !info[PARAM_TYPE]->IsNumber()) {
213         TAG_LOGW(
214             AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll2::NotifyContainerLayoutChange - invalid parameters ERROR");
215         return;
216     }
217 
218     auto repeatElmtId = info[PARAM_ELMTID]->ToNumber<int32_t>();
219     auto arrLen = info[PARAM_ARR_LEN]->ToNumber<uint32_t>();
220     auto totalCount = info[PARAM_TOTAL_COUNT]->ToNumber<int32_t>();
221     auto index = info[PARAM_INDEX]->ToNumber<int32_t>();
222     auto count = info[PARAM_COUNT]->ToNumber<int32_t>();
223     auto notificationType = static_cast<NG::UINode::NotificationType>(info[PARAM_TYPE]->ToNumber<int32_t>());
224 
225     RepeatVirtualScroll2Model::GetInstance()->NotifyContainerLayoutChange(
226         repeatElmtId, arrLen, totalCount, index, count, notificationType);
227 }
228 
229 // updateL1Rid4Index(repeatElmtId: number,
230 //   totalCount: number,
231 //   invalidateContainerLayoutFromChildIndex: number,
232 //   l1rid4index: Array<Array<number>>): void;
UpdateL1Rid4Index(const JSCallbackInfo & info)233 void JSRepeatVirtualScroll2::UpdateL1Rid4Index(const JSCallbackInfo& info)
234 {
235     ACE_SCOPED_TRACE("RepeatVirtualScroll:UpdateL1Rid4Index");
236     enum {
237         PARAM_ELMTID = 0,
238         PARAM_ARR_LEN = 1,
239         PARAM_TOTAL_COUNT = 2,
240         PARAM_CHILD_INDEX = 3,
241         PARAM_ARRAY_PAIRS = 4,
242         PARAM_RECYCLE_RID = 5,
243     };
244 
245     if (!info[PARAM_ELMTID]->IsNumber() || !info[PARAM_ARR_LEN]->IsNumber() || !info[PARAM_TOTAL_COUNT]->IsNumber() ||
246         !info[PARAM_CHILD_INDEX]->IsNumber() || !info[PARAM_ARRAY_PAIRS]->IsArray() ||
247         !info[PARAM_RECYCLE_RID]->IsArray()) {
248         TAG_LOGE(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll2::UpdateL1Rid4Index - invalid parameters ERROR");
249         return;
250     }
251 
252     TAG_LOGD(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll2::UpdateL1Rid4Index");
253     auto repeatElmtId = info[PARAM_ELMTID]->ToNumber<int32_t>();
254     auto arrLen = info[PARAM_ARR_LEN]->ToNumber<uint32_t>();
255     auto totalCount = info[PARAM_TOTAL_COUNT]->ToNumber<uint32_t>();
256     auto invalidateContainerLayoutFromChildIndex = info[PARAM_CHILD_INDEX]->ToNumber<uint32_t>();
257 
258     auto arrayOfPairs = JSRef<JSArray>::Cast(info[PARAM_ARRAY_PAIRS]);
259     std::map<int32_t, uint32_t> l1Rid4Index;
260     for (size_t i = 0; i < arrayOfPairs->Length(); i++) {
261         JSRef<JSArray> pair = arrayOfPairs->GetValueAt(i);
262         auto index = pair->GetValueAt(0)->ToNumber<int32_t>();
263         auto rid = pair->GetValueAt(1)->ToNumber<uint32_t>();
264         TAG_LOGD(AceLogTag::ACE_REPEAT, "   ... index: %{public}d rid: %{public}d", index, static_cast<uint32_t>(rid));
265         l1Rid4Index[index] = rid;
266     }
267 
268     auto arrayOfRidNeedToRecycle = JSRef<JSArray>::Cast(info[PARAM_RECYCLE_RID]);
269     std::unordered_set<uint32_t> ridNeedToRecycle;
270     for (size_t i = 0; i < arrayOfRidNeedToRecycle->Length(); i++) {
271         auto rid = arrayOfRidNeedToRecycle->GetValueAt(i);
272         if (rid->IsNumber()) {
273             ridNeedToRecycle.emplace(rid->ToNumber<uint32_t>());
274         }
275     }
276 
277     RepeatVirtualScroll2Model::GetInstance()->UpdateL1Rid4Index(
278         repeatElmtId, arrLen, totalCount, invalidateContainerLayoutFromChildIndex, l1Rid4Index, ridNeedToRecycle);
279 }
280 
OnMove(const JSCallbackInfo & info)281 void JSRepeatVirtualScroll2::OnMove(const JSCallbackInfo& info)
282 {
283     enum OnMoveParam {
284         ELMTID = 0,
285         ON_MOVE = 1,
286         ITEM_DRAG_HANDLER = 2,
287     };
288     if (!info[OnMoveParam::ELMTID]->IsNumber()) {
289         TAG_LOGE(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll2::OnMove - invalid parameters ERROR");
290         return;
291     }
292     auto repeatElmtId = info[OnMoveParam::ELMTID]->ToNumber<int32_t>();
293     if (!info[OnMoveParam::ON_MOVE]->IsFunction()) {
294         RepeatVirtualScroll2Model::GetInstance()->OnMove(repeatElmtId, nullptr);
295         RepeatVirtualScroll2Model::GetInstance()->SetItemDragHandler(repeatElmtId, nullptr, nullptr, nullptr, nullptr);
296         return;
297     }
298     auto context = info.GetExecutionContext();
299     auto onMove = [execCtx = context, func = JSRef<JSFunc>::Cast(info[OnMoveParam::ON_MOVE])](
300                       int32_t from, int32_t to) {
301         auto params = ConvertToJSValues(from, to);
302         func->Call(JSRef<JSObject>(), params.size(), params.data());
303     };
304     RepeatVirtualScroll2Model::GetInstance()->OnMove(repeatElmtId, std::move(onMove));
305     if ((info.Length() > 2) && info[ITEM_DRAG_HANDLER]->IsObject()) { // 2: Array length
306         JsParseItemDragEventHandler(context, info[ITEM_DRAG_HANDLER], repeatElmtId);
307     } else {
308         RepeatVirtualScroll2Model::GetInstance()->SetItemDragHandler(repeatElmtId, nullptr, nullptr, nullptr, nullptr);
309     }
310 }
311 
JsParseItemDragEventHandler(const JsiExecutionContext & context,const JSRef<JSObject> & itemDragEventObj,int32_t repeatElmtId)312 void JSRepeatVirtualScroll2::JsParseItemDragEventHandler(
313     const JsiExecutionContext& context, const JSRef<JSObject>& itemDragEventObj, int32_t repeatElmtId)
314 {
315     auto onLongPress = itemDragEventObj->GetProperty("onLongPress");
316     std::function<void(int32_t)> onLongPressCallback;
317     if (onLongPress->IsFunction()) {
318         onLongPressCallback = [execCtx = context, func = JSRef<JSFunc>::Cast(onLongPress)](int32_t index) {
319             auto params = ConvertToJSValues(index);
320             func->Call(JSRef<JSObject>(), params.size(), params.data());
321         };
322     }
323 
324     auto onDragStart = itemDragEventObj->GetProperty("onDragStart");
325     std::function<void(int32_t)> onDragStartCallback;
326     if (onDragStart->IsFunction()) {
327         onDragStartCallback = [execCtx = context, func = JSRef<JSFunc>::Cast(onDragStart)](int32_t index) {
328             auto params = ConvertToJSValues(index);
329             func->Call(JSRef<JSObject>(), params.size(), params.data());
330         };
331     }
332     auto onMoveThrough = itemDragEventObj->GetProperty("onMoveThrough");
333     std::function<void(int32_t, int32_t)> onMoveThroughCallback;
334     if (onMoveThrough->IsFunction()) {
335         onMoveThroughCallback = [execCtx = context, func = JSRef<JSFunc>::Cast(onMoveThrough)](
336                                     int32_t from, int32_t to) {
337             auto params = ConvertToJSValues(from, to);
338             func->Call(JSRef<JSObject>(), params.size(), params.data());
339         };
340     }
341     auto onDrop = itemDragEventObj->GetProperty("onDrop");
342     std::function<void(int32_t)> onDropCallback;
343     if (onDrop->IsFunction()) {
344         onDropCallback = [execCtx = context, func = JSRef<JSFunc>::Cast(onDrop)](int32_t index) {
345             auto params = ConvertToJSValues(index);
346             func->Call(JSRef<JSObject>(), params.size(), params.data());
347         };
348     }
349     RepeatVirtualScroll2Model::GetInstance()->SetItemDragHandler(repeatElmtId, std::move(onLongPressCallback),
350         std::move(onDragStartCallback), std::move(onMoveThroughCallback), std::move(onDropCallback));
351 }
352 
SetCreateByTemplate(const JSCallbackInfo & info)353 void JSRepeatVirtualScroll2::SetCreateByTemplate(const JSCallbackInfo& info)
354 {
355     if (!info[0]->IsBoolean()) {
356         TAG_LOGE(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll2::SetCreateByTemplate wrong parameter, internal error.");
357         return;
358     }
359     RepeatVirtualScroll2Model::GetInstance()->SetCreateByTemplate(info[0]->ToBoolean());
360 }
361 
JSBind(BindingTarget globalObj)362 void JSRepeatVirtualScroll2::JSBind(BindingTarget globalObj)
363 {
364     JSClass<JSRepeatVirtualScroll2>::Declare("RepeatVirtualScroll2Native");
365     JSClass<JSRepeatVirtualScroll2>::StaticMethod("create", &JSRepeatVirtualScroll2::Create);
366 
367     JSClass<JSRepeatVirtualScroll2>::StaticMethod("removeNode", &JSRepeatVirtualScroll2::RemoveNode);
368     JSClass<JSRepeatVirtualScroll2>::StaticMethod("setInvalid", &JSRepeatVirtualScroll2::SetInvalid);
369     JSClass<JSRepeatVirtualScroll2>::StaticMethod(
370         "requestContainerReLayout", &JSRepeatVirtualScroll2::RequestContainerReLayout);
371     JSClass<JSRepeatVirtualScroll2>::StaticMethod(
372         "notifyContainerLayoutChange", &JSRepeatVirtualScroll2::NotifyContainerLayoutChange);
373 
374     JSClass<JSRepeatVirtualScroll2>::StaticMethod("updateL1Rid4Index", &JSRepeatVirtualScroll2::UpdateL1Rid4Index);
375 
376     JSClass<JSRepeatVirtualScroll2>::StaticMethod("onMove", &JSRepeatVirtualScroll2::OnMove);
377     JSClass<JSRepeatVirtualScroll2>::StaticMethod("setCreateByTemplate", &JSRepeatVirtualScroll2::SetCreateByTemplate);
378     JSClass<JSRepeatVirtualScroll2>::Bind<>(globalObj);
379 }
380 
381 } // namespace OHOS::Ace::Framework
382