• 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 = 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