1 /*
2 * Copyright (c) 2021-2023 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_indexer.h"
17
18 #include "base/geometry/dimension.h"
19 #include "base/log/ace_scoring_log.h"
20 #include "base/utils/utils.h"
21 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
22 #include "bridge/declarative_frontend/jsview/js_scroller.h"
23 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
24 #include "bridge/declarative_frontend/jsview/models/indexer_model_impl.h"
25 #include "core/components/common/properties/text_style.h"
26 #include "core/components_ng/pattern/indexer/indexer_model_ng.h"
27
28 namespace OHOS::Ace {
29 std::unique_ptr<IndexerModel> IndexerModel::instance_ = nullptr;
30 std::mutex IndexerModel::mutex_;
GetInstance()31 IndexerModel* IndexerModel::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::IndexerModelNG());
38 #else
39 if (Container::IsCurrentUseNewPipeline()) {
40 instance_.reset(new NG::IndexerModelNG());
41 } else {
42 instance_.reset(new Framework::IndexerModelImpl());
43 }
44 #endif
45 }
46 }
47 return instance_.get();
48 }
49 } // namespace OHOS::Ace
50
51 namespace OHOS::Ace::Framework {
52 namespace {
53 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
54 const std::vector<V2::AlignStyle> ALIGN_STYLE = { V2::AlignStyle::LEFT, V2::AlignStyle::RIGHT };
55 const std::vector<NG::AlignStyle> NG_ALIGN_STYLE = { NG::AlignStyle::LEFT, NG::AlignStyle::RIGHT };
56 constexpr Dimension DEFAULT_ITEM_SIZE = 16.0_vp;
57 }; // namespace
58
ParseIndexerSelectedObject(const JSCallbackInfo & info,const JSRef<JSVal> & changeEventVal,bool isMethodProp=false)59 void JSIndexer::ParseIndexerSelectedObject(
60 const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal, bool isMethodProp = false)
61 {
62 CHECK_NULL_VOID(changeEventVal->IsFunction());
63 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
64 auto changeEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const int32_t selected) {
65 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
66 ACE_SCORING_EVENT("Indexer.SelectedChangeEvent");
67 auto newJSVal = JSRef<JSVal>::Make(ToJSValue(selected));
68 func->ExecuteJS(1, &newJSVal);
69 };
70
71 if (isMethodProp) {
72 IndexerModel::GetInstance()->SetChangeEvent(changeEvent);
73 } else {
74 IndexerModel::GetInstance()->SetCreatChangeEvent(changeEvent);
75 }
76 }
77
Create(const JSCallbackInfo & args)78 void JSIndexer::Create(const JSCallbackInfo& args)
79 {
80 if (args.Length() < 1 || !args[0]->IsObject()) {
81 return;
82 }
83 size_t length = 0;
84 int32_t selectedVal = 0;
85 std::vector<std::string> indexerArray;
86 JSRef<JSObject> paramObj = JSRef<JSObject>::Cast(args[0]);
87 JSRef<JSVal> arrayVal = paramObj->GetProperty("arrayValue");
88 if (arrayVal->IsArray()) {
89 JSRef<JSArray> jsArray = JSRef<JSArray>::Cast(arrayVal);
90 length = jsArray->Length();
91 for (size_t i = 0; i < length; i++) {
92 auto value = jsArray->GetValueAt(i);
93 if (value->IsString()) {
94 indexerArray.emplace_back(value->ToString());
95 }
96 }
97 }
98
99 JSRef<JSVal> selectedProperty = paramObj->GetProperty("selected");
100 if (selectedProperty->IsNumber()) {
101 selectedVal = selectedProperty->ToNumber<int32_t>();
102 IndexerModel::GetInstance()->Create(indexerArray, selectedVal);
103 } else if (length > 0 && selectedProperty->IsObject()) {
104 JSRef<JSObject> selectedObj = JSRef<JSObject>::Cast(selectedProperty);
105 auto selectedValueProperty = selectedObj->GetProperty("value");
106 if (selectedValueProperty->IsNumber()) {
107 selectedVal = selectedValueProperty->ToNumber<int32_t>();
108 }
109 IndexerModel::GetInstance()->Create(indexerArray, selectedVal);
110 JSRef<JSVal> changeEventVal = selectedObj->GetProperty("changeEvent");
111 if (!changeEventVal.IsEmpty()) {
112 if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) {
113 ParseIndexerSelectedObject(args, changeEventVal);
114 }
115 return;
116 }
117
118 args.ReturnSelf();
119 } else {
120 IndexerModel::GetInstance()->Create(indexerArray, selectedVal);
121 }
122 }
123
SetSelectedColor(const JSCallbackInfo & args)124 void JSIndexer::SetSelectedColor(const JSCallbackInfo& args)
125 {
126 if (args.Length() < 1) {
127 return;
128 }
129 IndexerModel::GetInstance()->SetSelectedColor(PaseColor(args));
130 }
131
SetColor(const JSCallbackInfo & args)132 void JSIndexer::SetColor(const JSCallbackInfo& args)
133 {
134 if (args.Length() < 1) {
135 return;
136 }
137 IndexerModel::GetInstance()->SetColor(PaseColor(args));
138 }
139
SetPopupColor(const JSCallbackInfo & args)140 void JSIndexer::SetPopupColor(const JSCallbackInfo& args)
141 {
142 if (args.Length() < 1) {
143 return;
144 }
145 IndexerModel::GetInstance()->SetPopupColor(PaseColor(args));
146 }
147
SetSelectedBackgroundColor(const JSCallbackInfo & args)148 void JSIndexer::SetSelectedBackgroundColor(const JSCallbackInfo& args)
149 {
150 if (args.Length() < 1) {
151 return;
152 }
153 IndexerModel::GetInstance()->SetSelectedBackgroundColor(PaseColor(args));
154 }
155
SetPopupBackground(const JSCallbackInfo & args)156 void JSIndexer::SetPopupBackground(const JSCallbackInfo& args)
157 {
158 if (args.Length() < 1) {
159 return;
160 }
161 IndexerModel::GetInstance()->SetPopupBackground(PaseColor(args));
162 }
163
SetUsingPopup(bool state)164 void JSIndexer::SetUsingPopup(bool state)
165 {
166 IndexerModel::GetInstance()->SetUsingPopup(state);
167 }
168
SetSelectedFont(const JSCallbackInfo & args)169 void JSIndexer::SetSelectedFont(const JSCallbackInfo& args)
170 {
171 if (args.Length() < 1) {
172 return;
173 }
174 std::optional<Dimension> fontSize;
175 std::optional<FontWeight> fontWeight;
176 std::optional<std::vector<std::string>> fontFamily;
177 std::optional<FontStyle> fontStyle;
178 if (args[0]->IsObject()) {
179 GetFontContent(args, fontSize, fontWeight, fontFamily, fontStyle);
180 }
181 IndexerModel::GetInstance()->SetSelectedFont(fontSize, fontWeight, fontFamily, fontStyle);
182 }
183
SetPopupFont(const JSCallbackInfo & args)184 void JSIndexer::SetPopupFont(const JSCallbackInfo& args)
185 {
186 if (args.Length() < 1) {
187 return;
188 }
189 std::optional<Dimension> fontSize;
190 std::optional<FontWeight> fontWeight;
191 std::optional<std::vector<std::string>> fontFamily;
192 std::optional<FontStyle> fontStyle;
193 if (args[0]->IsObject()) {
194 GetFontContent(args, fontSize, fontWeight, fontFamily, fontStyle);
195 }
196 IndexerModel::GetInstance()->SetPopupFont(fontSize, fontWeight, fontFamily, fontStyle);
197 }
198
SetFont(const JSCallbackInfo & args)199 void JSIndexer::SetFont(const JSCallbackInfo& args)
200 {
201 if (args.Length() < 1) {
202 return;
203 }
204 std::optional<Dimension> fontSize;
205 std::optional<FontWeight> fontWeight;
206 std::optional<std::vector<std::string>> fontFamily;
207 std::optional<FontStyle> fontStyle;
208 if (args[0]->IsObject()) {
209 GetFontContent(args, fontSize, fontWeight, fontFamily, fontStyle);
210 }
211 IndexerModel::GetInstance()->SetFont(fontSize, fontWeight, fontFamily, fontStyle);
212 }
213
JsOnSelected(const JSCallbackInfo & args)214 void JSIndexer::JsOnSelected(const JSCallbackInfo& args)
215 {
216 if (args[0]->IsFunction()) {
217 auto onSelected = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
218 const int32_t selected) {
219 JAVASCRIPT_EXECUTION_SCOPE(execCtx);
220 auto params = ConvertToJSValues(selected);
221 func->Call(JSRef<JSObject>(), params.size(), params.data());
222 };
223 IndexerModel::GetInstance()->SetOnSelected(onSelected);
224 }
225 }
226
JsOnRequestPopupData(const JSCallbackInfo & args)227 void JSIndexer::JsOnRequestPopupData(const JSCallbackInfo& args)
228 {
229 if (args[0]->IsFunction()) {
230 auto requestPopupData = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
231 const int32_t selected) {
232 std::vector<std::string> popupData;
233 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, popupData);
234 auto params = ConvertToJSValues(selected);
235 JSRef<JSArray> result = func->Call(JSRef<JSObject>(), params.size(), params.data());
236 if (result.IsEmpty()) {
237 return popupData;
238 }
239
240 if (!result->IsArray()) {
241 return popupData;
242 }
243
244 for (size_t i = 0; i < result->Length(); i++) {
245 if (result->GetValueAt(i)->IsString()) {
246 auto item = result->GetValueAt(i);
247 popupData.emplace_back(item->ToString());
248 }
249 }
250 return popupData;
251 };
252 IndexerModel::GetInstance()->SetOnRequestPopupData(requestPopupData);
253 }
254 }
255
JsOnPopupSelected(const JSCallbackInfo & args)256 void JSIndexer::JsOnPopupSelected(const JSCallbackInfo& args)
257 {
258 if (args[0]->IsFunction()) {
259 auto onPopupSelected = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
260 const int32_t selected) {
261 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
262 auto params = ConvertToJSValues(selected);
263 func->Call(JSRef<JSObject>(), params.size(), params.data());
264 };
265 IndexerModel::GetInstance()->SetOnPopupSelected(onPopupSelected);
266 }
267 }
268
GetFontContent(const JSCallbackInfo & args,std::optional<Dimension> & fontSize,std::optional<FontWeight> & fontWeight,std::optional<std::vector<std::string>> & fontFamily,std::optional<FontStyle> & fontStyle)269 void JSIndexer::GetFontContent(const JSCallbackInfo& args, std::optional<Dimension>& fontSize,
270 std::optional<FontWeight>& fontWeight, std::optional<std::vector<std::string>>& fontFamily,
271 std::optional<FontStyle>& fontStyle)
272 {
273 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
274 JSRef<JSVal> size = obj->GetProperty("size");
275 CalcDimension fontSizeData;
276 if (ParseJsDimensionFp(size, fontSizeData) && !fontSizeData.IsNegative() &&
277 fontSizeData.Unit() != DimensionUnit::PERCENT) {
278 fontSize = fontSizeData;
279 }
280
281 JSRef<JSVal> weight = obj->GetProperty("weight");
282 if (weight->IsString() || weight->IsNumber()) {
283 fontWeight = ConvertStrToFontWeight(weight->ToString());
284 }
285
286 JSRef<JSVal> family = obj->GetProperty("family");
287 std::vector<std::string> fontFamilies;
288 if (ParseJsFontFamilies(family, fontFamilies)) {
289 fontFamily = fontFamilies;
290 }
291
292 JSRef<JSVal> style = obj->GetProperty("style");
293 if (style->IsNumber()) {
294 int32_t value = style->ToNumber<int32_t>();
295 if (value >= 0 && value < static_cast<int32_t>(FONT_STYLES.size())) {
296 fontStyle = FONT_STYLES[value];
297 }
298 }
299 }
300
SetItemSize(const JSCallbackInfo & args)301 void JSIndexer::SetItemSize(const JSCallbackInfo& args)
302 {
303 if (args.Length() < 1) {
304 return;
305 }
306 CalcDimension itemSize;
307 if (ParseJsDimensionVp(args[0], itemSize) && GreatNotEqual(itemSize.Value(), 0.0) &&
308 itemSize.Unit() != DimensionUnit::PERCENT) {
309 IndexerModel::GetInstance()->SetItemSize(itemSize);
310 return;
311 }
312 IndexerModel::GetInstance()->SetItemSize(DEFAULT_ITEM_SIZE);
313 }
314
SetAlignStyle(const JSCallbackInfo & args)315 void JSIndexer::SetAlignStyle(const JSCallbackInfo& args)
316 {
317 if (args.Length() < 1) {
318 return;
319 }
320 int32_t value = Container::IsCurrentUseNewPipeline() ? static_cast<int32_t>(NG::AlignStyle::RIGHT)
321 : static_cast<int32_t>(V2::AlignStyle::RIGHT);
322 auto alignValue = -1;
323 if (args[0]->IsNumber()) {
324 alignValue = args[0]->ToNumber<int32_t>();
325 }
326 if (alignValue >= 0 && alignValue < static_cast<int32_t>(ALIGN_STYLE.size())) {
327 value = alignValue;
328 }
329 IndexerModel::GetInstance()->SetAlignStyle(value);
330 CalcDimension popupHorizontalSpace(-1.0);
331 if (args.Length() > 1) {
332 ParseJsDimensionVp(args[1], popupHorizontalSpace);
333 }
334 IndexerModel::GetInstance()->SetPopupHorizontalSpace(popupHorizontalSpace);
335 }
336
SetSelected(const JSCallbackInfo & args)337 void JSIndexer::SetSelected(const JSCallbackInfo& args)
338 {
339 if (args.Length() >= 1) {
340 int32_t selected = 0;
341 if (ParseJsInteger<int32_t>(args[0], selected)) {
342 IndexerModel::GetInstance()->SetSelected(selected);
343 }
344 if (args.Length() > 1 && args[1]->IsFunction()) {
345 ParseIndexerSelectedObject(args, args[1], true);
346 }
347 }
348 }
349
SetPopupPosition(const JSCallbackInfo & args)350 void JSIndexer::SetPopupPosition(const JSCallbackInfo& args)
351 {
352 if (args[0]->IsObject()) {
353 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
354 std::optional<Dimension> xOpt;
355 std::optional<Dimension> yOpt;
356 CalcDimension x;
357 CalcDimension y;
358 JSRef<JSVal> xVal = obj->GetProperty("x");
359 JSRef<JSVal> yVal = obj->GetProperty("y");
360 if ((xVal->IsString() && StringUtils::StringToCalcDimensionNG(xVal->ToString(), x, false)) ||
361 (!xVal->IsString() && JSViewAbstract::ParseJsDimensionVp(xVal, x))) {
362 xOpt = x;
363 }
364 IndexerModel::GetInstance()->SetPopupPositionX(xOpt);
365 if ((yVal->IsString() && StringUtils::StringToCalcDimensionNG(yVal->ToString(), y, false)) ||
366 (!yVal->IsString() && JSViewAbstract::ParseJsDimensionVp(yVal, y))) {
367 yOpt = y;
368 }
369 IndexerModel::GetInstance()->SetPopupPositionY(yOpt);
370 }
371 }
372
SetPopupSelectedColor(const JSCallbackInfo & args)373 void JSIndexer::SetPopupSelectedColor(const JSCallbackInfo& args)
374 {
375 if (args.Length() < 1) {
376 return;
377 }
378 IndexerModel::GetInstance()->SetPopupSelectedColor(PaseColor(args));
379 CalcDimension popupHorizontalSpace(-1.0);
380 if (args.Length() > 1) {
381 ParseJsDimensionVp(args[1], popupHorizontalSpace);
382 }
383 IndexerModel::GetInstance()->SetPopupHorizontalSpace(popupHorizontalSpace);
384 }
385
SetPopupUnselectedColor(const JSCallbackInfo & args)386 void JSIndexer::SetPopupUnselectedColor(const JSCallbackInfo& args)
387 {
388 if (args.Length() < 1) {
389 return;
390 }
391 IndexerModel::GetInstance()->SetPopupUnselectedColor(PaseColor(args));
392 }
393
SetPopupItemFont(const JSCallbackInfo & args)394 void JSIndexer::SetPopupItemFont(const JSCallbackInfo& args)
395 {
396 CalcDimension fontSize;
397 std::string weight;
398 if (args[0]->IsObject()) {
399 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
400 JSRef<JSVal> size = obj->GetProperty("size");
401 if (!size->IsNull()) {
402 CalcDimension fontSizeData;
403 if (ParseJsDimensionFp(size, fontSizeData) && !fontSizeData.IsNegative() &&
404 fontSizeData.Unit() != DimensionUnit::PERCENT) {
405 fontSize = fontSizeData;
406 }
407 }
408
409 auto jsWeight = obj->GetProperty("weight");
410 if (!jsWeight->IsNull()) {
411 if (jsWeight->IsNumber()) {
412 weight = std::to_string(jsWeight->ToNumber<int32_t>());
413 } else {
414 ParseJsString(jsWeight, weight);
415 }
416 }
417 }
418 IndexerModel::GetInstance()->SetFontSize(fontSize);
419 IndexerModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(weight, FontWeight::MEDIUM));
420 }
421
SetPopupItemBackgroundColor(const JSCallbackInfo & args)422 void JSIndexer::SetPopupItemBackgroundColor(const JSCallbackInfo& args)
423 {
424 if (args.Length() < 1) {
425 return;
426 }
427 IndexerModel::GetInstance()->SetPopupItemBackground(PaseColor(args));
428 }
429
PaseColor(const JSCallbackInfo & args)430 std::optional<Color> JSIndexer::PaseColor(const JSCallbackInfo& args)
431 {
432 std::optional<Color> colorOpt;
433 Color color;
434 if (ParseJsColor(args[0], color)) {
435 colorOpt = color;
436 }
437 return colorOpt;
438 }
439
SetAutoCollapse(bool state)440 void JSIndexer::SetAutoCollapse(bool state)
441 {
442 IndexerModel::GetInstance()->SetAutoCollapse(state);
443 }
444
JSBind(BindingTarget globalObj)445 void JSIndexer::JSBind(BindingTarget globalObj)
446 {
447 MethodOptions opt = MethodOptions::NONE;
448 JSClass<JSIndexer>::Declare("AlphabetIndexer");
449 JSClass<JSIndexer>::StaticMethod("create", &JSIndexer::Create);
450 // API7 onSelected deprecated
451 JSClass<JSIndexer>::StaticMethod("onSelected", &JSIndexer::JsOnSelected);
452 JSClass<JSIndexer>::StaticMethod("onSelect", &JSIndexer::JsOnSelected);
453 JSClass<JSIndexer>::StaticMethod("color", &JSIndexer::SetColor, opt);
454 JSClass<JSIndexer>::StaticMethod("selectedColor", &JSIndexer::SetSelectedColor, opt);
455 JSClass<JSIndexer>::StaticMethod("popupColor", &JSIndexer::SetPopupColor, opt);
456 JSClass<JSIndexer>::StaticMethod("selectedBackgroundColor", &JSIndexer::SetSelectedBackgroundColor, opt);
457 JSClass<JSIndexer>::StaticMethod("popupBackground", &JSIndexer::SetPopupBackground, opt);
458 JSClass<JSIndexer>::StaticMethod("usingPopup", &JSIndexer::SetUsingPopup, opt);
459 JSClass<JSIndexer>::StaticMethod("selectedFont", &JSIndexer::SetSelectedFont);
460 JSClass<JSIndexer>::StaticMethod("font", &JSIndexer::SetFont);
461 JSClass<JSIndexer>::StaticMethod("popupFont", &JSIndexer::SetPopupFont);
462 JSClass<JSIndexer>::StaticMethod("itemSize", &JSIndexer::SetItemSize, opt);
463 JSClass<JSIndexer>::StaticMethod("alignStyle", &JSIndexer::SetAlignStyle, opt);
464 JSClass<JSIndexer>::StaticMethod("onRequestPopupData", &JSIndexer::JsOnRequestPopupData, opt);
465 JSClass<JSIndexer>::StaticMethod("selected", &JSIndexer::SetSelected, opt);
466 JSClass<JSIndexer>::StaticMethod("popupPosition", &JSIndexer::SetPopupPosition, opt);
467 JSClass<JSIndexer>::StaticMethod("popupSelectedColor", &JSIndexer::SetPopupSelectedColor, opt);
468 JSClass<JSIndexer>::StaticMethod("popupUnselectedColor", &JSIndexer::SetPopupUnselectedColor, opt);
469 JSClass<JSIndexer>::StaticMethod("popupItemFont", &JSIndexer::SetPopupItemFont);
470 JSClass<JSIndexer>::StaticMethod("popupItemBackgroundColor", &JSIndexer::SetPopupItemBackgroundColor, opt);
471 JSClass<JSIndexer>::StaticMethod("autoCollapse", &JSIndexer::SetAutoCollapse, opt);
472 // keep compatible, need remove after
473 JSClass<JSIndexer>::StaticMethod("onPopupSelected", &JSIndexer::JsOnPopupSelected, opt);
474 JSClass<JSIndexer>::StaticMethod("onPopupSelect", &JSIndexer::JsOnPopupSelected, opt);
475 JSClass<JSIndexer>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
476 JSClass<JSIndexer>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
477 JSClass<JSIndexer>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
478 JSClass<JSIndexer>::InheritAndBind<JSViewAbstract>(globalObj);
479 }
480 } // namespace OHOS::Ace::Framework
481