1 /*
2 * Copyright (c) 2024 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.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_model_ng.h"
24
25 #define JSFUNC(opts, propName) (JSRef<JSFunc>::Cast((opts)->GetProperty(propName)))
26
27 namespace OHOS::Ace {
28
29 std::unique_ptr<RepeatVirtualScrollModel> RepeatVirtualScrollModel::instance_ = nullptr;
30 #define UNUSED_CACHED_SIZE_PARAM 2
31
GetInstance()32 RepeatVirtualScrollModel* RepeatVirtualScrollModel::GetInstance()
33 {
34 if (!instance_) {
35 instance_.reset(new NG::RepeatVirtualScrollModelNG());
36 }
37 return instance_.get();
38 }
39 } // namespace OHOS::Ace
40
41 namespace OHOS::Ace::Framework {
42
43 enum {
44 PARAM_TOTAL_COUNT = 0,
45 PARAM_TEMPLATE_OPTS = 1,
46 PARAM_HANDLERS = 2,
47 PARAM_REUSABLE = 3,
48 MIN_PARAM_SIZE = 4,
49 };
50
ParseAndVerifyParams(const JSCallbackInfo & info)51 static bool ParseAndVerifyParams(const JSCallbackInfo& info)
52 {
53 if (info.Length() < MIN_PARAM_SIZE) {
54 return false;
55 }
56
57 if (!info[PARAM_TOTAL_COUNT]->IsNumber()) {
58 return false;
59 }
60 if (!info[PARAM_TEMPLATE_OPTS]->IsArray()) {
61 return false;
62 }
63 if (!info[PARAM_HANDLERS]->IsObject()) {
64 return false;
65 }
66 if (!info[PARAM_REUSABLE]->IsBoolean()) {
67 return false;
68 }
69
70 auto templateOptsArray = JSRef<JSArray>::Cast(info[PARAM_TEMPLATE_OPTS]);
71 for (size_t i = 0; i < templateOptsArray->Length(); i++) {
72 JSRef<JSArray> pair = templateOptsArray->GetValueAt(i);
73 if (!pair->GetValueAt(0)->IsString()) {
74 return false;
75 }
76 if (!pair->GetValueAt(1)->IsObject()) {
77 return false;
78 }
79 auto type = pair->GetValueAt(0)->ToString();
80 auto opts = JSRef<JSObject>::Cast(pair->GetValueAt(1));
81 if (!opts->GetProperty("cachedCountSpecified")->IsBoolean()) {
82 return false;
83 }
84 }
85
86 auto handlers = JSRef<JSObject>::Cast(info[PARAM_HANDLERS]);
87 if (!handlers->GetProperty("onCreateNode")->IsFunction() || !handlers->GetProperty("onUpdateNode")->IsFunction() ||
88 !handlers->GetProperty("onGetKeys4Range")->IsFunction() ||
89 !handlers->GetProperty("onGetTypes4Range")->IsFunction() ||
90 !handlers->GetProperty("onSetActiveRange")->IsFunction()) {
91 return false;
92 }
93
94 return true;
95 }
96
Create(const JSCallbackInfo & info)97 void JSRepeatVirtualScroll::Create(const JSCallbackInfo& info)
98 {
99 if (!ParseAndVerifyParams(info)) {
100 TAG_LOGW(AceLogTag::ACE_LAZY_FOREACH, "Invalid arguments for RepeatVirtualScroll");
101 return;
102 }
103
104 // arg 0
105 auto totalCount = info[PARAM_TOTAL_COUNT]->ToNumber<uint32_t>();
106
107 // arg 1
108 auto templateOptsArray = JSRef<JSArray>::Cast(info[PARAM_TEMPLATE_OPTS]);
109 std::map<std::string, std::pair<bool, uint32_t>> templateCachedCountMap;
110 for (size_t i = 0; i < templateOptsArray->Length(); i++) {
111 JSRef<JSArray> pair = templateOptsArray->GetValueAt(i);
112 auto type = pair->GetValueAt(0)->ToString();
113 auto opts = JSRef<JSObject>::Cast(pair->GetValueAt(1));
114
115 templateCachedCountMap[type] =
116 opts->GetProperty("cachedCountSpecified")->ToNumber <bool>()
117 ? std::pair<bool, uint32_t>(true, opts->GetProperty("cachedCount")->ToNumber<uint32_t>())
118 : std::pair<bool, uint32_t>(false, UNUSED_CACHED_SIZE_PARAM);
119 }
120
121 // arg 2
122 auto handlers = JSRef<JSObject>::Cast(info[PARAM_HANDLERS]);
123 auto onCreateNode = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onCreateNode")](
124 uint32_t forIndex) -> void {
125 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
126 auto params = ConvertToJSValues(forIndex);
127 func->Call(JSRef<JSObject>(), params.size(), params.data());
128 };
129
130 auto onUpdateNode = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onUpdateNode")](
131 const std::string& fromKey, uint32_t forIndex) -> void {
132 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
133 auto params = ConvertToJSValues(fromKey, forIndex);
134 func->Call(JSRef<JSObject>(), params.size(), params.data());
135 };
136
137 auto onGetKeys4Range = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onGetKeys4Range")](
138 uint32_t from, uint32_t to) -> std::list<std::string> {
139 std::list<std::string> list;
140 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, list);
141 auto params = ConvertToJSValues(from, to);
142 JSRef<JSVal> jsVal = func->Call(JSRef<JSObject>(), params.size(), params.data());
143 // convert js-array to std::list
144 if (!jsVal->IsArray()) {
145 TAG_LOGW(AceLogTag::ACE_REPEAT, "jsVal should be array.");
146 return list;
147 }
148 JSRef<JSArray> jsArr = JSRef<JSArray>::Cast(jsVal);
149 for (size_t i = 0; i < jsArr->Length(); i++) {
150 list.emplace_back(jsArr->GetValueAt(i)->ToString());
151 }
152 return list;
153 };
154
155 auto onGetTypes4Range = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onGetTypes4Range")](
156 uint32_t from, uint32_t to) -> std::list<std::string> {
157 std::list<std::string> list;
158 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, list);
159 auto params = ConvertToJSValues(from, to);
160 JSRef<JSVal> jsVal = func->Call(JSRef<JSObject>(), params.size(), params.data());
161
162 // convert js-array to std::list
163 if (!jsVal->IsArray()) {
164 TAG_LOGW(AceLogTag::ACE_REPEAT, "jsVal should be array.");
165 return list;
166 }
167 JSRef<JSArray> jsArr = JSRef<JSArray>::Cast(jsVal);
168 for (size_t i = 0; i < jsArr->Length(); i++) {
169 list.emplace_back(jsArr->GetValueAt(i)->ToString());
170 }
171 return list;
172 };
173
174 auto onSetActiveRange = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onSetActiveRange")](
175 int32_t from, int32_t to) -> void {
176 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
177 auto params = ConvertToJSValues(from, to);
178 func->Call(JSRef<JSObject>(), params.size(), params.data());
179 };
180
181 // arg 3
182 auto reusable = info[PARAM_REUSABLE]->ToBoolean();
183
184 RepeatVirtualScrollModel::GetInstance()->Create(
185 totalCount, templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range,
186 onSetActiveRange, reusable);
187 }
188
UpdateRenderState(const JSCallbackInfo & info)189 void JSRepeatVirtualScroll::UpdateRenderState(const JSCallbackInfo& info)
190 {
191 ACE_SCOPED_TRACE("RepeatVirtualScroll:UpdateRenderState");
192 TAG_LOGD(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll::UpdateRenderState");
193 if (!info[0]->IsNumber() || !info[1]->IsBoolean()) {
194 return;
195 }
196 auto totalCount = info[0]->ToNumber<uint32_t>();
197 auto visibleItemsChanged = info[1]->ToBoolean();
198 RepeatVirtualScrollModel::GetInstance()->UpdateRenderState(totalCount, visibleItemsChanged);
199 }
200
OnMove(const JSCallbackInfo & info)201 void JSRepeatVirtualScroll::OnMove(const JSCallbackInfo& info)
202 {
203 if (!info[0]->IsFunction()) {
204 RepeatVirtualScrollModel::GetInstance()->OnMove(nullptr);
205 return;
206 }
207 auto onMove = [execCtx = info.GetExecutionContext(), func = JSRef<JSFunc>::Cast(info[0])](
208 int32_t from, int32_t to) {
209 auto params = ConvertToJSValues(from, to);
210 func->Call(JSRef<JSObject>(), params.size(), params.data());
211 };
212 RepeatVirtualScrollModel::GetInstance()->OnMove(std::move(onMove));
213 }
214
SetCreateByTemplate(const JSCallbackInfo & info)215 void JSRepeatVirtualScroll::SetCreateByTemplate(const JSCallbackInfo& info)
216 {
217 if (!info[0]->IsBoolean()) {
218 TAG_LOGE(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll::SetCreateByTemplate wrong parameter, internal error.");
219 return;
220 }
221 RepeatVirtualScrollModel::GetInstance()->SetCreateByTemplate(info[0]->ToBoolean());
222 }
223
JSBind(BindingTarget globalObj)224 void JSRepeatVirtualScroll::JSBind(BindingTarget globalObj)
225 {
226 JSClass<JSRepeatVirtualScroll>::Declare("RepeatVirtualScrollNative");
227 JSClass<JSRepeatVirtualScroll>::StaticMethod("create", &JSRepeatVirtualScroll::Create);
228 JSClass<JSRepeatVirtualScroll>::StaticMethod("updateRenderState", &JSRepeatVirtualScroll::UpdateRenderState);
229 JSClass<JSRepeatVirtualScroll>::StaticMethod("onMove", &JSRepeatVirtualScroll::OnMove);
230 JSClass<JSRepeatVirtualScroll>::StaticMethod("setCreateByTemplate", &JSRepeatVirtualScroll::SetCreateByTemplate);
231 JSClass<JSRepeatVirtualScroll>::Bind<>(globalObj);
232 }
233
234 } // namespace OHOS::Ace::Framework
235