1 /*
2 * Copyright (c) 2021-2022 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_item.h"
17
18 #include <cstdint>
19 #include <functional>
20
21 #include "base/log/ace_scoring_log.h"
22 #include "bridge/declarative_frontend/engine/functions/js_drag_function.h"
23 #include "bridge/declarative_frontend/engine/functions/js_function.h"
24 #include "bridge/declarative_frontend/jsview/js_utils.h"
25 #include "core/components_ng/base/view_abstract_model.h"
26 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
27 #include "bridge/declarative_frontend/jsview/models/list_item_model_impl.h"
28 #include "core/common/container.h"
29 #include "core/components_ng/event/gesture_event_hub.h"
30 #include "core/components_ng/pattern/list/list_item_model.h"
31 #include "core/components_ng/pattern/list/list_item_model_ng.h"
32
33 namespace OHOS::Ace {
34
35 std::unique_ptr<ListItemModel> ListItemModel::instance_ = nullptr;
36 std::mutex ListItemModel::mutex_;
37
GetInstance()38 ListItemModel* ListItemModel::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::ListItemModelNG());
45 #else
46 if (Container::IsCurrentUseNewPipeline()) {
47 instance_.reset(new NG::ListItemModelNG());
48 } else {
49 instance_.reset(new Framework::ListItemModelImpl());
50 }
51 #endif
52 }
53 }
54 return instance_.get();
55 }
56
57 } // namespace OHOS::Ace
58
59 namespace OHOS::Ace::Framework {
60
Create(const JSCallbackInfo & args)61 void JSListItem::Create(const JSCallbackInfo& args)
62 {
63 if (Container::IsCurrentUsePartialUpdate()) {
64 CreateForPartialUpdate(args);
65 return;
66 }
67 std::string type;
68 if (args.Length() >= 1 && args[0]->IsString()) {
69 type = args[0]->ToString();
70 }
71
72 ListItemModel::GetInstance()->Create();
73 if (!type.empty()) {
74 ListItemModel::GetInstance()->SetType(type);
75 }
76 args.ReturnSelf();
77 }
78
CreateForPartialUpdate(const JSCallbackInfo & args)79 void JSListItem::CreateForPartialUpdate(const JSCallbackInfo& args)
80 {
81 if (args.Length() < 2 || !args[0]->IsFunction()) {
82 LOGE("Expected deep render function parameter");
83 ListItemModel::GetInstance()->Create();
84 return;
85 }
86 RefPtr<JsFunction> jsDeepRender = AceType::MakeRefPtr<JsFunction>(args.This(), JSRef<JSFunc>::Cast(args[0]));
87
88 if (!args[1]->IsBoolean()) {
89 LOGE("Expected isLazy parameter");
90 return;
91 }
92 const bool isLazy = args[1]->ToBoolean();
93
94 V2::ListItemStyle listItemStyle = V2::ListItemStyle::NONE;
95 if (args[2]->IsObject()) {
96 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[2]);
97 JSRef<JSVal> styleObj = obj->GetProperty("style");
98 listItemStyle = styleObj->IsNumber() ? static_cast<V2::ListItemStyle>(styleObj->ToNumber<int32_t>())
99 : V2::ListItemStyle::NONE;
100 }
101
102 if (!isLazy) {
103 ListItemModel::GetInstance()->Create();
104 } else {
105 RefPtr<JsFunction> jsDeepRender = AceType::MakeRefPtr<JsFunction>(args.This(), JSRef<JSFunc>::Cast(args[0]));
106 auto listItemDeepRenderFunc = [execCtx = args.GetExecutionContext(),
107 jsDeepRenderFunc = std::move(jsDeepRender)](int32_t nodeId) {
108 ACE_SCOPED_TRACE("JSListItem::ExecuteDeepRender");
109 LOGD("ListItem elmtId %{public}d DeepRender JS function execution start ....", nodeId);
110 JAVASCRIPT_EXECUTION_SCOPE(execCtx);
111 JSRef<JSVal> jsParams[2];
112 jsParams[0] = JSRef<JSVal>::Make(ToJSValue(nodeId));
113 jsParams[1] = JSRef<JSVal>::Make(ToJSValue(true));
114 jsDeepRenderFunc->ExecuteJS(2, jsParams);
115 }; // listItemDeepRenderFunc lambda
116 ListItemModel::GetInstance()->Create(std::move(listItemDeepRenderFunc), listItemStyle);
117 ListItemModel::GetInstance()->SetIsLazyCreating(isLazy);
118 }
119 args.ReturnSelf();
120 }
121
SetSticky(int32_t sticky)122 void JSListItem::SetSticky(int32_t sticky)
123 {
124 ListItemModel::GetInstance()->SetSticky(static_cast<V2::StickyMode>(sticky));
125 }
126
SetEditable(const JSCallbackInfo & args)127 void JSListItem::SetEditable(const JSCallbackInfo& args)
128 {
129 if (args[0]->IsBoolean()) {
130 uint32_t value = args[0]->ToBoolean() ? V2::EditMode::DELETABLE | V2::EditMode::MOVABLE : V2::EditMode::SHAM;
131 ListItemModel::GetInstance()->SetEditMode(value);
132 return;
133 }
134
135 if (args[0]->IsNumber()) {
136 auto value = args[0]->ToNumber<uint32_t>();
137 ListItemModel::GetInstance()->SetEditMode(value);
138 return;
139 }
140 }
141
SetSelectable(bool selectable)142 void JSListItem::SetSelectable(bool selectable)
143 {
144 ListItemModel::GetInstance()->SetSelectable(selectable);
145 }
146
SetSelected(const JSCallbackInfo & info)147 void JSListItem::SetSelected(const JSCallbackInfo& info)
148 {
149 if (info.Length() < 1) {
150 LOGW("The arg is wrong, it is supposed to have 1 or 2 arguments");
151 return;
152 }
153 bool select = false;
154 if (info[0]->IsBoolean()) {
155 select = info[0]->ToBoolean();
156 }
157 ListItemModel::GetInstance()->SetSelected(select);
158
159 if (info.Length() > 1 && info[1]->IsFunction()) {
160 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[1]));
161 auto changeEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](bool param) {
162 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
163 ACE_SCORING_EVENT("ListItem.ChangeEvent");
164 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(param));
165 func->ExecuteJS(1, &newJSVal);
166 };
167 ListItemModel::GetInstance()->SetSelectChangeEvent(std::move(changeEvent));
168 }
169 }
170
JsParseDeleteArea(const JSCallbackInfo & args,const JSRef<JSVal> & jsValue,bool isStartArea)171 void JSListItem::JsParseDeleteArea(const JSCallbackInfo& args, const JSRef<JSVal>& jsValue, bool isStartArea)
172 {
173 auto deleteAreaObj = JSRef<JSObject>::Cast(jsValue);
174 auto listItemTheme = GetTheme<ListItemTheme>();
175
176 std::function<void()> builderAction;
177 auto builderObject = deleteAreaObj->GetProperty("builder");
178 if (builderObject->IsFunction()) {
179 auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(builderObject));
180 builderAction = [builderFunc]() { builderFunc->Execute(); };
181 }
182 auto defaultDeleteAnimation = deleteAreaObj->GetProperty("useDefaultDeleteAnimation");
183 bool useDefaultDeleteAnimation = true;
184 if (defaultDeleteAnimation->IsBoolean()) {
185 useDefaultDeleteAnimation = defaultDeleteAnimation->ToBoolean();
186 }
187 auto onAction = deleteAreaObj->GetProperty("onAction");
188 if (!onAction->IsFunction()) {
189 onAction = deleteAreaObj->GetProperty("onDelete");
190 }
191 std::function<void()> onActionCallback;
192 if (onAction->IsFunction()) {
193 onActionCallback = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(onAction)]() {
194 func->Call(JSRef<JSObject>());
195 return;
196 };
197 }
198 auto onEnterActionArea = deleteAreaObj->GetProperty("onEnterActionArea");
199 if (!onEnterActionArea->IsFunction()) {
200 onEnterActionArea = deleteAreaObj->GetProperty("onEnterDeleteArea");
201 }
202 std::function<void()> onEnterActionAreaCallback;
203 if (onEnterActionArea->IsFunction()) {
204 onEnterActionAreaCallback = [execCtx = args.GetExecutionContext(),
205 func = JSRef<JSFunc>::Cast(onEnterActionArea)]() {
206 func->Call(JSRef<JSObject>());
207 return;
208 };
209 }
210 auto onExitActionArea = deleteAreaObj->GetProperty("onExitActionArea");
211 if (!onExitActionArea->IsFunction()) {
212 onExitActionArea = deleteAreaObj->GetProperty("onExitDeleteArea");
213 }
214 std::function<void()> onExitActionAreaCallback;
215 if (onExitActionArea->IsFunction()) {
216 onExitActionAreaCallback = [execCtx = args.GetExecutionContext(),
217 func = JSRef<JSFunc>::Cast(onExitActionArea)]() {
218 func->Call(JSRef<JSObject>());
219 return;
220 };
221 }
222 auto actionAreaDistance = deleteAreaObj->GetProperty("actionAreaDistance");
223 CalcDimension length;
224 if (!ParseJsDimensionVp(actionAreaDistance, length)) {
225 actionAreaDistance = deleteAreaObj->GetProperty("deleteAreaDistance");
226 if (!ParseJsDimensionVp(actionAreaDistance, length)) {
227 length = listItemTheme->GetDeleteDistance();
228 }
229 }
230
231 ListItemModel::GetInstance()->SetDeleteArea(std::move(builderAction), useDefaultDeleteAnimation,
232 std::move(onActionCallback), std::move(onEnterActionAreaCallback), std::move(onExitActionAreaCallback), length,
233 isStartArea);
234 }
235
SetSwiperAction(const JSCallbackInfo & args)236 void JSListItem::SetSwiperAction(const JSCallbackInfo& args)
237 {
238 if (!args[0]->IsObject()) {
239 return;
240 }
241
242 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
243 std::function<void()> startAction;
244 auto startObject = obj->GetProperty("start");
245 if (startObject->IsFunction()) {
246 auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(startObject));
247 startAction = [builderFunc]() { builderFunc->Execute(); };
248 } else if (startObject->IsObject()) {
249 JsParseDeleteArea(args, startObject, true);
250 }
251
252 std::function<void()> endAction;
253 auto endObject = obj->GetProperty("end");
254 if (endObject->IsFunction()) {
255 auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(endObject));
256 endAction = [builderFunc]() { builderFunc->Execute(); };
257 } else if (endObject->IsObject()) {
258 JsParseDeleteArea(args, endObject, false);
259 }
260
261 auto edgeEffect = obj->GetProperty("edgeEffect");
262 V2::SwipeEdgeEffect swipeEdgeEffect = V2::SwipeEdgeEffect::Spring;
263 if (edgeEffect->IsNumber()) {
264 swipeEdgeEffect = static_cast<V2::SwipeEdgeEffect>(edgeEffect->ToNumber<int32_t>());
265 }
266 ListItemModel::GetInstance()->SetSwiperAction(std::move(startAction), std::move(endAction), swipeEdgeEffect);
267 }
268
SelectCallback(const JSCallbackInfo & args)269 void JSListItem::SelectCallback(const JSCallbackInfo& args)
270 {
271 if (!args[0]->IsFunction()) {
272 return;
273 }
274
275 RefPtr<JsMouseFunction> jsOnSelectFunc = AceType::MakeRefPtr<JsMouseFunction>(JSRef<JSFunc>::Cast(args[0]));
276 auto onSelect = [execCtx = args.GetExecutionContext(), func = std::move(jsOnSelectFunc)](bool isSelected) {
277 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
278 func->SelectExecute(isSelected);
279 };
280 ListItemModel::GetInstance()->SetSelectCallback(std::move(onSelect));
281 }
282
JsBorderRadius(const JSCallbackInfo & info)283 void JSListItem::JsBorderRadius(const JSCallbackInfo& info)
284 {
285 JSViewAbstract::JsBorderRadius(info);
286 CalcDimension borderRadius;
287 if (!JSViewAbstract::ParseJsDimensionVp(info[0], borderRadius)) {
288 return;
289 }
290 ListItemModel::GetInstance()->SetBorderRadius(borderRadius);
291 }
292
JsOnDragStart(const JSCallbackInfo & info)293 void JSListItem::JsOnDragStart(const JSCallbackInfo& info)
294 {
295 if (!info[0]->IsFunction()) {
296 return;
297 }
298 RefPtr<JsDragFunction> jsOnDragStartFunc = AceType::MakeRefPtr<JsDragFunction>(JSRef<JSFunc>::Cast(info[0]));
299 auto onDragStart = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragStartFunc)](
300 const RefPtr<DragEvent>& info, const std::string& extraParams) -> NG::DragDropBaseInfo {
301 NG::DragDropBaseInfo itemInfo;
302 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, itemInfo);
303
304 auto ret = func->Execute(info, extraParams);
305 if (!ret->IsObject()) {
306 return itemInfo;
307 }
308 auto node = ParseDragNode(ret);
309 if (node) {
310 itemInfo.node = node;
311 return itemInfo;
312 }
313
314 auto builderObj = JSRef<JSObject>::Cast(ret);
315 #if defined(PIXEL_MAP_SUPPORTED)
316 auto pixmap = builderObj->GetProperty("pixelMap");
317 itemInfo.pixelMap = CreatePixelMapFromNapiValue(pixmap);
318 #endif
319 auto extraInfo = builderObj->GetProperty("extraInfo");
320 ParseJsString(extraInfo, itemInfo.extraInfo);
321 node = ParseDragNode(builderObj->GetProperty("builder"));
322 itemInfo.node = node;
323 return itemInfo;
324 };
325 #ifdef NG_BUILD
326 ViewAbstractModel::GetInstance()->SetOnDragStart(std::move(onDragStart));
327 #else
328 if (Container::IsCurrentUseNewPipeline()) {
329 ViewAbstractModel::GetInstance()->SetOnDragStart(std::move(onDragStart));
330 } else {
331 ListItemModel::GetInstance()->SetOnDragStart(std::move(onDragStart));
332 }
333 #endif
334 }
335
JSBind(BindingTarget globalObj)336 void JSListItem::JSBind(BindingTarget globalObj)
337 {
338 JSClass<JSListItem>::Declare("ListItem");
339 JSClass<JSListItem>::StaticMethod("create", &JSListItem::Create);
340
341 JSClass<JSListItem>::StaticMethod("sticky", &JSListItem::SetSticky);
342 JSClass<JSListItem>::StaticMethod("editable", &JSListItem::SetEditable);
343 JSClass<JSListItem>::StaticMethod("selectable", &JSListItem::SetSelectable);
344 JSClass<JSListItem>::StaticMethod("onSelect", &JSListItem::SelectCallback);
345 JSClass<JSListItem>::StaticMethod("borderRadius", &JSListItem::JsBorderRadius);
346 JSClass<JSListItem>::StaticMethod("swipeAction", &JSListItem::SetSwiperAction);
347 JSClass<JSListItem>::StaticMethod("selected", &JSListItem::SetSelected);
348
349 JSClass<JSListItem>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
350 JSClass<JSListItem>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
351 JSClass<JSListItem>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
352 JSClass<JSListItem>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
353 JSClass<JSListItem>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
354 JSClass<JSListItem>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
355 JSClass<JSListItem>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
356 JSClass<JSListItem>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
357 JSClass<JSListItem>::StaticMethod("onDragStart", &JSListItem::JsOnDragStart);
358
359 JSClass<JSListItem>::InheritAndBind<JSContainerBase>(globalObj);
360 }
361
362 } // namespace OHOS::Ace::Framework
363