1 /*
2 * Copyright (c) 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_group.h"
17
18 #include "bridge/declarative_frontend/jsview/js_list_item.h"
19 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
20 #include "bridge/declarative_frontend/jsview/models/list_item_group_model_impl.h"
21 #include "bridge/declarative_frontend/view_stack_processor.h"
22 #include "core/components_v2/list/list_item_group_component.h"
23 #include "core/components_ng/pattern/list/list_item_group_model.h"
24 #include "core/components_ng/pattern/list/list_item_group_model_ng.h"
25
26 namespace OHOS::Ace {
27
28 std::unique_ptr<ListItemGroupModel> ListItemGroupModel::instance_ = nullptr;
29 std::mutex ListItemGroupModel::mutex_;
30
GetInstance()31 ListItemGroupModel* ListItemGroupModel::GetInstance()
32 {
33 if (!instance_) {
34 std::lock_guard<std::mutex> lock(mutex_);
35 if (!instance_) {
36 #ifdef NG_BUILD
37 instance_.reset(new NG::ListItemGroupModelNG());
38 #else
39 if (Container::IsCurrentUseNewPipeline()) {
40 instance_.reset(new NG::ListItemGroupModelNG());
41 } else {
42 instance_.reset(new Framework::ListItemGroupModelImpl());
43 }
44 #endif
45 }
46 }
47 return instance_.get();
48 }
49
50 } // namespace OHOS::Ace
51 namespace OHOS::Ace::Framework {
52
53 namespace {
ParseChange(const JSRef<JSObject> & changeObject,const float defaultSize,int32_t & start,int32_t & deleteCount,std::vector<float> & newChildrenSize)54 bool ParseChange(const JSRef<JSObject>& changeObject, const float defaultSize, int32_t& start,
55 int32_t& deleteCount, std::vector<float>& newChildrenSize)
56 {
57 if (!JSViewAbstract::ParseJsInteger<int32_t>(changeObject->GetProperty("start"), start) || start < 0) {
58 return false;
59 }
60 if (!(changeObject->HasProperty("deleteCount"))) {
61 // If only input one parameter, set -1 to deleteCount for deleting elements after index 'start' in the array.
62 deleteCount = -1;
63 } else if (!JSViewAbstract::ParseJsInteger<int32_t>(changeObject->GetProperty("deleteCount"), deleteCount) ||
64 deleteCount < 0) {
65 deleteCount = 0;
66 }
67 auto childrenSizeValue = changeObject->GetProperty("childrenSize");
68 if (childrenSizeValue->IsArray()) {
69 auto childrenSize = JSRef<JSArray>::Cast(childrenSizeValue);
70 auto childrenSizeCount = childrenSize->Length();
71 for (size_t j = 0; j < childrenSizeCount; ++j) {
72 // -1.0: represent default size.
73 double childSize = -1.0;
74 if (!JSViewAbstract::ParseJsDouble(childrenSize->GetValueAt(j), childSize) || Negative(childSize)) {
75 // -1.0f: represent default size.
76 newChildrenSize.emplace_back(-1.0f);
77 } else {
78 newChildrenSize.emplace_back(Dimension(childSize, DimensionUnit::VP).ConvertToPx());
79 }
80 }
81 }
82 return true;
83 }
84
SyncChildrenSize(const JSRef<JSObject> & childrenSizeObj,RefPtr<NG::ListChildrenMainSize> childrenSize)85 void SyncChildrenSize(const JSRef<JSObject>& childrenSizeObj, RefPtr<NG::ListChildrenMainSize> childrenSize)
86 {
87 auto sizeArray = childrenSizeObj->GetProperty("sizeArray");
88 if (!sizeArray->IsArray()) {
89 return;
90 }
91 childrenSize->ResizeChildrenSize(0);
92 auto childrenSizeJSArray = JSRef<JSArray>::Cast(sizeArray);
93 auto length = childrenSizeJSArray->Length();
94 for (size_t i = 0; i < length; ++i) {
95 // -1.0: represent default size.
96 double childSize = -1.0;
97 if (!JSViewAbstract::ParseJsDouble(childrenSizeJSArray->GetValueAt(i), childSize) || Negative(childSize)) {
98 // -1.0f: represent default size.
99 childrenSize->SyncChildrenSize(-1.0f);
100 } else {
101 childrenSize->SyncChildrenSize(Dimension(childSize, DimensionUnit::VP).ConvertToPx());
102 }
103 }
104 childrenSize->SyncChildrenSizeOver();
105 }
106 } // namespace
107
SetChildrenMainSize(const JSCallbackInfo & args)108 void JSListItemGroup::SetChildrenMainSize(const JSCallbackInfo& args)
109 {
110 if (args.Length() != 1 || !(args[0]->IsObject())) {
111 return;
112 }
113 SetChildrenMainSize(JSRef<JSObject>::Cast(args[0]));
114 }
115
SetChildrenMainSize(const JSRef<JSObject> & childrenSizeObj)116 void JSListItemGroup::SetChildrenMainSize(const JSRef<JSObject>& childrenSizeObj)
117 {
118 double defaultSize = 0.0f;
119 if (!ParseJsDouble(childrenSizeObj->GetProperty("defaultMainSize"), defaultSize) || !NonNegative(defaultSize)) {
120 LOGW("JSListItemGroup input parameter defaultSize check failed.");
121 return;
122 }
123 auto listChildrenMainSize = ListItemGroupModel::GetInstance()->GetOrCreateListChildrenMainSize();
124 CHECK_NULL_VOID(listChildrenMainSize);
125 listChildrenMainSize->UpdateDefaultSize(Dimension(defaultSize, DimensionUnit::VP).ConvertToPx());
126
127 if (listChildrenMainSize->NeedSync()) {
128 SyncChildrenSize(childrenSizeObj, listChildrenMainSize);
129 } else {
130 auto changes = childrenSizeObj->GetProperty("changeArray");
131 if (!changes->IsArray()) {
132 return;
133 }
134 auto changeArray = JSRef<JSArray>::Cast(changes);
135 auto length = changeArray->Length();
136 for (size_t i = 0; i < length; ++i) {
137 auto change = changeArray->GetValueAt(i);
138 if (!change->IsObject()) {
139 continue;
140 }
141 auto changeObject = JSRef<JSObject>::Cast(change);
142 int32_t start = 0;
143 int32_t deleteCount = 0;
144 std::vector<float> newChildrenSize;
145 if (!ParseChange(changeObject, defaultSize, start, deleteCount, newChildrenSize)) {
146 SyncChildrenSize(childrenSizeObj, listChildrenMainSize);
147 break;
148 }
149 listChildrenMainSize->ChangeData(start, deleteCount, newChildrenSize);
150 }
151 }
152 auto clearFunc = childrenSizeObj->GetProperty("clearChanges");
153 if (!clearFunc->IsFunction()) {
154 return;
155 }
156 auto func = JSRef<JSFunc>::Cast(clearFunc);
157 JSRef<JSVal>::Cast(func->Call(childrenSizeObj));
158 }
159
Create(const JSCallbackInfo & args)160 void JSListItemGroup::Create(const JSCallbackInfo& args)
161 {
162 auto listItemGroupStyle = GetListItemGroupStyle(args);
163 ListItemGroupModel::GetInstance()->Create(listItemGroupStyle);
164 if (args.Length() < 1 || !args[0]->IsObject()) {
165 NG::ListItemGroupModelNG::GetInstance()->RemoveHeader();
166 NG::ListItemGroupModelNG::GetInstance()->RemoveFooter();
167 args.ReturnSelf();
168 return;
169 }
170 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
171
172 Dimension space;
173 if (ConvertFromJSValue(obj->GetProperty("space"), space) && space.IsNonNegative()) {
174 ListItemGroupModel::GetInstance()->SetSpace(space);
175 }
176
177 if (obj->HasProperty("headerComponent")) {
178 auto headerComponentObject = obj->GetProperty("headerComponent");
179 if (!ParseHeaderAndFooterContent(headerComponentObject, true)) {
180 NG::ListItemGroupModelNG::GetInstance()->RemoveHeader();
181 }
182 } else {
183 if (!SetHeaderBuilder(obj)) {
184 NG::ListItemGroupModelNG::GetInstance()->RemoveHeader();
185 }
186 }
187
188 if (obj->HasProperty("footerComponent")) {
189 auto footerComponentObject = obj->GetProperty("footerComponent");
190 if (!ParseHeaderAndFooterContent(footerComponentObject, false)) {
191 NG::ListItemGroupModelNG::GetInstance()->RemoveFooter();
192 }
193 } else {
194 if (!SetFooterBuilder(obj)) {
195 NG::ListItemGroupModelNG::GetInstance()->RemoveFooter();
196 }
197 }
198
199 args.ReturnSelf();
200 }
201
SetDivider(const JSCallbackInfo & args)202 void JSListItemGroup::SetDivider(const JSCallbackInfo& args)
203 {
204 V2::ItemDivider divider;
205 if (args.Length() >= 1 && args[0]->IsObject()) {
206 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
207 if (!ConvertFromJSValue(obj->GetProperty("strokeWidth"), divider.strokeWidth)) {
208 LOGW("Invalid strokeWidth of divider");
209 divider.strokeWidth.Reset();
210 }
211 if (!ConvertFromJSValue(obj->GetProperty("color"), divider.color)) {
212 // Failed to get color from param, using default color defined in theme
213 RefPtr<ListTheme> listTheme = GetTheme<ListTheme>();
214 if (listTheme) {
215 divider.color = listTheme->GetDividerColor();
216 }
217 }
218 ConvertFromJSValue(obj->GetProperty("startMargin"), divider.startMargin);
219 ConvertFromJSValue(obj->GetProperty("endMargin"), divider.endMargin);
220 }
221 ListItemGroupModel::GetInstance()->SetDivider(divider);
222 args.ReturnSelf();
223 }
224
SetAspectRatio(const JSCallbackInfo & args)225 void JSListItemGroup::SetAspectRatio(const JSCallbackInfo& args) {}
226
ParseHeaderAndFooterContent(const JSRef<JSVal> & contentParam,bool isHeader)227 bool JSListItemGroup::ParseHeaderAndFooterContent(const JSRef<JSVal>& contentParam, bool isHeader)
228 {
229 if (!contentParam->IsObject()) {
230 return false;
231 }
232 JSRef<JSObject> contentObject = JSRef<JSObject>::Cast(contentParam);
233 JSRef<JSVal> builderNodeParam = contentObject->GetProperty("builderNode_");
234 if (!builderNodeParam->IsObject()) {
235 return false;
236 }
237 JSRef<JSObject> builderNodeObject = JSRef<JSObject>::Cast(builderNodeParam);
238 JSRef<JSVal> nodeptr = builderNodeObject->GetProperty("nodePtr_");
239 if (nodeptr.IsEmpty()) {
240 return false;
241 }
242 const auto* vm = nodeptr->GetEcmaVM();
243 auto* node = nodeptr->GetLocalHandle()->ToNativePointer(vm)->Value();
244 auto* frameNode = reinterpret_cast<NG::FrameNode*>(node);
245 CHECK_NULL_RETURN(frameNode, false);
246 RefPtr<NG::FrameNode> refPtrFrameNode = AceType::Claim(frameNode);
247 if (isHeader) {
248 NG::ListItemGroupModelNG::GetInstance()->SetHeaderComponent(refPtrFrameNode);
249 } else {
250 NG::ListItemGroupModelNG::GetInstance()->SetFooterComponent(refPtrFrameNode);
251 }
252 return true;
253 }
254
SetHeaderBuilder(const JSRef<JSObject> & obj)255 bool JSListItemGroup::SetHeaderBuilder(const JSRef<JSObject>& obj)
256 {
257 auto headerObject = obj->GetProperty("header");
258 if (headerObject->IsFunction()) {
259 auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(headerObject));
260 auto headerAction = [builderFunc]() { builderFunc->Execute(); };
261 ListItemGroupModel::GetInstance()->SetHeader(headerAction);
262 return true;
263 }
264 return false;
265 }
266
SetFooterBuilder(const JSRef<JSObject> & obj)267 bool JSListItemGroup::SetFooterBuilder(const JSRef<JSObject>& obj)
268 {
269 auto footerObject = obj->GetProperty("footer");
270 if (footerObject->IsFunction()) {
271 auto builderFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSFunc>::Cast(footerObject));
272 auto footerAction = [builderFunc]() { builderFunc->Execute(); };
273 ListItemGroupModel::GetInstance()->SetFooter(footerAction);
274 return true;
275 }
276 return false;
277 }
278
GetListItemGroupStyle(const JSCallbackInfo & args)279 V2::ListItemGroupStyle JSListItemGroup::GetListItemGroupStyle(const JSCallbackInfo& args)
280 {
281 V2::ListItemGroupStyle listItemGroupStyle = V2::ListItemGroupStyle::NONE;
282 if (args.Length() >= 1 && args[0]->IsObject()) {
283 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
284 auto styleObject = obj->GetProperty("style");
285 listItemGroupStyle = styleObject->IsNumber()
286 ? static_cast<V2::ListItemGroupStyle>(styleObject->ToNumber<int32_t>())
287 : V2::ListItemGroupStyle::NONE;
288 }
289 return listItemGroupStyle;
290 }
291
JSBind(BindingTarget globalObj)292 void JSListItemGroup::JSBind(BindingTarget globalObj)
293 {
294 JSClass<JSListItemGroup>::Declare("ListItemGroup");
295 JSClass<JSListItemGroup>::StaticMethod("create", &JSListItemGroup::Create);
296
297 JSClass<JSListItemGroup>::StaticMethod("aspectRatio", &JSListItemGroup::SetAspectRatio);
298 JSClass<JSListItemGroup>::StaticMethod("childrenMainSize", &JSListItemGroup::SetChildrenMainSize);
299 JSClass<JSListItemGroup>::StaticMethod("divider", &JSListItemGroup::SetDivider);
300 JSClass<JSListItemGroup>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
301 JSClass<JSListItemGroup>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
302 JSClass<JSListItemGroup>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
303 JSClass<JSListItemGroup>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
304 JSClass<JSListItemGroup>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
305
306 JSClass<JSListItemGroup>::Inherit<JSInteractableView>();
307 JSClass<JSListItemGroup>::InheritAndBind<JSContainerBase>(globalObj);
308 }
309
310 } // namespace OHOS::Ace::Framework
311