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