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