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 
ResetFont(void)227 void ResetFont(void)
228 {
229     auto pipeline = PipelineBase::GetCurrentContext();
230     CHECK_NULL_VOID(pipeline);
231     auto selectTheme = pipeline->GetTheme<SelectTheme>();
232     CHECK_NULL_VOID(selectTheme);
233     auto textTheme = pipeline->GetTheme<TextTheme>();
234     CHECK_NULL_VOID(textTheme);
235     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
236         SelectModel::GetInstance()->SetFontSize(selectTheme->GetFontSize());
237     } else {
238         auto controlSize = SelectModel::GetInstance()->GetControlSize();
239         SelectModel::GetInstance()->SetFontSize(selectTheme->GetFontSize(controlSize));
240     }
241     SelectModel::GetInstance()->SetFontWeight(FontWeight::MEDIUM);
242     SelectModel::GetInstance()->SetFontFamily(textTheme->GetTextStyle().GetFontFamilies());
243     SelectModel::GetInstance()->SetItalicFontStyle(textTheme->GetTextStyle().GetFontStyle());
244     return;
245 }
246 
Font(const JSCallbackInfo & info)247 void JSSelect::Font(const JSCallbackInfo& info)
248 {
249     if (info[0]->IsNull() || info[0]->IsUndefined()) {
250         ResetFont(SelectFontType::SELECT);
251         return;
252     }
253     if (!info[0]->IsObject()) {
254         return;
255     }
256     auto param = JSRef<JSObject>::Cast(info[0]);
257     ParseFontSize(param->GetProperty("size"), SelectFontType::SELECT);
258     ParseFontWeight(param->GetProperty("weight"), SelectFontType::SELECT);
259     ParseFontFamily(param->GetProperty("family"), SelectFontType::SELECT);
260     ParseFontStyle(param->GetProperty("style"), SelectFontType::SELECT);
261 }
262 
ParseFontSize(const JSRef<JSVal> & jsValue,SelectFontType type)263 void JSSelect::ParseFontSize(const JSRef<JSVal>& jsValue, SelectFontType type)
264 {
265     CalcDimension fontSize;
266     if (!ParseJsDimensionFp(jsValue, fontSize)) {
267         ResetFontSize(type);
268         return;
269     }
270     if (type == SelectFontType::SELECT) {
271         SelectModel::GetInstance()->SetFontSize(fontSize);
272     } else if (type == SelectFontType::OPTION) {
273         SelectModel::GetInstance()->SetOptionFontSize(fontSize);
274     } else if (type == SelectFontType::SELECTED_OPTION) {
275         SelectModel::GetInstance()->SetSelectedOptionFontSize(fontSize);
276     }
277 }
278 
ParseFontWeight(const JSRef<JSVal> & jsValue,SelectFontType type)279 void JSSelect::ParseFontWeight(const JSRef<JSVal>& jsValue, SelectFontType type)
280 {
281     std::string weight;
282     if (jsValue->IsNumber()) {
283         weight = std::to_string(jsValue->ToNumber<int32_t>());
284     } else {
285         ParseJsString(jsValue, weight);
286     }
287     if (type == SelectFontType::SELECT) {
288         SelectModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(weight, FontWeight::MEDIUM));
289     } else if (type == SelectFontType::OPTION) {
290         SelectModel::GetInstance()->SetOptionFontWeight(ConvertStrToFontWeight(weight, FontWeight::REGULAR));
291     } else if (type == SelectFontType::SELECTED_OPTION) {
292         SelectModel::GetInstance()->SetSelectedOptionFontWeight(ConvertStrToFontWeight(weight, FontWeight::REGULAR));
293     }
294 }
295 
ParseFontFamily(const JSRef<JSVal> & jsValue,SelectFontType type)296 void JSSelect::ParseFontFamily(const JSRef<JSVal>& jsValue, SelectFontType type)
297 {
298     if (!jsValue->IsString()) {
299         ResetFontFamily(type);
300         return;
301     }
302     auto family = ConvertStrToFontFamilies(jsValue->ToString());
303     if (type == SelectFontType::SELECT) {
304         SelectModel::GetInstance()->SetFontFamily(family);
305     } else if (type == SelectFontType::OPTION) {
306         SelectModel::GetInstance()->SetOptionFontFamily(family);
307     } else if (type == SelectFontType::SELECTED_OPTION) {
308         SelectModel::GetInstance()->SetSelectedOptionFontFamily(family);
309     }
310 }
311 
ParseFontStyle(const JSRef<JSVal> & jsValue,SelectFontType type)312 void JSSelect::ParseFontStyle(const JSRef<JSVal>& jsValue, SelectFontType type)
313 {
314     if (!jsValue->IsNumber()) {
315         ResetFontStyle(type);
316         return;
317     }
318     auto styleVal = static_cast<FontStyle>(jsValue->ToNumber<int32_t>());
319     if (type == SelectFontType::SELECT) {
320         SelectModel::GetInstance()->SetItalicFontStyle(styleVal);
321     } else if (type == SelectFontType::OPTION) {
322         SelectModel::GetInstance()->SetOptionItalicFontStyle(styleVal);
323     } else if (type == SelectFontType::SELECTED_OPTION) {
324         SelectModel::GetInstance()->SetSelectedOptionItalicFontStyle(styleVal);
325     }
326 }
327 
ResetFontSize(SelectFontType type)328 void JSSelect::ResetFontSize(SelectFontType type)
329 {
330     auto pipeline = PipelineBase::GetCurrentContext();
331     CHECK_NULL_VOID(pipeline);
332     auto selectTheme = pipeline->GetTheme<SelectTheme>();
333     CHECK_NULL_VOID(selectTheme);
334     if (type == SelectFontType::OPTION) {
335         SelectModel::GetInstance()->SetOptionFontSize(selectTheme->GetMenuFontSize());
336         return;
337     } else if (type == SelectFontType::SELECTED_OPTION) {
338         SelectModel::GetInstance()->SetSelectedOptionFontSize(selectTheme->GetMenuFontSize());
339         return;
340     }
341     SelectModel::GetInstance()->SetFontSize(selectTheme->GetFontSize());
342 }
343 
ResetFontWeight(SelectFontType type)344 void JSSelect::ResetFontWeight(SelectFontType type)
345 {
346     if (type == SelectFontType::SELECT) {
347         SelectModel::GetInstance()->SetFontWeight(FontWeight::MEDIUM);
348     } else if (type == SelectFontType::OPTION) {
349         SelectModel::GetInstance()->SetOptionFontWeight(FontWeight::REGULAR);
350     } else if (type == SelectFontType::SELECTED_OPTION) {
351         SelectModel::GetInstance()->SetSelectedOptionFontWeight(FontWeight::REGULAR);
352     }
353 }
354 
ResetFontFamily(SelectFontType type)355 void JSSelect::ResetFontFamily(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()->SetFontFamily(textTheme->GetTextStyle().GetFontFamilies());
363     } else if (type == SelectFontType::OPTION) {
364         SelectModel::GetInstance()->SetOptionFontFamily(textTheme->GetTextStyle().GetFontFamilies());
365     } else if (type == SelectFontType::SELECTED_OPTION) {
366         SelectModel::GetInstance()->SetSelectedOptionFontFamily(textTheme->GetTextStyle().GetFontFamilies());
367     }
368 }
369 
ResetFontStyle(SelectFontType type)370 void JSSelect::ResetFontStyle(SelectFontType type)
371 {
372     auto pipeline = PipelineBase::GetCurrentContext();
373     CHECK_NULL_VOID(pipeline);
374     auto textTheme = pipeline->GetTheme<TextTheme>();
375     CHECK_NULL_VOID(textTheme);
376     if (type == SelectFontType::SELECT) {
377         SelectModel::GetInstance()->SetItalicFontStyle(textTheme->GetTextStyle().GetFontStyle());
378     } else if (type == SelectFontType::OPTION) {
379         SelectModel::GetInstance()->SetOptionItalicFontStyle(textTheme->GetTextStyle().GetFontStyle());
380     } else if (type == SelectFontType::SELECTED_OPTION) {
381         SelectModel::GetInstance()->SetSelectedOptionItalicFontStyle(textTheme->GetTextStyle().GetFontStyle());
382     }
383 }
384 
ResetFont(SelectFontType type)385 void JSSelect::ResetFont(SelectFontType type)
386 {
387     ResetFontSize(type);
388     ResetFontWeight(type);
389     ResetFontFamily(type);
390     ResetFontStyle(type);
391 }
392 
FontColor(const JSCallbackInfo & info)393 void JSSelect::FontColor(const JSCallbackInfo& info)
394 {
395     if (info.Length() < 1) {
396         return;
397     }
398 
399     Color textColor;
400     if (!ParseJsColor(info[0], textColor)) {
401         if (info[0]->IsNull() || info[0]->IsUndefined()) {
402             auto pipeline = PipelineBase::GetCurrentContext();
403             CHECK_NULL_VOID(pipeline);
404             auto theme = pipeline->GetTheme<SelectTheme>();
405             CHECK_NULL_VOID(theme);
406             textColor = theme->GetFontColor();
407         } else {
408             return;
409         }
410     }
411 
412     SelectModel::GetInstance()->SetFontColor(textColor);
413 }
414 
SelectedOptionBgColor(const JSCallbackInfo & info)415 void JSSelect::SelectedOptionBgColor(const JSCallbackInfo& info)
416 {
417     if (info.Length() < 1) {
418         return;
419     }
420     Color bgColor;
421     if (!ParseJsColor(info[0], bgColor)) {
422         if (info[0]->IsUndefined() || info[0]->IsNull()) {
423             auto pipeline = PipelineBase::GetCurrentContext();
424             CHECK_NULL_VOID(pipeline);
425             auto theme = pipeline->GetTheme<SelectTheme>();
426             CHECK_NULL_VOID(theme);
427             bgColor = theme->GetSelectedColor();
428         } else {
429             return;
430         }
431     }
432     SelectModel::GetInstance()->SetSelectedOptionBgColor(bgColor);
433 }
434 
SelectedOptionFont(const JSCallbackInfo & info)435 void JSSelect::SelectedOptionFont(const JSCallbackInfo& info)
436 {
437     if (info[0]->IsNull() || info[0]->IsUndefined()) {
438         ResetFont(SelectFontType::SELECTED_OPTION);
439         return;
440     }
441     if (!info[0]->IsObject()) {
442         return;
443     }
444     auto param = JSRef<JSObject>::Cast(info[0]);
445     ParseFontSize(param->GetProperty("size"), SelectFontType::SELECTED_OPTION);
446     ParseFontWeight(param->GetProperty("weight"), SelectFontType::SELECTED_OPTION);
447     ParseFontFamily(param->GetProperty("family"), SelectFontType::SELECTED_OPTION);
448     ParseFontStyle(param->GetProperty("style"), SelectFontType::SELECTED_OPTION);
449 }
450 
SelectedOptionFontColor(const JSCallbackInfo & info)451 void JSSelect::SelectedOptionFontColor(const JSCallbackInfo& info)
452 {
453     if (info.Length() < 1) {
454         return;
455     }
456     Color textColor;
457     if (!ParseJsColor(info[0], textColor)) {
458         if (info[0]->IsNull() || info[0]->IsUndefined()) {
459             auto pipeline = PipelineBase::GetCurrentContext();
460             CHECK_NULL_VOID(pipeline);
461             auto theme = pipeline->GetTheme<SelectTheme>();
462             CHECK_NULL_VOID(theme);
463             textColor = theme->GetSelectedColorText();
464         } else {
465             return;
466         }
467     }
468     SelectModel::GetInstance()->SetSelectedOptionFontColor(textColor);
469 }
470 
OptionBgColor(const JSCallbackInfo & info)471 void JSSelect::OptionBgColor(const JSCallbackInfo& info)
472 {
473     if (info.Length() < 1) {
474         return;
475     }
476     Color bgColor;
477     if (!ParseJsColor(info[0], bgColor)) {
478         if (info[0]->IsUndefined() || info[0]->IsNull()) {
479             auto pipeline = PipelineBase::GetCurrentContext();
480             CHECK_NULL_VOID(pipeline);
481             auto theme = pipeline->GetTheme<SelectTheme>();
482             CHECK_NULL_VOID(theme);
483             bgColor = theme->GetBackgroundColor();
484         } else {
485             return;
486         }
487     }
488 
489     SelectModel::GetInstance()->SetOptionBgColor(bgColor);
490 }
491 
OptionFont(const JSCallbackInfo & info)492 void JSSelect::OptionFont(const JSCallbackInfo& info)
493 {
494     if (info[0]->IsNull() || info[0]->IsUndefined()) {
495         ResetFont(SelectFontType::OPTION);
496         return;
497     }
498     if (!info[0]->IsObject()) {
499         return;
500     }
501     auto param = JSRef<JSObject>::Cast(info[0]);
502     ParseFontSize(param->GetProperty("size"), SelectFontType::OPTION);
503     ParseFontWeight(param->GetProperty("weight"), SelectFontType::OPTION);
504     ParseFontFamily(param->GetProperty("family"), SelectFontType::OPTION);
505     ParseFontStyle(param->GetProperty("style"), SelectFontType::OPTION);
506 }
507 
OptionFontColor(const JSCallbackInfo & info)508 void JSSelect::OptionFontColor(const JSCallbackInfo& info)
509 {
510     if (info.Length() < 1) {
511         return;
512     }
513     Color textColor;
514     if (!ParseJsColor(info[0], textColor)) {
515         if (info[0]->IsUndefined() || info[0]->IsNull()) {
516             auto pipeline = PipelineBase::GetCurrentContext();
517             CHECK_NULL_VOID(pipeline);
518             auto theme = pipeline->GetTheme<SelectTheme>();
519             CHECK_NULL_VOID(theme);
520             textColor = theme->GetMenuFontColor();
521         } else {
522             return;
523         }
524     }
525     TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "set option font color %{public}s", textColor.ColorToString().c_str());
526     SelectModel::GetInstance()->SetOptionFontColor(textColor);
527 }
528 
OnSelected(const JSCallbackInfo & info)529 void JSSelect::OnSelected(const JSCallbackInfo& info)
530 {
531     if (!info[0]->IsFunction()) {
532         return;
533     }
534     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
535     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
536     auto onSelect = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
537                         int32_t index, const std::string& value) {
538         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
539         ACE_SCORING_EVENT("Select.onSelect");
540         TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "fire change event %{public}d %{public}s", index, value.c_str());
541         PipelineContext::SetCallBackNode(node);
542         JSRef<JSVal> params[2];
543         params[0] = JSRef<JSVal>::Make(ToJSValue(index));
544         params[1] = JSRef<JSVal>::Make(ToJSValue(value));
545         func->ExecuteJS(2, params);
546 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
547         UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "Select.onSelect");
548 #endif
549     };
550     SelectModel::GetInstance()->SetOnSelect(std::move(onSelect));
551     info.ReturnSelf();
552 }
553 
JsSize(const JSCallbackInfo & info)554 void JSSelect::JsSize(const JSCallbackInfo& info)
555 {
556     if (!info[0]->IsObject()) {
557         JSViewAbstract::JsWidth(JSVal::Undefined());
558         JSViewAbstract::JsHeight(JSVal::Undefined());
559         return;
560     }
561     JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
562     JSViewAbstract::JsWidth(sizeObj->GetProperty("width"));
563     JSViewAbstract::JsHeight(sizeObj->GetProperty("height"));
564 }
565 
JsPadding(const JSCallbackInfo & info)566 void JSSelect::JsPadding(const JSCallbackInfo& info)
567 {
568     if (!info[0]->IsString() && !info[0]->IsNumber() && !info[0]->IsObject()) {
569         return;
570     }
571 
572     if (info[0]->IsObject()) {
573         std::optional<CalcDimension> left;
574         std::optional<CalcDimension> right;
575         std::optional<CalcDimension> top;
576         std::optional<CalcDimension> bottom;
577         JSRef<JSObject> paddingObj = JSRef<JSObject>::Cast(info[0]);
578 
579         CalcDimension leftDimen;
580         if (ParseJsDimensionVp(paddingObj->GetProperty("left"), leftDimen)) {
581             left = leftDimen;
582         }
583         CalcDimension rightDimen;
584         if (ParseJsDimensionVp(paddingObj->GetProperty("right"), rightDimen)) {
585             right = rightDimen;
586         }
587         CalcDimension topDimen;
588         if (ParseJsDimensionVp(paddingObj->GetProperty("top"), topDimen)) {
589             top = topDimen;
590         }
591         CalcDimension bottomDimen;
592         if (ParseJsDimensionVp(paddingObj->GetProperty("bottom"), bottomDimen)) {
593             bottom = bottomDimen;
594         }
595         if (left.has_value() || right.has_value() || top.has_value() || bottom.has_value()) {
596             ViewAbstractModel::GetInstance()->SetPaddings(top, bottom, left, right);
597             return;
598         }
599     }
600 
601     CalcDimension value;
602     if (!ParseJsDimensionVp(info[0], value)) {
603         value.Reset();
604     }
605     SelectModel::GetInstance()->SetPadding(value);
606 }
607 
SetPaddingLeft(const JSCallbackInfo & info)608 void JSSelect::SetPaddingLeft(const JSCallbackInfo& info)
609 {
610     if (info.Length() < 1) {
611         return;
612     }
613     CalcDimension value;
614     if (!ParseJsDimensionVp(info[0], value)) {
615         return;
616     }
617     SelectModel::GetInstance()->SetPaddingLeft(value);
618 }
619 
SetPaddingTop(const JSCallbackInfo & info)620 void JSSelect::SetPaddingTop(const JSCallbackInfo& info)
621 {
622     if (info.Length() < 1) {
623         return;
624     }
625     CalcDimension value;
626     if (!ParseJsDimensionVp(info[0], value)) {
627         return;
628     }
629     SelectModel::GetInstance()->SetPaddingTop(value);
630 }
631 
SetPaddingRight(const JSCallbackInfo & info)632 void JSSelect::SetPaddingRight(const JSCallbackInfo& info)
633 {
634     if (info.Length() < 1) {
635         return;
636     }
637     CalcDimension value;
638     if (!ParseJsDimensionVp(info[0], value)) {
639         return;
640     }
641     SelectModel::GetInstance()->SetPaddingRight(value);
642 }
643 
SetPaddingBottom(const JSCallbackInfo & info)644 void JSSelect::SetPaddingBottom(const JSCallbackInfo& info)
645 {
646     if (info.Length() < 1) {
647         return;
648     }
649     CalcDimension value;
650     if (!ParseJsDimensionVp(info[0], value)) {
651         return;
652     }
653     SelectModel::GetInstance()->SetPaddingBottom(value);
654 }
655 
SetSpace(const JSCallbackInfo & info)656 void JSSelect::SetSpace(const JSCallbackInfo& info)
657 {
658     if (info.Length() < 1) {
659         return;
660     }
661 
662     auto selectTheme = GetTheme<SelectTheme>();
663 
664     CalcDimension value;
665     if (!ParseJsDimensionVp(info[0], value)) {
666         value = selectTheme->GetContentSpinnerPadding();
667     }
668     if (LessNotEqual(value.Value(), 0.0) || value.Unit() == DimensionUnit::PERCENT) {
669         value = selectTheme->GetContentSpinnerPadding();
670     }
671 
672     SelectModel::GetInstance()->SetSpace(value);
673 }
674 
SetArrowPosition(const JSCallbackInfo & info)675 void JSSelect::SetArrowPosition(const JSCallbackInfo& info)
676 {
677     if (info.Length() < 1) {
678         return;
679     }
680 
681     int32_t direction = 0;
682     if (!ParseJsInt32(info[0], direction)) {
683         direction = 0;
684     }
685 
686     if (static_cast<ArrowPosition>(direction) != ArrowPosition::START &&
687         static_cast<ArrowPosition>(direction) != ArrowPosition::END) {
688         direction = 0;
689     }
690 
691     SelectModel::GetInstance()->SetArrowPosition(static_cast<ArrowPosition>(direction));
692 }
693 
SetMenuAlign(const JSCallbackInfo & info)694 void JSSelect::SetMenuAlign(const JSCallbackInfo& info)
695 {
696     if (info.Length() < 1) {
697         return;
698     }
699 
700     MenuAlign menuAlignObj;
701 
702     if (!info[0]->IsNumber()) {
703         if (!(info[0]->IsUndefined() || info[0]->IsNull())) {
704             return;
705         }
706     } else {
707         menuAlignObj.alignType = static_cast<MenuAlignType>(info[0]->ToNumber<int32_t>());
708         TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "set alignType %{public}d", menuAlignObj.alignType);
709     }
710 
711     if (info.Length() > 1) {
712         if (info[1]->IsUndefined() || info[1]->IsNull()) {
713             SelectModel::GetInstance()->SetMenuAlign(menuAlignObj);
714             return;
715         }
716         if (!info[1]->IsObject()) {
717             return;
718         }
719         auto offsetObj = JSRef<JSObject>::Cast(info[1]);
720         CalcDimension dx;
721         auto dxValue = offsetObj->GetProperty("dx");
722         ParseJsDimensionVp(dxValue, dx);
723         CalcDimension dy;
724         auto dyValue = offsetObj->GetProperty("dy");
725         ParseJsDimensionVp(dyValue, dy);
726         TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "set offset dx %{public}f dy %{public}f", dx.Value(), dy.Value());
727         menuAlignObj.offset = DimensionOffset(dx, dy);
728     }
729 
730     SelectModel::GetInstance()->SetMenuAlign(menuAlignObj);
731 }
732 
IsPercentStr(std::string & percent)733 bool JSSelect::IsPercentStr(std::string& percent)
734 {
735     if (percent.find("%") != std::string::npos) {
736         size_t index = percent.find("%");
737         percent = percent.substr(0, index);
738         return true;
739     }
740     return false;
741 }
742 
SetOptionWidth(const JSCallbackInfo & info)743 void JSSelect::SetOptionWidth(const JSCallbackInfo& info)
744 {
745     CalcDimension value;
746     if (info[0]->IsUndefined() || info[0]->IsNull()) {
747         SelectModel::GetInstance()->SetHasOptionWidth(false);
748         SelectModel::GetInstance()->SetOptionWidth(value);
749         return;
750     } else if (info[0]->IsString()) {
751         SelectModel::GetInstance()->SetHasOptionWidth(true);
752         std::string modeFlag = info[0]->ToString();
753         if (modeFlag.compare("fit_content") == 0) {
754             SelectModel::GetInstance()->SetOptionWidthFitTrigger(false);
755         } else if (modeFlag.compare("fit_trigger") == 0) {
756             SelectModel::GetInstance()->SetOptionWidthFitTrigger(true);
757         } else if (IsPercentStr(modeFlag)) {
758             return;
759         } else {
760             ParseJsDimensionVpNG(info[0], value);
761             if (value.IsNegative()) {
762                 value.Reset();
763             }
764             SelectModel::GetInstance()->SetOptionWidth(value);
765         }
766     } else {
767         SelectModel::GetInstance()->SetHasOptionWidth(true);
768         ParseJsDimensionVpNG(info[0], value);
769         if (value.IsNegative()) {
770             value.Reset();
771         }
772         SelectModel::GetInstance()->SetOptionWidth(value);
773     }
774 }
775 
SetOptionHeight(const JSCallbackInfo & info)776 void JSSelect::SetOptionHeight(const JSCallbackInfo& info)
777 {
778     CalcDimension value;
779     if (info[0]->IsUndefined() || info[0]->IsNull()) {
780         return;
781     } else if (info[0]->IsString()) {
782         std::string modeFlag = info[0]->ToString();
783         if (IsPercentStr(modeFlag)) {
784             return;
785         } else {
786             ParseJsDimensionVpNG(info[0], value);
787             if (value.IsNonPositive()) {
788                 return;
789             }
790             SelectModel::GetInstance()->SetOptionHeight(value);
791         }
792     } else {
793         ParseJsDimensionVpNG(info[0], value);
794         if (value.IsNonPositive()) {
795             return;
796         }
797         SelectModel::GetInstance()->SetOptionHeight(value);
798     }
799 }
800 
SetOptionWidthFitTrigger(const JSCallbackInfo & info)801 void JSSelect::SetOptionWidthFitTrigger(const JSCallbackInfo& info)
802 {
803     bool isFitTrigger = false;
804     if (info[0]->IsBoolean()) {
805         isFitTrigger = info[0]->ToBoolean();
806     }
807 
808     SelectModel::GetInstance()->SetOptionWidthFitTrigger(isFitTrigger);
809 }
810 
SetMenuBackgroundColor(const JSCallbackInfo & info)811 void JSSelect::SetMenuBackgroundColor(const JSCallbackInfo& info)
812 {
813     if (info.Length() < 1) {
814         return;
815     }
816     Color menuBackgroundColor;
817     if (!ParseJsColor(info[0], menuBackgroundColor)) {
818         if (info[0]->IsNull() || info[0]->IsUndefined()) {
819             menuBackgroundColor = Color::TRANSPARENT;
820         } else {
821             return;
822         }
823     }
824     TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "set menu background color %{public}s",
825         menuBackgroundColor.ColorToString().c_str());
826     SelectModel::GetInstance()->SetMenuBackgroundColor(menuBackgroundColor);
827 }
828 
SetMenuBackgroundBlurStyle(const JSCallbackInfo & info)829 void JSSelect::SetMenuBackgroundBlurStyle(const JSCallbackInfo& info)
830 {
831     if (info.Length() < 1) {
832         return;
833     }
834 
835     BlurStyleOption styleOption;
836     if (info[0]->IsNumber()) {
837         auto blurStyle = info[0]->ToNumber<int32_t>();
838         if (blurStyle >= static_cast<int>(BlurStyle::NO_MATERIAL) &&
839             blurStyle <= static_cast<int>(BlurStyle::COMPONENT_ULTRA_THICK)) {
840             styleOption.blurStyle = static_cast<BlurStyle>(blurStyle);
841             TAG_LOGD(AceLogTag::ACE_SELECT_COMPONENT, "set menu blurStyle %{public}d", blurStyle);
842             SelectModel::GetInstance()->SetMenuBackgroundBlurStyle(styleOption);
843         }
844     }
845 }
846 
SetControlSize(const JSCallbackInfo & info)847 void JSSelect::SetControlSize(const JSCallbackInfo& info)
848 {
849     if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
850         return;
851     }
852     if (info.Length() < 1) {
853         return;
854     }
855     if (info[0]->IsNumber()) {
856         auto controlSize = static_cast<ControlSize>(info[0]->ToNumber<int32_t>());
857         SelectModel::GetInstance()->SetControlSize(controlSize);
858     } else {
859         LOGE("JSSelect::SetControlSize Is not Number.");
860     }
861 }
862 
SetDivider(const JSCallbackInfo & info)863 void JSSelect::SetDivider(const JSCallbackInfo& info)
864 {
865     NG::SelectDivider divider;
866     auto selectTheme = GetTheme<SelectTheme>();
867     Dimension defaultStrokeWidth = 0.0_vp;
868     Dimension defaultMargin = -1.0_vp;
869     Color defaultColor = Color::TRANSPARENT;
870     // Set default strokeWidth and color
871     if (selectTheme) {
872         defaultStrokeWidth = selectTheme->GetDefaultDividerWidth();
873         defaultColor = selectTheme->GetLineColor();
874         divider.strokeWidth = defaultStrokeWidth;
875         divider.color = defaultColor;
876         divider.startMargin = defaultMargin;
877         divider.endMargin = defaultMargin;
878     }
879 
880     if (info.Length() >= 1 && info[0]->IsObject()) {
881         JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
882 
883         Dimension strokeWidth = defaultStrokeWidth;
884         if (ConvertFromJSValueNG(obj->GetProperty("strokeWidth"), strokeWidth) && CheckDividerValue(strokeWidth)) {
885             divider.strokeWidth = strokeWidth;
886         }
887 
888         Color color = defaultColor;
889         if (ConvertFromJSValue(obj->GetProperty("color"), color)) {
890             divider.color = color;
891         }
892 
893         Dimension startMargin = defaultMargin;
894         if (ConvertFromJSValueNG(obj->GetProperty("startMargin"), startMargin) && CheckDividerValue(startMargin)) {
895             divider.startMargin = startMargin;
896         }
897 
898         Dimension endMargin = defaultMargin;
899         if (ConvertFromJSValueNG(obj->GetProperty("endMargin"), endMargin) &&  CheckDividerValue(endMargin)) {
900             divider.endMargin = endMargin;
901         }
902     } else if (info.Length() >= 1 && info[0]->IsNull()) {
903         divider.strokeWidth = 0.0_vp;
904     }
905     SelectModel::GetInstance()->SetDivider(divider);
906 }
907 
CheckDividerValue(const Dimension & dimension)908 bool JSSelect::CheckDividerValue(const Dimension &dimension)
909 {
910     if (dimension.Value() >= 0.0f && dimension.Unit() != DimensionUnit::PERCENT) {
911         return true;
912     }
913     return false;
914 }
915 
SetDirection(const std::string & dir)916 void JSSelect::SetDirection(const std::string& dir)
917 {
918     TextDirection direction = TextDirection::AUTO;
919     if (dir == "Ltr") {
920         direction = TextDirection::LTR;
921     } else if (dir == "Rtl") {
922         direction = TextDirection::RTL;
923     } else if (dir == "Auto") {
924         direction = TextDirection::AUTO;
925     } else if (dir == "undefined" && Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
926         direction = TextDirection::AUTO;
927     }
928     SelectModel::GetInstance()->SetLayoutDirection(direction);
929 }
930 } // namespace OHOS::Ace::Framework
931