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_select.h"
17 
18 #include <cstdint>
19 #include <string>
20 #include <vector>
21 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
22 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
23 #endif
24 
25 #include "base/log/ace_scoring_log.h"
26 #include "base/utils/utils.h"
27 #include "bridge/common/utils/utils.h"
28 #include "bridge/declarative_frontend/engine/functions/js_function.h"
29 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
30 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
31 #include "bridge/declarative_frontend/jsview/js_symbol_modifier.h"
32 #include "bridge/declarative_frontend/jsview/models/select_model_impl.h"
33 #include "bridge/declarative_frontend/ark_theme/theme_apply/js_select_theme.h"
34 #include "core/components_ng/base/view_abstract_model.h"
35 #include "core/components_ng/base/view_stack_processor.h"
36 #include "core/components_ng/pattern/select/select_model.h"
37 #include "core/components_ng/pattern/select/select_model_ng.h"
38 #include "core/components_ng/pattern/select/select_properties.h"
39 #include "core/components_v2/inspector/inspector_constants.h"
40 #include "core/pipeline/pipeline_base.h"
41 
42 namespace OHOS::Ace {
43 std::unique_ptr<SelectModel> SelectModel::instance_ = nullptr;
44 std::mutex SelectModel::mutex_;
45 
GetInstance()46 SelectModel* SelectModel::GetInstance()
47 {
48     if (!instance_) {
49         std::lock_guard<std::mutex> lock(mutex_);
50         if (!instance_) {
51 #ifdef NG_BUILD
52             instance_.reset(new NG::SelectModelNG());
53 #else
54             if (Container::IsCurrentUseNewPipeline()) {
55                 instance_.reset(new NG::SelectModelNG());
56             } else {
57                 instance_.reset(new Framework::SelectModelImpl());
58             }
59 #endif
60         }
61     }
62     return instance_.get();
63 }
64 } // namespace OHOS::Ace
65 
66 namespace OHOS::Ace::Framework {
Create(const JSCallbackInfo& info)67 void JSSelect::Create(const JSCallbackInfo& info)
68 {
69     if (info.Length() < 0) {
70         return;
71     }
72     if (info[0]->IsArray()) {
73         auto paramArray = JSRef<JSArray>::Cast(info[0]);
74         size_t size = paramArray->Length();
75         std::vector<SelectParam> params(size);
76         for (size_t i = 0; i < size; i++) {
77             std::string value;
78             std::string icon;
79             JSRef<JSVal> indexVal = paramArray->GetValueAt(i);
80             if (!indexVal->IsObject()) {
81                 return;
82             }
83             auto indexObject = JSRef<JSObject>::Cast(indexVal);
84             auto selectValue = indexObject->GetProperty("value");
85             auto selectIcon = indexObject->GetProperty("icon");
86             auto selectSymbolIcon = indexObject->GetProperty("symbolIcon");
87             RefPtr<JSSymbolGlyphModifier> selectSymbol = AceType::MakeRefPtr<JSSymbolGlyphModifier>();
88             selectSymbol->symbol_ = selectSymbolIcon;
89             params[i].symbolModifier = selectSymbol;
90             ParseJsString(selectValue, value);
91             params[i].text = value;
92             if (selectSymbolIcon->IsObject()) {
93                 std::function<void(WeakPtr<NG::FrameNode>)> symbolApply = nullptr;
94                 JSViewAbstract::SetSymbolOptionApply(info, symbolApply, selectSymbolIcon);
95                 params[i].symbolIcon = symbolApply;
96             } else {
97                 ParseJsMedia(selectIcon, icon);
98                 params[i].icon = icon;
99             }
100         }
101         SelectModel::GetInstance()->Create(params);
102         JSSelectTheme::ApplyTheme();
103     }
104 }
105 
JSBind(BindingTarget globalObj)106 void JSSelect::JSBind(BindingTarget globalObj)
107 {
108     JSClass<JSSelect>::Declare("Select");
109     MethodOptions opt = MethodOptions::NONE;
110     JSClass<JSSelect>::StaticMethod("create", &JSSelect::Create, opt);
111 
112     JSClass<JSSelect>::StaticMethod("selected", &JSSelect::Selected, opt);
113     JSClass<JSSelect>::StaticMethod("value", &JSSelect::Value, opt);
114     JSClass<JSSelect>::StaticMethod("font", &JSSelect::Font, opt);
115     JSClass<JSSelect>::StaticMethod("fontColor", &JSSelect::FontColor, opt);
116     JSClass<JSSelect>::StaticMethod("selectedOptionBgColor", &JSSelect::SelectedOptionBgColor, opt);
117     JSClass<JSSelect>::StaticMethod("selectedOptionFont", &JSSelect::SelectedOptionFont, opt);
118     JSClass<JSSelect>::StaticMethod("selectedOptionFontColor", &JSSelect::SelectedOptionFontColor, opt);
119     JSClass<JSSelect>::StaticMethod("optionBgColor", &JSSelect::OptionBgColor, opt);
120     JSClass<JSSelect>::StaticMethod("optionFont", &JSSelect::OptionFont, opt);
121     JSClass<JSSelect>::StaticMethod("optionFontColor", &JSSelect::OptionFontColor, opt);
122     JSClass<JSSelect>::StaticMethod("onSelect", &JSSelect::OnSelected, opt);
123     JSClass<JSSelect>::StaticMethod("space", &JSSelect::SetSpace, opt);
124     JSClass<JSSelect>::StaticMethod("arrowPosition", &JSSelect::SetArrowPosition, opt);
125     JSClass<JSSelect>::StaticMethod("menuAlign", &JSSelect::SetMenuAlign, opt);
126 
127     // API7 onSelected deprecated
128     JSClass<JSSelect>::StaticMethod("onSelected", &JSSelect::OnSelected, opt);
129     JSClass<JSSelect>::StaticMethod("size", &JSSelect::JsSize);
130     JSClass<JSSelect>::StaticMethod("padding", &JSSelect::JsPadding);
131     JSClass<JSSelect>::StaticMethod("paddingTop", &JSSelect::SetPaddingTop, opt);
132     JSClass<JSSelect>::StaticMethod("paddingBottom", &JSSelect::SetPaddingBottom, opt);
133     JSClass<JSSelect>::StaticMethod("paddingLeft", &JSSelect::SetPaddingLeft, opt);
134     JSClass<JSSelect>::StaticMethod("paddingRight", &JSSelect::SetPaddingRight, opt);
135     JSClass<JSSelect>::StaticMethod("optionWidth", &JSSelect::SetOptionWidth, opt);
136     JSClass<JSSelect>::StaticMethod("optionHeight", &JSSelect::SetOptionHeight, opt);
137     JSClass<JSSelect>::StaticMethod("optionWidthFitTrigger", &JSSelect::SetOptionWidthFitTrigger, opt);
138     JSClass<JSSelect>::StaticMethod("menuBackgroundColor", &JSSelect::SetMenuBackgroundColor, opt);
139     JSClass<JSSelect>::StaticMethod("menuBackgroundBlurStyle", &JSSelect::SetMenuBackgroundBlurStyle, opt);
140     JSClass<JSSelect>::StaticMethod("divider", &JSSelect::SetDivider);
141     JSClass<JSSelect>::StaticMethod("controlSize", &JSSelect::SetControlSize);
142     JSClass<JSSelect>::StaticMethod("direction", &JSSelect::SetDirection, opt);
143 
144     JSClass<JSSelect>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
145     JSClass<JSSelect>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
146     JSClass<JSSelect>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
147     JSClass<JSSelect>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
148     JSClass<JSSelect>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
149     JSClass<JSSelect>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
150     JSClass<JSSelect>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
151     JSClass<JSSelect>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
152     JSClass<JSSelect>::InheritAndBind<JSViewAbstract>(globalObj);
153 }
154 
ParseSelectedObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)155 void ParseSelectedObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
156 {
157     CHECK_NULL_VOID(changeEventVal->IsFunction());
158 
159     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
160     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
161     auto onSelect = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](int32_t index) {
162         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
163         ACE_SCORING_EVENT("Select.SelectChangeEvent");
164         PipelineContext::SetCallBackNode(node);
165         auto newJSVal = JSRef<JSVal>::Make(ToJSValue(index));
166         func->ExecuteJS(1, &newJSVal);
167     };
168     SelectModel::GetInstance()->SetSelectChangeEvent(onSelect);
169 }
170 
Selected(const JSCallbackInfo& info)171 void JSSelect::Selected(const JSCallbackInfo& info)
172 {
173     if (info.Length() < 1 || info.Length() > 2) {
174         return;
175     }
176 
177     int32_t value = 0;
178     if (info.Length() > 0) {
179         ParseJsInteger<int32_t>(info[0], value);
180     }
181 
182     if (value < -1) {
183         value = -1;
184     }
185     if (info.Length() > 1 && info[1]->IsFunction()) {
186         ParseSelectedObject(info, info[1]);
187     }
188     TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "set selected index %{public}d", value);
189     SelectModel::GetInstance()->SetSelected(value);
190 }
191 
ParseValueObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)192 void ParseValueObject(const JSCallbackInfo& info, const JSRef<JSVal>& changeEventVal)
193 {
194     CHECK_NULL_VOID(changeEventVal->IsFunction());
195 
196     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(changeEventVal));
197     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
198     auto onSelect = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
199                         const std::string& value) {
200         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
201         ACE_SCORING_EVENT("Select.ValueChangeEvent");
202         PipelineContext::SetCallBackNode(node);
203         auto newJSVal = JSRef<JSVal>::Make(ToJSValue(value));
204         func->ExecuteJS(1, &newJSVal);
205     };
206     SelectModel::GetInstance()->SetValueChangeEvent(onSelect);
207 }
208 
Value(const JSCallbackInfo& info)209 void JSSelect::Value(const JSCallbackInfo& info)
210 {
211     if (info.Length() < 1 || info.Length() > 2) {
212         return;
213     }
214 
215     std::string value;
216     if (info.Length() > 0) {
217         ParseJsString(info[0], value);
218     }
219 
220     if (info.Length() > 1 && info[1]->IsFunction()) {
221         ParseValueObject(info, info[1]);
222     }
223     TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "value set by user");
224     SelectModel::GetInstance()->SetValue(value);
225 }
226 
Font(const JSCallbackInfo& info)227 void JSSelect::Font(const JSCallbackInfo& info)
228 {
229     if (info[0]->IsNull() || info[0]->IsUndefined()) {
230         ResetFont(SelectFontType::SELECT);
231         return;
232     }
233     if (!info[0]->IsObject()) {
234         return;
235     }
236     auto param = JSRef<JSObject>::Cast(info[0]);
237     ParseFontSize(param->GetProperty("size"), SelectFontType::SELECT);
238     ParseFontWeight(param->GetProperty("weight"), SelectFontType::SELECT);
239     ParseFontFamily(param->GetProperty("family"), SelectFontType::SELECT);
240     ParseFontStyle(param->GetProperty("style"), SelectFontType::SELECT);
241 }
242 
ParseFontSize(const JSRef<JSVal>& jsValue, SelectFontType type)243 void JSSelect::ParseFontSize(const JSRef<JSVal>& jsValue, SelectFontType type)
244 {
245     CalcDimension fontSize;
246     if (!ParseJsDimensionFp(jsValue, fontSize)) {
247         ResetFontSize(type);
248         return;
249     }
250     if (type == SelectFontType::SELECT) {
251         SelectModel::GetInstance()->SetFontSize(fontSize);
252     } else if (type == SelectFontType::OPTION) {
253         SelectModel::GetInstance()->SetOptionFontSize(fontSize);
254     } else if (type == SelectFontType::SELECTED_OPTION) {
255         SelectModel::GetInstance()->SetSelectedOptionFontSize(fontSize);
256     }
257 }
258 
ParseFontWeight(const JSRef<JSVal>& jsValue, SelectFontType type)259 void JSSelect::ParseFontWeight(const JSRef<JSVal>& jsValue, SelectFontType type)
260 {
261     std::string weight;
262     if (jsValue->IsNumber()) {
263         weight = std::to_string(jsValue->ToNumber<int32_t>());
264     } else {
265         ParseJsString(jsValue, weight);
266     }
267     if (type == SelectFontType::SELECT) {
268         SelectModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(weight, FontWeight::MEDIUM));
269     } else if (type == SelectFontType::OPTION) {
270         SelectModel::GetInstance()->SetOptionFontWeight(ConvertStrToFontWeight(weight, FontWeight::REGULAR));
271     } else if (type == SelectFontType::SELECTED_OPTION) {
272         SelectModel::GetInstance()->SetSelectedOptionFontWeight(ConvertStrToFontWeight(weight, FontWeight::REGULAR));
273     }
274 }
275 
ParseFontFamily(const JSRef<JSVal>& jsValue, SelectFontType type)276 void JSSelect::ParseFontFamily(const JSRef<JSVal>& jsValue, SelectFontType type)
277 {
278     if (!jsValue->IsString()) {
279         ResetFontFamily(type);
280         return;
281     }
282     auto family = ConvertStrToFontFamilies(jsValue->ToString());
283     if (type == SelectFontType::SELECT) {
284         SelectModel::GetInstance()->SetFontFamily(family);
285     } else if (type == SelectFontType::OPTION) {
286         SelectModel::GetInstance()->SetOptionFontFamily(family);
287     } else if (type == SelectFontType::SELECTED_OPTION) {
288         SelectModel::GetInstance()->SetSelectedOptionFontFamily(family);
289     }
290 }
291 
ParseFontStyle(const JSRef<JSVal>& jsValue, SelectFontType type)292 void JSSelect::ParseFontStyle(const JSRef<JSVal>& jsValue, SelectFontType type)
293 {
294     if (!jsValue->IsNumber()) {
295         ResetFontStyle(type);
296         return;
297     }
298     auto styleVal = static_cast<FontStyle>(jsValue->ToNumber<int32_t>());
299     if (type == SelectFontType::SELECT) {
300         SelectModel::GetInstance()->SetItalicFontStyle(styleVal);
301     } else if (type == SelectFontType::OPTION) {
302         SelectModel::GetInstance()->SetOptionItalicFontStyle(styleVal);
303     } else if (type == SelectFontType::SELECTED_OPTION) {
304         SelectModel::GetInstance()->SetSelectedOptionItalicFontStyle(styleVal);
305     }
306 }
307 
ResetFontSize(SelectFontType type)308 void JSSelect::ResetFontSize(SelectFontType type)
309 {
310     auto pipeline = PipelineBase::GetCurrentContext();
311     CHECK_NULL_VOID(pipeline);
312     auto selectTheme = pipeline->GetTheme<SelectTheme>();
313     CHECK_NULL_VOID(selectTheme);
314     if (type == SelectFontType::OPTION) {
315         SelectModel::GetInstance()->SetOptionFontSize(selectTheme->GetMenuFontSize());
316         return;
317     } else if (type == SelectFontType::SELECTED_OPTION) {
318         SelectModel::GetInstance()->SetSelectedOptionFontSize(selectTheme->GetMenuFontSize());
319         return;
320     }
321     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
322         SelectModel::GetInstance()->SetFontSize(selectTheme->GetFontSize());
323     } else {
324         auto controlSize = SelectModel::GetInstance()->GetControlSize();
325         SelectModel::GetInstance()->SetFontSize(selectTheme->GetFontSize(controlSize));
326     }
327 }
328 
ResetFontWeight(SelectFontType type)329 void JSSelect::ResetFontWeight(SelectFontType type)
330 {
331     if (type == SelectFontType::SELECT) {
332         SelectModel::GetInstance()->SetFontWeight(FontWeight::MEDIUM);
333     } else if (type == SelectFontType::OPTION) {
334         SelectModel::GetInstance()->SetOptionFontWeight(FontWeight::REGULAR);
335     } else if (type == SelectFontType::SELECTED_OPTION) {
336         SelectModel::GetInstance()->SetSelectedOptionFontWeight(FontWeight::REGULAR);
337     }
338 }
339 
ResetFontFamily(SelectFontType type)340 void JSSelect::ResetFontFamily(SelectFontType type)
341 {
342     auto pipeline = PipelineBase::GetCurrentContext();
343     CHECK_NULL_VOID(pipeline);
344     auto textTheme = pipeline->GetTheme<TextTheme>();
345     CHECK_NULL_VOID(textTheme);
346     if (type == SelectFontType::SELECT) {
347         SelectModel::GetInstance()->SetFontFamily(textTheme->GetTextStyle().GetFontFamilies());
348     } else if (type == SelectFontType::OPTION) {
349         SelectModel::GetInstance()->SetOptionFontFamily(textTheme->GetTextStyle().GetFontFamilies());
350     } else if (type == SelectFontType::SELECTED_OPTION) {
351         SelectModel::GetInstance()->SetSelectedOptionFontFamily(textTheme->GetTextStyle().GetFontFamilies());
352     }
353 }
354 
ResetFontStyle(SelectFontType type)355 void JSSelect::ResetFontStyle(SelectFontType type)
356 {
357     auto pipeline = PipelineBase::GetCurrentContext();
358     CHECK_NULL_VOID(pipeline);
359     auto textTheme = pipeline->GetTheme<TextTheme>();
360     CHECK_NULL_VOID(textTheme);
361     if (type == SelectFontType::SELECT) {
362         SelectModel::GetInstance()->SetItalicFontStyle(textTheme->GetTextStyle().GetFontStyle());
363     } else if (type == SelectFontType::OPTION) {
364         SelectModel::GetInstance()->SetOptionItalicFontStyle(textTheme->GetTextStyle().GetFontStyle());
365     } else if (type == SelectFontType::SELECTED_OPTION) {
366         SelectModel::GetInstance()->SetSelectedOptionItalicFontStyle(textTheme->GetTextStyle().GetFontStyle());
367     }
368 }
369 
ResetFont(SelectFontType type)370 void JSSelect::ResetFont(SelectFontType type)
371 {
372     ResetFontSize(type);
373     ResetFontWeight(type);
374     ResetFontFamily(type);
375     ResetFontStyle(type);
376 }
377 
FontColor(const JSCallbackInfo& info)378 void JSSelect::FontColor(const JSCallbackInfo& info)
379 {
380     if (info.Length() < 1) {
381         return;
382     }
383 
384     Color textColor;
385     if (!ParseJsColor(info[0], textColor)) {
386         if (info[0]->IsNull() || info[0]->IsUndefined()) {
387             auto pipeline = PipelineBase::GetCurrentContext();
388             CHECK_NULL_VOID(pipeline);
389             auto theme = pipeline->GetTheme<SelectTheme>();
390             CHECK_NULL_VOID(theme);
391             textColor = theme->GetFontColor();
392         } else {
393             return;
394         }
395     }
396 
397     SelectModel::GetInstance()->SetFontColor(textColor);
398 }
399 
SelectedOptionBgColor(const JSCallbackInfo& info)400 void JSSelect::SelectedOptionBgColor(const JSCallbackInfo& info)
401 {
402     if (info.Length() < 1) {
403         return;
404     }
405     Color bgColor;
406     if (!ParseJsColor(info[0], bgColor)) {
407         if (info[0]->IsUndefined() || info[0]->IsNull()) {
408             auto pipeline = PipelineBase::GetCurrentContext();
409             CHECK_NULL_VOID(pipeline);
410             auto theme = pipeline->GetTheme<SelectTheme>();
411             CHECK_NULL_VOID(theme);
412             bgColor = theme->GetSelectedColor();
413         } else {
414             return;
415         }
416     }
417     SelectModel::GetInstance()->SetSelectedOptionBgColor(bgColor);
418 }
419 
SelectedOptionFont(const JSCallbackInfo& info)420 void JSSelect::SelectedOptionFont(const JSCallbackInfo& info)
421 {
422     if (info[0]->IsNull() || info[0]->IsUndefined()) {
423         ResetFont(SelectFontType::SELECTED_OPTION);
424         return;
425     }
426     if (!info[0]->IsObject()) {
427         return;
428     }
429     auto param = JSRef<JSObject>::Cast(info[0]);
430     ParseFontSize(param->GetProperty("size"), SelectFontType::SELECTED_OPTION);
431     ParseFontWeight(param->GetProperty("weight"), SelectFontType::SELECTED_OPTION);
432     ParseFontFamily(param->GetProperty("family"), SelectFontType::SELECTED_OPTION);
433     ParseFontStyle(param->GetProperty("style"), SelectFontType::SELECTED_OPTION);
434 }
435 
SelectedOptionFontColor(const JSCallbackInfo& info)436 void JSSelect::SelectedOptionFontColor(const JSCallbackInfo& info)
437 {
438     if (info.Length() < 1) {
439         return;
440     }
441     Color textColor;
442     if (!ParseJsColor(info[0], textColor)) {
443         if (info[0]->IsNull() || info[0]->IsUndefined()) {
444             auto pipeline = PipelineBase::GetCurrentContext();
445             CHECK_NULL_VOID(pipeline);
446             auto theme = pipeline->GetTheme<SelectTheme>();
447             CHECK_NULL_VOID(theme);
448             textColor = theme->GetSelectedColorText();
449         } else {
450             return;
451         }
452     }
453     SelectModel::GetInstance()->SetSelectedOptionFontColor(textColor);
454 }
455 
OptionBgColor(const JSCallbackInfo& info)456 void JSSelect::OptionBgColor(const JSCallbackInfo& info)
457 {
458     if (info.Length() < 1) {
459         return;
460     }
461     Color bgColor;
462     if (!ParseJsColor(info[0], bgColor)) {
463         if (info[0]->IsUndefined() || info[0]->IsNull()) {
464             auto pipeline = PipelineBase::GetCurrentContext();
465             CHECK_NULL_VOID(pipeline);
466             auto theme = pipeline->GetTheme<SelectTheme>();
467             CHECK_NULL_VOID(theme);
468             bgColor = theme->GetBackgroundColor();
469         } else {
470             return;
471         }
472     }
473 
474     SelectModel::GetInstance()->SetOptionBgColor(bgColor);
475 }
476 
OptionFont(const JSCallbackInfo& info)477 void JSSelect::OptionFont(const JSCallbackInfo& info)
478 {
479     if (info[0]->IsNull() || info[0]->IsUndefined()) {
480         ResetFont(SelectFontType::OPTION);
481         return;
482     }
483     if (!info[0]->IsObject()) {
484         return;
485     }
486     auto param = JSRef<JSObject>::Cast(info[0]);
487     ParseFontSize(param->GetProperty("size"), SelectFontType::OPTION);
488     ParseFontWeight(param->GetProperty("weight"), SelectFontType::OPTION);
489     ParseFontFamily(param->GetProperty("family"), SelectFontType::OPTION);
490     ParseFontStyle(param->GetProperty("style"), SelectFontType::OPTION);
491 }
492 
OptionFontColor(const JSCallbackInfo& info)493 void JSSelect::OptionFontColor(const JSCallbackInfo& info)
494 {
495     if (info.Length() < 1) {
496         return;
497     }
498     Color textColor;
499     if (!ParseJsColor(info[0], textColor)) {
500         if (info[0]->IsUndefined() || info[0]->IsNull()) {
501             auto pipeline = PipelineBase::GetCurrentContext();
502             CHECK_NULL_VOID(pipeline);
503             auto theme = pipeline->GetTheme<SelectTheme>();
504             CHECK_NULL_VOID(theme);
505             textColor = theme->GetMenuFontColor();
506         } else {
507             return;
508         }
509     }
510     TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "set option font color %{public}s", textColor.ColorToString().c_str());
511     SelectModel::GetInstance()->SetOptionFontColor(textColor);
512 }
513 
OnSelected(const JSCallbackInfo& info)514 void JSSelect::OnSelected(const JSCallbackInfo& info)
515 {
516     if (!info[0]->IsFunction()) {
517         return;
518     }
519     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
520     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
521     auto onSelect = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
522                         int32_t index, const std::string& value) {
523         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
524         ACE_SCORING_EVENT("Select.onSelect");
525         TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "fire change event %{public}d %{public}s", index, value.c_str());
526         PipelineContext::SetCallBackNode(node);
527         JSRef<JSVal> params[2];
528         params[0] = JSRef<JSVal>::Make(ToJSValue(index));
529         params[1] = JSRef<JSVal>::Make(ToJSValue(value));
530         func->ExecuteJS(2, params);
531 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
532         UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "Select.onSelect");
533 #endif
534     };
535     SelectModel::GetInstance()->SetOnSelect(std::move(onSelect));
536     info.ReturnSelf();
537 }
538 
JsSize(const JSCallbackInfo& info)539 void JSSelect::JsSize(const JSCallbackInfo& info)
540 {
541     if (!info[0]->IsObject()) {
542         JSViewAbstract::JsWidth(JSVal::Undefined());
543         JSViewAbstract::JsHeight(JSVal::Undefined());
544         return;
545     }
546     JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
547     JSViewAbstract::JsWidth(sizeObj->GetProperty("width"));
548     JSViewAbstract::JsHeight(sizeObj->GetProperty("height"));
549 }
550 
JsPadding(const JSCallbackInfo& info)551 void JSSelect::JsPadding(const JSCallbackInfo& info)
552 {
553     if (!info[0]->IsString() && !info[0]->IsNumber() && !info[0]->IsObject()) {
554         return;
555     }
556 
557     if (info[0]->IsObject()) {
558         JSRef<JSObject> paddingObj = JSRef<JSObject>::Cast(info[0]);
559         CommonCalcDimension commonCalcDimension;
560         JSViewAbstract::ParseCommonMarginOrPaddingCorner(paddingObj, commonCalcDimension);
561         if (commonCalcDimension.left.has_value() || commonCalcDimension.right.has_value() ||
562             commonCalcDimension.top.has_value() || commonCalcDimension.bottom.has_value()) {
563             ViewAbstractModel::GetInstance()->SetPaddings(commonCalcDimension.top, commonCalcDimension.bottom,
564                 commonCalcDimension.left, commonCalcDimension.right);
565             return;
566         }
567     }
568 
569     CalcDimension value;
570     if (!ParseJsDimensionVp(info[0], value)) {
571         value.Reset();
572     }
573     SelectModel::GetInstance()->SetPadding(value);
574 }
575 
SetPaddingLeft(const JSCallbackInfo& info)576 void JSSelect::SetPaddingLeft(const JSCallbackInfo& info)
577 {
578     if (info.Length() < 1) {
579         return;
580     }
581     CalcDimension value;
582     if (!ParseJsDimensionVp(info[0], value)) {
583         return;
584     }
585     SelectModel::GetInstance()->SetPaddingLeft(value);
586 }
587 
SetPaddingTop(const JSCallbackInfo& info)588 void JSSelect::SetPaddingTop(const JSCallbackInfo& info)
589 {
590     if (info.Length() < 1) {
591         return;
592     }
593     CalcDimension value;
594     if (!ParseJsDimensionVp(info[0], value)) {
595         return;
596     }
597     SelectModel::GetInstance()->SetPaddingTop(value);
598 }
599 
SetPaddingRight(const JSCallbackInfo& info)600 void JSSelect::SetPaddingRight(const JSCallbackInfo& info)
601 {
602     if (info.Length() < 1) {
603         return;
604     }
605     CalcDimension value;
606     if (!ParseJsDimensionVp(info[0], value)) {
607         return;
608     }
609     SelectModel::GetInstance()->SetPaddingRight(value);
610 }
611 
SetPaddingBottom(const JSCallbackInfo& info)612 void JSSelect::SetPaddingBottom(const JSCallbackInfo& info)
613 {
614     if (info.Length() < 1) {
615         return;
616     }
617     CalcDimension value;
618     if (!ParseJsDimensionVp(info[0], value)) {
619         return;
620     }
621     SelectModel::GetInstance()->SetPaddingBottom(value);
622 }
623 
SetSpace(const JSCallbackInfo& info)624 void JSSelect::SetSpace(const JSCallbackInfo& info)
625 {
626     if (info.Length() < 1) {
627         return;
628     }
629 
630     auto selectTheme = GetTheme<SelectTheme>();
631 
632     CalcDimension value;
633     if (!ParseJsDimensionVp(info[0], value)) {
634         value = selectTheme->GetContentSpinnerPadding();
635     }
636     if (LessNotEqual(value.Value(), 0.0) || value.Unit() == DimensionUnit::PERCENT) {
637         value = selectTheme->GetContentSpinnerPadding();
638     }
639 
640     SelectModel::GetInstance()->SetSpace(value);
641 }
642 
SetArrowPosition(const JSCallbackInfo& info)643 void JSSelect::SetArrowPosition(const JSCallbackInfo& info)
644 {
645     if (info.Length() < 1) {
646         return;
647     }
648 
649     int32_t direction = 0;
650     if (!ParseJsInt32(info[0], direction)) {
651         direction = 0;
652     }
653 
654     if (static_cast<ArrowPosition>(direction) != ArrowPosition::START &&
655         static_cast<ArrowPosition>(direction) != ArrowPosition::END) {
656         direction = 0;
657     }
658 
659     SelectModel::GetInstance()->SetArrowPosition(static_cast<ArrowPosition>(direction));
660 }
661 
SetMenuAlign(const JSCallbackInfo& info)662 void JSSelect::SetMenuAlign(const JSCallbackInfo& info)
663 {
664     if (info.Length() < 1) {
665         return;
666     }
667 
668     MenuAlign menuAlignObj;
669 
670     if (!info[0]->IsNumber()) {
671         if (!(info[0]->IsUndefined() || info[0]->IsNull())) {
672             return;
673         }
674     } else {
675         menuAlignObj.alignType = static_cast<MenuAlignType>(info[0]->ToNumber<int32_t>());
676         TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "set alignType %{public}d", menuAlignObj.alignType);
677     }
678 
679     if (info.Length() > 1) {
680         if (info[1]->IsUndefined() || info[1]->IsNull()) {
681             SelectModel::GetInstance()->SetMenuAlign(menuAlignObj);
682             return;
683         }
684         if (!info[1]->IsObject()) {
685             return;
686         }
687         auto offsetObj = JSRef<JSObject>::Cast(info[1]);
688         CalcDimension dx;
689         auto dxValue = offsetObj->GetProperty("dx");
690         ParseJsDimensionVp(dxValue, dx);
691         CalcDimension dy;
692         auto dyValue = offsetObj->GetProperty("dy");
693         ParseJsDimensionVp(dyValue, dy);
694         TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "set offset dx %{public}f dy %{public}f", dx.Value(), dy.Value());
695         menuAlignObj.offset = DimensionOffset(dx, dy);
696     }
697 
698     SelectModel::GetInstance()->SetMenuAlign(menuAlignObj);
699 }
700 
IsPercentStr(std::string& percent)701 bool JSSelect::IsPercentStr(std::string& percent)
702 {
703     if (percent.find("%") != std::string::npos) {
704         size_t index = percent.find("%");
705         percent = percent.substr(0, index);
706         return true;
707     }
708     return false;
709 }
710 
SetOptionWidth(const JSCallbackInfo& info)711 void JSSelect::SetOptionWidth(const JSCallbackInfo& info)
712 {
713     CalcDimension value;
714     if (info[0]->IsUndefined() || info[0]->IsNull()) {
715         SelectModel::GetInstance()->SetHasOptionWidth(false);
716         SelectModel::GetInstance()->SetOptionWidth(value);
717         return;
718     } else if (info[0]->IsString()) {
719         SelectModel::GetInstance()->SetHasOptionWidth(true);
720         std::string modeFlag = info[0]->ToString();
721         if (modeFlag.compare("fit_content") == 0) {
722             SelectModel::GetInstance()->SetOptionWidthFitTrigger(false);
723         } else if (modeFlag.compare("fit_trigger") == 0) {
724             SelectModel::GetInstance()->SetOptionWidthFitTrigger(true);
725         } else if (IsPercentStr(modeFlag)) {
726             return;
727         } else {
728             ParseJsDimensionVpNG(info[0], value);
729             if (value.IsNegative()) {
730                 value.Reset();
731             }
732             SelectModel::GetInstance()->SetOptionWidth(value);
733         }
734     } else {
735         SelectModel::GetInstance()->SetHasOptionWidth(true);
736         ParseJsDimensionVpNG(info[0], value);
737         if (value.IsNegative()) {
738             value.Reset();
739         }
740         SelectModel::GetInstance()->SetOptionWidth(value);
741     }
742 }
743 
SetOptionHeight(const JSCallbackInfo& info)744 void JSSelect::SetOptionHeight(const JSCallbackInfo& info)
745 {
746     CalcDimension value;
747     if (info[0]->IsUndefined() || info[0]->IsNull()) {
748         return;
749     } else if (info[0]->IsString()) {
750         std::string modeFlag = info[0]->ToString();
751         if (IsPercentStr(modeFlag)) {
752             return;
753         } else {
754             ParseJsDimensionVpNG(info[0], value);
755             if (value.IsNonPositive()) {
756                 return;
757             }
758             SelectModel::GetInstance()->SetOptionHeight(value);
759         }
760     } else {
761         ParseJsDimensionVpNG(info[0], value);
762         if (value.IsNonPositive()) {
763             return;
764         }
765         SelectModel::GetInstance()->SetOptionHeight(value);
766     }
767 }
768 
SetOptionWidthFitTrigger(const JSCallbackInfo& info)769 void JSSelect::SetOptionWidthFitTrigger(const JSCallbackInfo& info)
770 {
771     bool isFitTrigger = false;
772     if (info[0]->IsBoolean()) {
773         isFitTrigger = info[0]->ToBoolean();
774     }
775 
776     SelectModel::GetInstance()->SetOptionWidthFitTrigger(isFitTrigger);
777 }
778 
SetMenuBackgroundColor(const JSCallbackInfo& info)779 void JSSelect::SetMenuBackgroundColor(const JSCallbackInfo& info)
780 {
781     if (info.Length() < 1) {
782         return;
783     }
784     Color menuBackgroundColor;
785     if (!ParseJsColor(info[0], menuBackgroundColor)) {
786         if (info[0]->IsNull() || info[0]->IsUndefined()) {
787             menuBackgroundColor = Color::TRANSPARENT;
788         } else {
789             return;
790         }
791     }
792     TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "set menu background color %{public}s",
793         menuBackgroundColor.ColorToString().c_str());
794     SelectModel::GetInstance()->SetMenuBackgroundColor(menuBackgroundColor);
795 }
796 
SetMenuBackgroundBlurStyle(const JSCallbackInfo& info)797 void JSSelect::SetMenuBackgroundBlurStyle(const JSCallbackInfo& info)
798 {
799     if (info.Length() < 1) {
800         return;
801     }
802 
803     BlurStyleOption styleOption;
804     if (info[0]->IsNumber()) {
805         auto blurStyle = info[0]->ToNumber<int32_t>();
806         if (blurStyle >= static_cast<int>(BlurStyle::NO_MATERIAL) &&
807             blurStyle <= static_cast<int>(BlurStyle::COMPONENT_ULTRA_THICK)) {
808             styleOption.blurStyle = static_cast<BlurStyle>(blurStyle);
809             TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "set menu blurStyle %{public}d", blurStyle);
810             SelectModel::GetInstance()->SetMenuBackgroundBlurStyle(styleOption);
811         }
812     }
813 }
814 
SetControlSize(const JSCallbackInfo& info)815 void JSSelect::SetControlSize(const JSCallbackInfo& info)
816 {
817     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
818         return;
819     }
820     if (info.Length() < 1) {
821         return;
822     }
823     if (info[0]->IsNumber()) {
824         auto controlSize = static_cast<ControlSize>(info[0]->ToNumber<int32_t>());
825         SelectModel::GetInstance()->SetControlSize(controlSize);
826     } else {
827         LOGE("JSSelect::SetControlSize Is not Number.");
828     }
829 }
830 
SetDivider(const JSCallbackInfo& info)831 void JSSelect::SetDivider(const JSCallbackInfo& info)
832 {
833     NG::SelectDivider divider;
834     auto selectTheme = GetTheme<SelectTheme>();
835     Dimension defaultStrokeWidth = 0.0_vp;
836     Dimension defaultMargin = -1.0_vp;
837     Color defaultColor = Color::TRANSPARENT;
838     // Set default strokeWidth and color
839     if (selectTheme) {
840         defaultStrokeWidth = selectTheme->GetDefaultDividerWidth();
841         defaultColor = selectTheme->GetLineColor();
842         divider.strokeWidth = defaultStrokeWidth;
843         divider.color = defaultColor;
844         divider.startMargin = defaultMargin;
845         divider.endMargin = defaultMargin;
846     }
847 
848     if (info.Length() >= 1 && info[0]->IsObject()) {
849         JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
850 
851         Dimension strokeWidth = defaultStrokeWidth;
852         if (ConvertFromJSValueNG(obj->GetProperty("strokeWidth"), strokeWidth) && CheckDividerValue(strokeWidth)) {
853             divider.strokeWidth = strokeWidth;
854         }
855 
856         Color color = defaultColor;
857         if (ConvertFromJSValue(obj->GetProperty("color"), color)) {
858             divider.color = color;
859         }
860 
861         Dimension startMargin = defaultMargin;
862         if (ConvertFromJSValueNG(obj->GetProperty("startMargin"), startMargin) && CheckDividerValue(startMargin)) {
863             divider.startMargin = startMargin;
864         }
865 
866         Dimension endMargin = defaultMargin;
867         if (ConvertFromJSValueNG(obj->GetProperty("endMargin"), endMargin) &&  CheckDividerValue(endMargin)) {
868             divider.endMargin = endMargin;
869         }
870     } else if (info.Length() >= 1 && info[0]->IsNull()) {
871         divider.strokeWidth = 0.0_vp;
872     }
873     SelectModel::GetInstance()->SetDivider(divider);
874 }
875 
CheckDividerValue(const Dimension &dimension)876 bool JSSelect::CheckDividerValue(const Dimension &dimension)
877 {
878     if (dimension.Value() >= 0.0f && dimension.Unit() != DimensionUnit::PERCENT) {
879         return true;
880     }
881     return false;
882 }
883 
SetDirection(const std::string& dir)884 void JSSelect::SetDirection(const std::string& dir)
885 {
886     TextDirection direction = TextDirection::AUTO;
887     if (dir == "Ltr") {
888         direction = TextDirection::LTR;
889     } else if (dir == "Rtl") {
890         direction = TextDirection::RTL;
891     } else if (dir == "Auto") {
892         direction = TextDirection::AUTO;
893     } else if (dir == "undefined" && Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
894         direction = TextDirection::AUTO;
895     }
896     SelectModel::GetInstance()->SetLayoutDirection(direction);
897 }
898 } // namespace OHOS::Ace::Framework
899