• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = 24.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         size_t length = 0;
82         int32_t selectedVal = 0;
83         auto param = JsonUtil::ParseJsonString(args[0]->ToString());
84         std::unique_ptr<JsonValue> arrayVal;
85         if (param && !param->IsNull()) {
86             arrayVal = param->GetValue("arrayValue");
87             if (arrayVal && arrayVal->IsArray()) {
88                 length = static_cast<uint32_t>(arrayVal->GetArraySize());
89             }
90         }
91         std::vector<std::string> indexerArray;
92         for (size_t i = 0; i < length; i++) {
93             auto value = arrayVal->GetArrayItem(i);
94             if (value) {
95                 indexerArray.emplace_back(value->GetString());
96             }
97         }
98 
99         JSRef<JSObject> paramObj = JSRef<JSObject>::Cast(args[0]);
100         JSRef<JSVal> selectedProperty = paramObj->GetProperty("selected");
101         if (selectedProperty->IsNumber()) {
102             selectedVal = selectedProperty->ToNumber<int32_t>();
103         }
104         IndexerModel::GetInstance()->Create(indexerArray, selectedVal);
105         if (length <= 0 || !selectedProperty->IsObject()) {
106             return;
107         }
108         JSRef<JSObject> selectedObj = JSRef<JSObject>::Cast(selectedProperty);
109         auto selectedValueProperty = selectedObj->GetProperty("value");
110         if (selectedValueProperty->IsNumber()) {
111             selectedVal = selectedValueProperty->ToNumber<int32_t>();
112         }
113         JSRef<JSVal> changeEventVal = selectedObj->GetProperty("changeEvent");
114         if (!changeEventVal.IsEmpty()) {
115             if (!changeEventVal->IsUndefined() && changeEventVal->IsFunction()) {
116                 ParseIndexerSelectedObject(args, changeEventVal);
117             }
118             return;
119         }
120 
121         args.ReturnSelf();
122     }
123 }
124 
SetSelectedColor(const JSCallbackInfo & args)125 void JSIndexer::SetSelectedColor(const JSCallbackInfo& args)
126 {
127     if (args.Length() < 1) {
128         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
129         return;
130     }
131     IndexerModel::GetInstance()->SetSelectedColor(PaseColor(args));
132 }
133 
SetColor(const JSCallbackInfo & args)134 void JSIndexer::SetColor(const JSCallbackInfo& args)
135 {
136     if (args.Length() < 1) {
137         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
138         return;
139     }
140     IndexerModel::GetInstance()->SetColor(PaseColor(args));
141 }
142 
SetPopupColor(const JSCallbackInfo & args)143 void JSIndexer::SetPopupColor(const JSCallbackInfo& args)
144 {
145     if (args.Length() < 1) {
146         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
147         return;
148     }
149     IndexerModel::GetInstance()->SetPopupColor(PaseColor(args));
150 }
151 
SetSelectedBackgroundColor(const JSCallbackInfo & args)152 void JSIndexer::SetSelectedBackgroundColor(const JSCallbackInfo& args)
153 {
154     if (args.Length() < 1) {
155         LOGW("The argv is wrong, it is supposed to have at least 1 argument");
156         return;
157     }
158     IndexerModel::GetInstance()->SetSelectedBackgroundColor(PaseColor(args));
159 }
160 
SetPopupBackground(const JSCallbackInfo & args)161 void JSIndexer::SetPopupBackground(const JSCallbackInfo& args)
162 {
163     if (args.Length() < 1) {
164         LOGW("The argv is wrong, it is supposed to have at least 1 argument");
165         return;
166     }
167     IndexerModel::GetInstance()->SetPopupBackground(PaseColor(args));
168 }
169 
SetUsingPopup(bool state)170 void JSIndexer::SetUsingPopup(bool state)
171 {
172     IndexerModel::GetInstance()->SetUsingPopup(state);
173 }
174 
SetSelectedFont(const JSCallbackInfo & args)175 void JSIndexer::SetSelectedFont(const JSCallbackInfo& args)
176 {
177     if (args.Length() < 1) {
178         LOGW("The argv is wrong, it is supposed to have at least 1 argument");
179         return;
180     }
181     std::optional<Dimension> fontSize;
182     std::optional<FontWeight> fontWeight;
183     std::optional<std::vector<std::string>> fontFamily;
184     std::optional<FontStyle> fontStyle;
185     if (args[0]->IsObject()) {
186         GetFontContent(args, fontSize, fontWeight, fontFamily, fontStyle);
187     }
188     IndexerModel::GetInstance()->SetSelectedFont(fontSize, fontWeight, fontFamily, fontStyle);
189 }
190 
SetPopupFont(const JSCallbackInfo & args)191 void JSIndexer::SetPopupFont(const JSCallbackInfo& args)
192 {
193     if (args.Length() < 1) {
194         LOGW("The argv is wrong, it is supposed to have at least 1 argument");
195         return;
196     }
197     std::optional<Dimension> fontSize;
198     std::optional<FontWeight> fontWeight;
199     std::optional<std::vector<std::string>> fontFamily;
200     std::optional<FontStyle> fontStyle;
201     if (args[0]->IsObject()) {
202         GetFontContent(args, fontSize, fontWeight, fontFamily, fontStyle);
203     }
204     IndexerModel::GetInstance()->SetPopupFont(fontSize, fontWeight, fontFamily, fontStyle);
205 }
206 
SetFont(const JSCallbackInfo & args)207 void JSIndexer::SetFont(const JSCallbackInfo& args)
208 {
209     if (args.Length() < 1) {
210         LOGW("The argv is wrong, it is supposed to have at least 1 argument");
211         return;
212     }
213     std::optional<Dimension> fontSize;
214     std::optional<FontWeight> fontWeight;
215     std::optional<std::vector<std::string>> fontFamily;
216     std::optional<FontStyle> fontStyle;
217     if (args[0]->IsObject()) {
218         GetFontContent(args, fontSize, fontWeight, fontFamily, fontStyle);
219     }
220     IndexerModel::GetInstance()->SetFont(fontSize, fontWeight, fontFamily, fontStyle);
221 }
222 
JsOnSelected(const JSCallbackInfo & args)223 void JSIndexer::JsOnSelected(const JSCallbackInfo& args)
224 {
225     if (args[0]->IsFunction()) {
226         auto onSelected = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
227                               const int32_t selected) {
228             JAVASCRIPT_EXECUTION_SCOPE(execCtx);
229             auto params = ConvertToJSValues(selected);
230             func->Call(JSRef<JSObject>(), params.size(), params.data());
231         };
232         IndexerModel::GetInstance()->SetOnSelected(onSelected);
233     }
234 }
235 
JsOnRequestPopupData(const JSCallbackInfo & args)236 void JSIndexer::JsOnRequestPopupData(const JSCallbackInfo& args)
237 {
238     if (args[0]->IsFunction()) {
239         auto requestPopupData = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
240                                     const int32_t selected) {
241             std::vector<std::string> popupData;
242             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, popupData);
243             auto params = ConvertToJSValues(selected);
244             JSRef<JSArray> result = func->Call(JSRef<JSObject>(), params.size(), params.data());
245             if (result.IsEmpty()) {
246                 LOGE("Error calling onRequestPopupData result is empty.");
247                 return popupData;
248             }
249 
250             if (!result->IsArray()) {
251                 LOGE("Error calling onRequestPopupData result is not array.");
252                 return popupData;
253             }
254 
255             for (size_t i = 0; i < result->Length(); i++) {
256                 if (result->GetValueAt(i)->IsString()) {
257                     auto item = result->GetValueAt(i);
258                     popupData.emplace_back(item->ToString());
259                 } else {
260                     LOGE("Error calling onRequestPopupData index %{public}zu is not string.", i);
261                 }
262             }
263             return popupData;
264         };
265         IndexerModel::GetInstance()->SetOnRequestPopupData(requestPopupData);
266     }
267 }
268 
JsOnPopupSelected(const JSCallbackInfo & args)269 void JSIndexer::JsOnPopupSelected(const JSCallbackInfo& args)
270 {
271     if (args[0]->IsFunction()) {
272         auto onPopupSelected = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](
273                                    const int32_t selected) {
274             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
275             auto params = ConvertToJSValues(selected);
276             func->Call(JSRef<JSObject>(), params.size(), params.data());
277         };
278         IndexerModel::GetInstance()->SetOnPopupSelected(onPopupSelected);
279     }
280 }
281 
GetFontContent(const JSCallbackInfo & args,std::optional<Dimension> & fontSize,std::optional<FontWeight> & fontWeight,std::optional<std::vector<std::string>> & fontFamily,std::optional<FontStyle> & fontStyle)282 void JSIndexer::GetFontContent(const JSCallbackInfo& args, std::optional<Dimension>& fontSize,
283     std::optional<FontWeight>& fontWeight, std::optional<std::vector<std::string>>& fontFamily,
284     std::optional<FontStyle>& fontStyle)
285 {
286     JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
287     JSRef<JSVal> size = obj->GetProperty("size");
288     CalcDimension fontSizeData;
289     if (ParseJsDimensionFp(size, fontSizeData) && !fontSizeData.IsNegative() &&
290         fontSizeData.Unit() != DimensionUnit::PERCENT) {
291         fontSize = fontSizeData;
292     }
293 
294     JSRef<JSVal> weight = obj->GetProperty("weight");
295     if (weight->IsString() || weight->IsNumber()) {
296         fontWeight = ConvertStrToFontWeight(weight->ToString());
297     }
298 
299     JSRef<JSVal> family = obj->GetProperty("family");
300     std::vector<std::string> fontFamilies;
301     if (ParseJsFontFamilies(family, fontFamilies)) {
302         fontFamily = fontFamilies;
303     }
304 
305     JSRef<JSVal> style = obj->GetProperty("style");
306     if (style->IsNumber()) {
307         int32_t value = style->ToNumber<int32_t>();
308         if (value >= 0 && value < static_cast<int32_t>(FONT_STYLES.size())) {
309             fontStyle = FONT_STYLES[value];
310         }
311     }
312 }
313 
SetItemSize(const JSCallbackInfo & args)314 void JSIndexer::SetItemSize(const JSCallbackInfo& args)
315 {
316     if (args.Length() < 1) {
317         LOGW("The argv is wrong, it is supposed to have at least 1 argument");
318         return;
319     }
320     CalcDimension itemSize;
321     if (ParseJsDimensionVp(args[0], itemSize) && GreatNotEqual(itemSize.Value(), 0.0) &&
322         itemSize.Unit() != DimensionUnit::PERCENT) {
323         IndexerModel::GetInstance()->SetItemSize(itemSize);
324         return;
325     }
326     IndexerModel::GetInstance()->SetItemSize(DEFAULT_ITEM_SIZE);
327 }
328 
SetAlignStyle(const JSCallbackInfo & args)329 void JSIndexer::SetAlignStyle(const JSCallbackInfo& args)
330 {
331     if (args.Length() < 1) {
332         LOGW("The argv is wrong, it is supposed to have at least 1 argument");
333         return;
334     }
335     int32_t value = Container::IsCurrentUseNewPipeline() ? static_cast<int32_t>(NG::AlignStyle::RIGHT)
336                                                          : static_cast<int32_t>(V2::AlignStyle::RIGHT);
337     auto alignValue = -1;
338     if (args[0]->IsNumber()) {
339         alignValue = args[0]->ToNumber<int32_t>();
340     }
341     if (alignValue >= 0 && alignValue < static_cast<int32_t>(ALIGN_STYLE.size())) {
342         value = alignValue;
343     }
344     IndexerModel::GetInstance()->SetAlignStyle(value);
345     CalcDimension popupHorizontalSpace(-1.0);
346     if (args.Length() > 1) {
347         ParseJsDimensionVp(args[1], popupHorizontalSpace);
348     }
349     IndexerModel::GetInstance()->SetPopupHorizontalSpace(popupHorizontalSpace);
350 }
351 
SetSelected(const JSCallbackInfo & args)352 void JSIndexer::SetSelected(const JSCallbackInfo& args)
353 {
354     if (args.Length() >= 1) {
355         int32_t selected = 0;
356         if (ParseJsInteger<int32_t>(args[0], selected)) {
357             IndexerModel::GetInstance()->SetSelected(selected);
358         }
359         if (args.Length() > 1 && args[1]->IsFunction()) {
360             ParseIndexerSelectedObject(args, args[1], true);
361         }
362     }
363 }
364 
SetPopupPosition(const JSCallbackInfo & args)365 void JSIndexer::SetPopupPosition(const JSCallbackInfo& args)
366 {
367     if (args[0]->IsObject()) {
368         JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
369         std::optional<Dimension> xOpt;
370         std::optional<Dimension> yOpt;
371         CalcDimension x;
372         CalcDimension y;
373         JSRef<JSVal> xVal = obj->GetProperty("x");
374         JSRef<JSVal> yVal = obj->GetProperty("y");
375         if (JSViewAbstract::ParseJsDimensionVp(xVal, x)) {
376             xOpt = x;
377         }
378         IndexerModel::GetInstance()->SetPopupPositionX(xOpt);
379         if (JSViewAbstract::ParseJsDimensionVp(yVal, y)) {
380             yOpt = y;
381         }
382         IndexerModel::GetInstance()->SetPopupPositionY(yOpt);
383     }
384 }
385 
SetPopupSelectedColor(const JSCallbackInfo & args)386 void JSIndexer::SetPopupSelectedColor(const JSCallbackInfo& args)
387 {
388     if (args.Length() < 1) {
389         LOGW("The argv is wrong, it is supposed to have at least 1 argument");
390         return;
391     }
392     IndexerModel::GetInstance()->SetPopupSelectedColor(PaseColor(args));
393     CalcDimension popupHorizontalSpace(-1.0);
394     if (args.Length() > 1) {
395         ParseJsDimensionVp(args[1], popupHorizontalSpace);
396     }
397     IndexerModel::GetInstance()->SetPopupHorizontalSpace(popupHorizontalSpace);
398 }
399 
SetPopupUnselectedColor(const JSCallbackInfo & args)400 void JSIndexer::SetPopupUnselectedColor(const JSCallbackInfo& args)
401 {
402     if (args.Length() < 1) {
403         LOGW("The argv is wrong, it is supposed to have at least 1 argument");
404         return;
405     }
406     IndexerModel::GetInstance()->SetPopupUnselectedColor(PaseColor(args));
407 }
408 
SetPopupItemFont(const JSCallbackInfo & args)409 void JSIndexer::SetPopupItemFont(const JSCallbackInfo& args)
410 {
411     CalcDimension fontSize;
412     std::string weight;
413     if (args[0]->IsObject()) {
414         JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
415         JSRef<JSVal> size = obj->GetProperty("size");
416         if (!size->IsNull()) {
417             CalcDimension fontSizeData;
418             if (ParseJsDimensionFp(size, fontSizeData) && !fontSizeData.IsNegative() &&
419                 fontSizeData.Unit() != DimensionUnit::PERCENT) {
420                 fontSize = fontSizeData;
421             }
422         }
423 
424         auto jsWeight = obj->GetProperty("weight");
425         if (!jsWeight->IsNull()) {
426             if (jsWeight->IsNumber()) {
427                 weight = std::to_string(jsWeight->ToNumber<int32_t>());
428             } else {
429                 ParseJsString(jsWeight, weight);
430             }
431         }
432     }
433     IndexerModel::GetInstance()->SetFontSize(fontSize);
434     IndexerModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(weight, FontWeight::MEDIUM));
435 }
436 
SetPopupItemBackgroundColor(const JSCallbackInfo & args)437 void JSIndexer::SetPopupItemBackgroundColor(const JSCallbackInfo& args)
438 {
439     if (args.Length() < 1) {
440         LOGW("The argv is wrong, it is supposed to have at least 1 argument");
441         return;
442     }
443     IndexerModel::GetInstance()->SetPopupItemBackground(PaseColor(args));
444 }
445 
PaseColor(const JSCallbackInfo & args)446 std::optional<Color> JSIndexer::PaseColor(const JSCallbackInfo& args)
447 {
448     std::optional<Color> colorOpt;
449     Color color;
450     if (ParseJsColor(args[0], color)) {
451         colorOpt = color;
452     }
453     return colorOpt;
454 }
455 
JSBind(BindingTarget globalObj)456 void JSIndexer::JSBind(BindingTarget globalObj)
457 {
458     MethodOptions opt = MethodOptions::NONE;
459     JSClass<JSIndexer>::Declare("AlphabetIndexer");
460     JSClass<JSIndexer>::StaticMethod("create", &JSIndexer::Create);
461     // API7 onSelected deprecated
462     JSClass<JSIndexer>::StaticMethod("onSelected", &JSIndexer::JsOnSelected);
463     JSClass<JSIndexer>::StaticMethod("onSelect", &JSIndexer::JsOnSelected);
464     JSClass<JSIndexer>::StaticMethod("color", &JSIndexer::SetColor, opt);
465     JSClass<JSIndexer>::StaticMethod("selectedColor", &JSIndexer::SetSelectedColor, opt);
466     JSClass<JSIndexer>::StaticMethod("popupColor", &JSIndexer::SetPopupColor, opt);
467     JSClass<JSIndexer>::StaticMethod("selectedBackgroundColor", &JSIndexer::SetSelectedBackgroundColor, opt);
468     JSClass<JSIndexer>::StaticMethod("popupBackground", &JSIndexer::SetPopupBackground, opt);
469     JSClass<JSIndexer>::StaticMethod("usingPopup", &JSIndexer::SetUsingPopup, opt);
470     JSClass<JSIndexer>::StaticMethod("selectedFont", &JSIndexer::SetSelectedFont);
471     JSClass<JSIndexer>::StaticMethod("font", &JSIndexer::SetFont);
472     JSClass<JSIndexer>::StaticMethod("popupFont", &JSIndexer::SetPopupFont);
473     JSClass<JSIndexer>::StaticMethod("itemSize", &JSIndexer::SetItemSize, opt);
474     JSClass<JSIndexer>::StaticMethod("alignStyle", &JSIndexer::SetAlignStyle, opt);
475     JSClass<JSIndexer>::StaticMethod("onRequestPopupData", &JSIndexer::JsOnRequestPopupData, opt);
476     JSClass<JSIndexer>::StaticMethod("selected", &JSIndexer::SetSelected, opt);
477     JSClass<JSIndexer>::StaticMethod("popupPosition", &JSIndexer::SetPopupPosition, opt);
478     JSClass<JSIndexer>::StaticMethod("popupSelectedColor", &JSIndexer::SetPopupSelectedColor, opt);
479     JSClass<JSIndexer>::StaticMethod("popupUnselectedColor", &JSIndexer::SetPopupUnselectedColor, opt);
480     JSClass<JSIndexer>::StaticMethod("popupItemFont", &JSIndexer::SetPopupItemFont);
481     JSClass<JSIndexer>::StaticMethod("popupItemBackgroundColor", &JSIndexer::SetPopupItemBackgroundColor, opt);
482     // keep compatible, need remove after
483     JSClass<JSIndexer>::StaticMethod("onPopupSelected", &JSIndexer::JsOnPopupSelected, opt);
484     JSClass<JSIndexer>::StaticMethod("onPopupSelect", &JSIndexer::JsOnPopupSelected, opt);
485     JSClass<JSIndexer>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
486     JSClass<JSIndexer>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
487     JSClass<JSIndexer>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
488     JSClass<JSIndexer>::InheritAndBind<JSViewAbstract>(globalObj);
489 }
490 } // namespace OHOS::Ace::Framework
491