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_indexer.h"
17
18 #include "base/geometry/dimension.h"
19 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
20 #include "bridge/declarative_frontend/jsview/js_scroller.h"
21 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
22 #include "bridge/declarative_frontend/view_stack_processor.h"
23 #include "core/components_ng/pattern/indexer/indexer_event_hub.h"
24 #include "core/components_ng/pattern/indexer/indexer_theme.h"
25 #include "core/components_ng/pattern/indexer/indexer_view.h"
26 #include "core/components_v2/indexer/indexer_component.h"
27 #include "core/components_v2/indexer/indexer_event_info.h"
28 #include "core/components_v2/indexer/render_indexer.h"
29
30 namespace OHOS::Ace::Framework {
31 namespace {
32 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
33 const std::vector<V2::AlignStyle> ALIGN_STYLE = { V2::AlignStyle::LEFT, V2::AlignStyle::RIGHT };
34 const std::vector<NG::AlignStyle> NG_ALIGN_STYLE = {NG::AlignStyle::LEFT, NG::AlignStyle::RIGHT};
35 }; // namespace
36
Create(const JSCallbackInfo & args)37 void JSIndexer::Create(const JSCallbackInfo& args)
38 {
39 if (args.Length() >= 1 && args[0]->IsObject()) {
40 auto param = JsonUtil::ParseJsonString(args[0]->ToString());
41 if (!param || param->IsNull()) {
42 LOGE("JSIndexer::Create param is null");
43 return;
44 }
45 std::vector<std::string> indexerArray;
46
47 auto arrayVal = param->GetValue("arrayValue");
48 if (!arrayVal || !arrayVal->IsArray()) {
49 LOGW("info is invalid");
50 return;
51 }
52
53 size_t length = static_cast<uint32_t>(arrayVal->GetArraySize());
54 if (length <= 0) {
55 LOGE("info is invalid");
56 return;
57 }
58 for (size_t i = 0; i < length; i++) {
59 auto value = arrayVal->GetArrayItem(i);
60 if (!value) {
61 return;
62 }
63 indexerArray.emplace_back(value->GetString());
64 }
65
66 auto selectedVal = param->GetInt("selected", 0);
67
68 if (Container::IsCurrentUseNewPipeline()) {
69 NG::IndexerView::Create(indexerArray, selectedVal);
70 return;
71 }
72
73 auto indexerComponent =
74 AceType::MakeRefPtr<V2::IndexerComponent>(indexerArray, selectedVal);
75 ViewStackProcessor::GetInstance()->ClaimElementId(indexerComponent);
76 ViewStackProcessor::GetInstance()->Push(indexerComponent);
77 JSInteractableView::SetFocusable(true);
78 JSInteractableView::SetFocusNode(true);
79 args.ReturnSelf();
80 }
81 }
82
SetSelectedColor(const JSCallbackInfo & args)83 void JSIndexer::SetSelectedColor(const JSCallbackInfo& args)
84 {
85 if (args.Length() < 1) {
86 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
87 return;
88 }
89 Color color;
90 if (!ParseJsColor(args[0], color)) {
91 return;
92 }
93 if (Container::IsCurrentUseNewPipeline()) {
94 NG::IndexerView::SetSelectedColor(color);
95 return;
96 }
97 auto indexerComponent =
98 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
99 if (indexerComponent) {
100 auto textStyle = indexerComponent->GetActiveTextStyle();
101 textStyle.SetTextColor(color);
102 indexerComponent->SetActiveTextStyle(std::move(textStyle));
103 }
104 }
105
SetColor(const JSCallbackInfo & args)106 void JSIndexer::SetColor(const JSCallbackInfo& args)
107 {
108 if (args.Length() < 1) {
109 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
110 return;
111 }
112 Color color;
113 if (!ParseJsColor(args[0], color)) {
114 return;
115 }
116 if (Container::IsCurrentUseNewPipeline()) {
117 NG::IndexerView::SetColor(color);
118 return;
119 }
120 auto indexerComponent =
121 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
122 if (indexerComponent) {
123 auto textStyle = indexerComponent->GetNormalTextStyle();
124 textStyle.SetTextColor(color);
125 indexerComponent->SetNormalTextStyle(std::move(textStyle));
126 }
127 }
128
SetPopupColor(const JSCallbackInfo & args)129 void JSIndexer::SetPopupColor(const JSCallbackInfo& args)
130 {
131 if (args.Length() < 1) {
132 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
133 return;
134 }
135 Color color;
136 if (!ParseJsColor(args[0], color)) {
137 return;
138 }
139 if (Container::IsCurrentUseNewPipeline()) {
140 NG::IndexerView::SetPopupColor(color);
141 return;
142 }
143 auto indexerComponent =
144 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
145 if (indexerComponent) {
146 auto textStyle = indexerComponent->GetBubbleTextStyle();
147 textStyle.SetTextColor(color);
148 indexerComponent->SetBubbleTextStyle(std::move(textStyle));
149 }
150 }
151
SetSelectedBackgroundColor(const JSCallbackInfo & args)152 void JSIndexer::SetSelectedBackgroundColor(const JSCallbackInfo& args)
153 {
154 if (args.Length() < 1) {
155 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
156 return;
157 }
158 Color color;
159 if (!ParseJsColor(args[0], color)) {
160 return;
161 }
162 if (Container::IsCurrentUseNewPipeline()) {
163 NG::IndexerView::SetSelectedBackgroundColor(color);
164 return;
165 }
166 auto indexerComponent =
167 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
168 if (indexerComponent) {
169 indexerComponent->SetSelectedBackgroundColor(color);
170 }
171 }
172
SetPopupBackground(const JSCallbackInfo & args)173 void JSIndexer::SetPopupBackground(const JSCallbackInfo& args)
174 {
175 if (args.Length() < 1) {
176 LOGE("The argv is wrong, it is supposed to have at least 1 argument");
177 return;
178 }
179 Color color;
180 if (!ParseJsColor(args[0], color)) {
181 return;
182 }
183 if (Container::IsCurrentUseNewPipeline()) {
184 NG::IndexerView::SetPopupBackground(color);
185 return;
186 }
187 auto indexerComponent =
188 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
189 if (indexerComponent) {
190 indexerComponent->SetBubbleBackgroundColor(color);
191 }
192 }
193
SetUsingPopup(bool state)194 void JSIndexer::SetUsingPopup(bool state)
195 {
196 if (Container::IsCurrentUseNewPipeline()) {
197 NG::IndexerView::SetUsingPopup(state);
198 return;
199 }
200 auto indexerComponent =
201 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
202 if (indexerComponent) {
203 indexerComponent->SetBubbleEnabled(state);
204 }
205 }
206
SetSelectedFont(const JSCallbackInfo & args)207 void JSIndexer::SetSelectedFont(const JSCallbackInfo& args)
208 {
209 if (Container::IsCurrentUseNewPipeline()) {
210 if (args.Length() >= 1 && args[0]->IsObject()) {
211 TextStyle textStyle;
212 GetFontContent(args, textStyle);
213 NG::IndexerView::SetSelectedFont(textStyle);
214 }
215 return;
216 }
217 if (args.Length() >= 1 && args[0]->IsObject()) {
218 auto indexerComponent =
219 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
220 if (indexerComponent) {
221 auto textStyle = indexerComponent->GetActiveTextStyle();
222 GetFontContent(args, textStyle);
223 indexerComponent->SetActiveTextStyle(std::move(textStyle));
224 }
225 }
226 }
227
SetPopupFont(const JSCallbackInfo & args)228 void JSIndexer::SetPopupFont(const JSCallbackInfo& args)
229 {
230 if (Container::IsCurrentUseNewPipeline()) {
231 if (args.Length() >= 1 && args[0]->IsObject()) {
232 TextStyle textStyle;
233 GetFontContent(args, textStyle);
234 NG::IndexerView::SetPopupFont(textStyle);
235 }
236 return;
237 }
238 if (args.Length() >= 1 && args[0]->IsObject()) {
239 auto indexerComponent =
240 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
241 if (indexerComponent) {
242 auto textStyle = indexerComponent->GetBubbleTextStyle();
243 GetFontContent(args, textStyle);
244 indexerComponent->SetBubbleTextStyle(std::move(textStyle));
245 }
246 }
247 }
248
SetFont(const JSCallbackInfo & args)249 void JSIndexer::SetFont(const JSCallbackInfo& args)
250 {
251 if (Container::IsCurrentUseNewPipeline()) {
252 if (args.Length() >= 1 && args[0]->IsObject()) {
253 TextStyle textStyle;
254 GetFontContent(args, textStyle);
255 NG::IndexerView::SetFont(textStyle);
256 }
257 return;
258 }
259 if (args.Length() >= 1 && args[0]->IsObject()) {
260 auto indexerComponent =
261 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
262 if (indexerComponent) {
263 auto textStyle = indexerComponent->GetNormalTextStyle();
264 GetFontContent(args, textStyle);
265 indexerComponent->SetNormalTextStyle(std::move(textStyle));
266 }
267 }
268 }
269
JsOnSelected(const JSCallbackInfo & args)270 void JSIndexer::JsOnSelected(const JSCallbackInfo& args)
271 {
272 if (Container::IsCurrentUseNewPipeline()) {
273 if (args[0]->IsFunction()) {
274 auto onSelected = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](const int32_t selected) {
275 JAVASCRIPT_EXECUTION_SCOPE(execCtx);
276 auto params = ConvertToJSValues(selected);
277 func->Call(JSRef<JSObject>(), params.size(), params.data());
278 };
279 NG::IndexerView::SetOnSelected(onSelected);
280 }
281 return;
282 }
283 if (args[0]->IsFunction()) {
284 auto onSelected = EventMarker(
285 [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](const BaseEventInfo* info) {
286 JAVASCRIPT_EXECUTION_SCOPE(execCtx);
287 auto eventInfo = TypeInfoHelper::DynamicCast<V2::IndexerEventInfo>(info);
288 if (!eventInfo) {
289 return;
290 }
291 auto params = ConvertToJSValues(eventInfo->GetSelectedIndex());
292 func->Call(JSRef<JSObject>(), params.size(), params.data());
293 });
294
295 auto indexerComponent =
296 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
297 if (indexerComponent) {
298 indexerComponent->SetSelectedEvent(onSelected);
299 }
300 }
301 }
302
JsOnRequestPopupData(const JSCallbackInfo & args)303 void JSIndexer::JsOnRequestPopupData(const JSCallbackInfo& args)
304 {
305 if (Container::IsCurrentUseNewPipeline()) {
306 if (args[0]->IsFunction()) {
307 auto requestPopupData = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]
308 (const int32_t selected) {
309 std::vector<std::string> popupData;
310 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, popupData);
311 auto params = ConvertToJSValues(selected);
312 JSRef<JSArray> result = func->Call(JSRef<JSObject>(), params.size(), params.data());
313 if (result.IsEmpty()) {
314 LOGE("Error calling onRequestPopupData result is empty.");
315 return popupData;
316 }
317
318 if (!result->IsArray()) {
319 LOGE("Error calling onRequestPopupData result is not array.");
320 return popupData;
321 }
322
323 for (size_t i = 0; i < result->Length(); i++) {
324 if (result->GetValueAt(i)->IsString()) {
325 auto item = result->GetValueAt(i);
326 popupData.emplace_back(item->ToString());
327 } else {
328 LOGE("Error calling onRequestPopupData index %{public}zu is not string.", i);
329 }
330 }
331 return popupData;
332 };
333 NG::IndexerView::SetOnRequestPopupData(requestPopupData);
334 }
335 return;
336 }
337 if (args[0]->IsFunction()) {
338 auto requestPopupData =
339 [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])]
340 (std::shared_ptr<V2::IndexerEventInfo> info) {
341 std::vector<std::string> popupData;
342 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, popupData);
343 auto params = ConvertToJSValues(info->GetSelectedIndex());
344 JSRef<JSArray> result = func->Call(JSRef<JSObject>(), params.size(), params.data());
345 if (result.IsEmpty()) {
346 LOGE("Error calling onRequestPopupData result is empty.");
347 return popupData;
348 }
349
350 if (!result->IsArray()) {
351 LOGE("Error calling onRequestPopupData result is not array.");
352 return popupData;
353 }
354
355 for (size_t i = 0; i < result->Length(); i++) {
356 if (result->GetValueAt(i)->IsString()) {
357 auto item = result->GetValueAt(i);
358 popupData.emplace_back(item->ToString());
359 } else {
360 LOGE("Error calling onRequestPopupData index %{public}zu is not string.", i);
361 }
362 }
363 return popupData;
364 };
365
366 auto indexerComponent =
367 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
368 if (indexerComponent) {
369 indexerComponent->SetRequestPopupDataFunc(requestPopupData);
370 }
371 }
372 }
373
JsOnPopupSelected(const JSCallbackInfo & args)374 void JSIndexer::JsOnPopupSelected(const JSCallbackInfo& args)
375 {
376 if (Container::IsCurrentUseNewPipeline()) {
377 if (args[0]->IsFunction()) {
378 auto onPopupSelected = [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](const int32_t selected) {
379 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
380 auto params = ConvertToJSValues(selected);
381 func->Call(JSRef<JSObject>(), params.size(), params.data());
382 };
383 NG::IndexerView::SetOnPopupSelected(onPopupSelected);
384 }
385 return;
386 }
387 if (args[0]->IsFunction()) {
388 auto onPopupSelected = EventMarker(
389 [execCtx = args.GetExecutionContext(), func = JSRef<JSFunc>::Cast(args[0])](const BaseEventInfo* info) {
390 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
391 auto eventInfo = TypeInfoHelper::DynamicCast<V2::IndexerEventInfo>(info);
392 if (!eventInfo) {
393 return;
394 }
395 auto params = ConvertToJSValues(eventInfo->GetSelectedIndex());
396 func->Call(JSRef<JSObject>(), params.size(), params.data());
397 });
398
399 auto indexerComponent =
400 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
401 if (indexerComponent) {
402 indexerComponent->SetPopupSelectedEvent(onPopupSelected);
403 }
404 }
405 }
406
GetFontContent(const JSCallbackInfo & args,TextStyle & textStyle)407 void JSIndexer::GetFontContent(const JSCallbackInfo& args, TextStyle& textStyle)
408 {
409 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
410 JSRef<JSVal> size = obj->GetProperty("size");
411 Dimension fontSize;
412 if (ParseJsDimensionVp(size, fontSize)) {
413 textStyle.SetFontSize(fontSize);
414 }
415
416 JSRef<JSVal> weight = obj->GetProperty("weight");
417 if (weight->IsString()) {
418 textStyle.SetFontWeight(ConvertStrToFontWeight(weight->ToString()));
419 }
420
421 JSRef<JSVal> family = obj->GetProperty("family");
422 std::vector<std::string> fontFamilies;
423 if (ParseJsFontFamilies(family, fontFamilies)) {
424 textStyle.SetFontFamilies(fontFamilies);
425 }
426
427 JSRef<JSVal> style = obj->GetProperty("style");
428 if (style->IsNumber()) {
429 int32_t value = style->ToNumber<int32_t>();
430 if (value >= 0 && value < static_cast<int32_t>(FONT_STYLES.size())) {
431 textStyle.SetFontStyle(FONT_STYLES[value]);
432 }
433 }
434 }
435
SetItemSize(const JSCallbackInfo & args)436 void JSIndexer::SetItemSize(const JSCallbackInfo& args)
437 {
438 if (Container::IsCurrentUseNewPipeline()) {
439 if (args.Length() >= 1) {
440 Dimension value;
441 if (ParseJsDimensionVp(args[0], value)) {
442 NG::IndexerView::SetItemSize(value);
443 }
444 }
445 }
446 if (args.Length() >= 1) {
447 Dimension value;
448 if (ParseJsDimensionVp(args[0], value)) {
449 auto indexerComponent =
450 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
451 if (indexerComponent) {
452 indexerComponent->SetItemSize(value);
453 }
454 }
455 }
456 }
457
SetAlignStyle(int32_t value)458 void JSIndexer::SetAlignStyle(int32_t value)
459 {
460 if (Container::IsCurrentUseNewPipeline()) {
461 if (value >= 0 && value < static_cast<int32_t>(ALIGN_STYLE.size())) {
462 NG::IndexerView::SetAlignStyle(NG_ALIGN_STYLE[value]);
463 }
464 }
465 if (value >= 0 && value < static_cast<int32_t>(ALIGN_STYLE.size())) {
466 auto indexerComponent =
467 AceType::DynamicCast<V2::IndexerComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
468 if (indexerComponent) {
469 indexerComponent->SetAlignStyle(ALIGN_STYLE[value]);
470 }
471 }
472 }
473
SetSelected(const JSCallbackInfo & args)474 void JSIndexer::SetSelected(const JSCallbackInfo& args)
475 {
476 if (Container::IsCurrentUseNewPipeline()) {
477 if (args.Length() >= 1) {
478 int32_t selected = 0;
479 if (ParseJsInteger<int32_t>(args[0], selected)) {
480 NG::IndexerView::SetSelected(selected);
481 }
482 }
483 }
484 }
485
SetPopupPosition(const JSCallbackInfo & args)486 void JSIndexer::SetPopupPosition(const JSCallbackInfo& args)
487 {
488 if (Container::IsCurrentUseNewPipeline()) {
489 if (args.Length() >= 1) {
490 JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
491 float positionX = 0.0f;
492 float positionY = 0.0f;
493 if (ConvertFromJSValue(obj->GetProperty("x"), positionX)) {
494 NG::IndexerView::SetPopupPositionX(Dimension(positionX, DimensionUnit::VP));
495 }
496 if (ConvertFromJSValue(obj->GetProperty("y"), positionY)) {
497 NG::IndexerView::SetPopupPositionY(Dimension(positionY, DimensionUnit::VP));
498 }
499 }
500 }
501 }
502
JSBind(BindingTarget globalObj)503 void JSIndexer::JSBind(BindingTarget globalObj)
504 {
505 MethodOptions opt = MethodOptions::NONE;
506 JSClass<JSIndexer>::Declare("AlphabetIndexer");
507 JSClass<JSIndexer>::StaticMethod("create", &JSIndexer::Create);
508 // API7 onSelected deprecated
509 JSClass<JSIndexer>::StaticMethod("onSelected", &JSIndexer::JsOnSelected);
510 JSClass<JSIndexer>::StaticMethod("onSelect", &JSIndexer::JsOnSelected);
511 JSClass<JSIndexer>::StaticMethod("color", &JSIndexer::SetColor, opt);
512 JSClass<JSIndexer>::StaticMethod("selectedColor", &JSIndexer::SetSelectedColor, opt);
513 JSClass<JSIndexer>::StaticMethod("popupColor", &JSIndexer::SetPopupColor, opt);
514 JSClass<JSIndexer>::StaticMethod("selectedBackgroundColor", &JSIndexer::SetSelectedBackgroundColor, opt);
515 JSClass<JSIndexer>::StaticMethod("popupBackground", &JSIndexer::SetPopupBackground, opt);
516 JSClass<JSIndexer>::StaticMethod("usingPopup", &JSIndexer::SetUsingPopup, opt);
517 JSClass<JSIndexer>::StaticMethod("selectedFont", &JSIndexer::SetSelectedFont);
518 JSClass<JSIndexer>::StaticMethod("font", &JSIndexer::SetFont);
519 JSClass<JSIndexer>::StaticMethod("popupFont", &JSIndexer::SetPopupFont);
520 JSClass<JSIndexer>::StaticMethod("itemSize", &JSIndexer::SetItemSize, opt);
521 JSClass<JSIndexer>::StaticMethod("alignStyle", &JSIndexer::SetAlignStyle, opt);
522 JSClass<JSIndexer>::StaticMethod("onRequestPopupData", &JSIndexer::JsOnRequestPopupData, opt);
523 JSClass<JSIndexer>::StaticMethod("selected", &JSIndexer::SetSelected, opt);
524 JSClass<JSIndexer>::StaticMethod("popupPosition", &JSIndexer::SetPopupPosition, opt);
525 // keep compatible, need remove after
526 JSClass<JSIndexer>::StaticMethod("onPopupSelected", &JSIndexer::JsOnPopupSelected, opt);
527 JSClass<JSIndexer>::StaticMethod("onPopupSelect", &JSIndexer::JsOnPopupSelected, opt);
528 JSClass<JSIndexer>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
529 JSClass<JSIndexer>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
530 JSClass<JSIndexer>::Inherit<JSViewAbstract>();
531 JSClass<JSIndexer>::Bind(globalObj);
532 }
533 } // namespace OHOS::Ace::Framework
534