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 "frameworks/bridge/declarative_frontend/jsview/js_button.h"
17 #include <limits>
18 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
19 #include "interfaces/inner_api/ui_session/ui_session_manager.h"
20 #endif
21 
22 #include "base/geometry/dimension.h"
23 #include "base/log/ace_scoring_log.h"
24 #include "base/log/ace_trace.h"
25 #include "base/utils/utils.h"
26 #include "core/components/button/button_component.h"
27 #include "core/components/button/button_theme.h"
28 #include "core/components_ng/base/view_stack_processor.h"
29 #include "core/components_ng/pattern/button/button_model_ng.h"
30 #include "frameworks/bridge/declarative_frontend/ark_theme/theme_apply/js_button_theme.h"
31 #include "frameworks/bridge/declarative_frontend/engine/functions/js_click_function.h"
32 #include "frameworks/bridge/declarative_frontend/engine/jsi/js_ui_index.h"
33 #include "frameworks/bridge/declarative_frontend/jsview/js_utils.h"
34 #include "frameworks/bridge/declarative_frontend/jsview/models/button_model_impl.h"
35 #include "frameworks/bridge/declarative_frontend/view_stack_processor.h"
36 
37 namespace OHOS::Ace {
38 std::unique_ptr<ButtonModel> ButtonModel::instance_ = nullptr;
39 std::mutex ButtonModel::mutex_;
40 
GetInstance()41 ButtonModel* ButtonModel::GetInstance()
42 {
43     if (!instance_) {
44         std::lock_guard<std::mutex> lock(mutex_);
45         if (!instance_) {
46 #ifdef NG_BUILD
47             instance_.reset(new NG::ButtonModelNG());
48 #else
49             if (Container::IsCurrentUseNewPipeline()) {
50                 instance_.reset(new NG::ButtonModelNG());
51             } else {
52                 instance_.reset(new Framework::ButtonModelImpl());
53             }
54 #endif
55         }
56     }
57     return instance_.get();
58 }
59 } // namespace OHOS::Ace
60 
61 namespace OHOS::Ace::Framework {
62 namespace {
63 
64 constexpr int32_t UNKNOWN_RESOURCE_TYPE = -1;
65 
ParseJsLengthMetrics(const JSRef<JSObject>& obj, std::optional<CalcDimension>& result)66 bool ParseJsLengthMetrics(const JSRef<JSObject>& obj, std::optional<CalcDimension>& result)
67 {
68     auto value = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::VALUE));
69     if (!value->IsNumber()) {
70         return false;
71     }
72     auto unit = DimensionUnit::VP;
73     auto jsUnit = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::UNIT));
74     if (jsUnit->IsNumber()) {
75         unit = static_cast<DimensionUnit>(jsUnit->ToNumber<int32_t>());
76     }
77     CalcDimension dimension(value->ToNumber<double>(), unit);
78     result = dimension;
79     return true;
80 }
81 
GetBorderRadiusByLengthMetrics( const char* key, const JSRef<JSObject>& object, std::optional<CalcDimension>& radius)82 void GetBorderRadiusByLengthMetrics(
83     const char* key, const JSRef<JSObject>& object, std::optional<CalcDimension>& radius)
84 {
85     if (object->HasProperty(key) && object->GetProperty(key)->IsObject()) {
86         JSRef<JSObject> startObj = JSRef<JSObject>::Cast(object->GetProperty(key));
87         ParseJsLengthMetrics(startObj, radius);
88     }
89 }
90 
GetNormalBorderRadius(const char* key, const JSRef<JSObject>& object, std::optional<CalcDimension>& radius)91 void GetNormalBorderRadius(const char* key, const JSRef<JSObject>& object, std::optional<CalcDimension>& radius)
92 {
93     CalcDimension calcDimension;
94     auto jsVal = object->GetProperty(key);
95     if (!jsVal->IsUndefined() && JSViewAbstract::ParseJsDimensionVp(jsVal, calcDimension)) {
96         radius = calcDimension;
97     }
98 }
99 
ParseAllBorderRadius(const JSRef<JSObject>& object, std::optional<CalcDimension>& topLeft, std::optional<CalcDimension>& topRight, std::optional<CalcDimension>& bottomLeft, std::optional<CalcDimension>& bottomRight)100 bool ParseAllBorderRadius(const JSRef<JSObject>& object, std::optional<CalcDimension>& topLeft,
101     std::optional<CalcDimension>& topRight, std::optional<CalcDimension>& bottomLeft,
102     std::optional<CalcDimension>& bottomRight)
103 {
104     if (object->HasProperty("topStart") || object->HasProperty("topEnd") || object->HasProperty("bottomStart") ||
105         object->HasProperty("bottomEnd")) {
106         GetBorderRadiusByLengthMetrics("topStart", object, topLeft);
107         GetBorderRadiusByLengthMetrics("topEnd", object, topRight);
108         GetBorderRadiusByLengthMetrics("bottomStart", object, bottomLeft);
109         GetBorderRadiusByLengthMetrics("bottomEnd", object, bottomRight);
110         return true;
111     }
112     GetNormalBorderRadius("topLeft", object, topLeft);
113     GetNormalBorderRadius("topRight", object, topRight);
114     GetNormalBorderRadius("bottomLeft", object, bottomLeft);
115     GetNormalBorderRadius("bottomRight", object, bottomRight);
116     return false;
117 }
118 } // namespace
119 const std::vector<TextOverflow> TEXT_OVERFLOWS = { TextOverflow::NONE, TextOverflow::CLIP, TextOverflow::ELLIPSIS,
120     TextOverflow::MARQUEE };
121 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
122 const std::vector<TextHeightAdaptivePolicy> HEIGHT_ADAPTIVE_POLICY = { TextHeightAdaptivePolicy::MAX_LINES_FIRST,
123     TextHeightAdaptivePolicy::MIN_FONT_SIZE_FIRST, TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST };
124 
125 bool JSButton::isLabelButton_ = false;
126 
SetFontSize(const JSCallbackInfo& info)127 void JSButton::SetFontSize(const JSCallbackInfo& info)
128 {
129     auto buttonTheme = GetTheme<ButtonTheme>();
130     CHECK_NULL_VOID(buttonTheme);
131     CalcDimension fontSize = buttonTheme->GetTextStyle().GetFontSize();
132     if (ParseJsDimensionVpNG(info[0], fontSize) && fontSize.Unit() != DimensionUnit::PERCENT &&
133         GreatOrEqual(fontSize.Value(), 0.0)) {
134         ParseJsDimensionFp(info[0], fontSize);
135     } else {
136         fontSize = buttonTheme->GetTextStyle().GetFontSize();
137     }
138     ButtonModel::GetInstance()->SetFontSize(fontSize);
139 }
140 
SetFontWeight(const std::string& value)141 void JSButton::SetFontWeight(const std::string& value)
142 {
143     ButtonModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(value));
144 }
145 
SetFontStyle(int32_t value)146 void JSButton::SetFontStyle(int32_t value)
147 {
148     const std::vector<FontStyle> fontStyles = { FontStyle::NORMAL, FontStyle::ITALIC };
149     if (value < 0 || value >= static_cast<int32_t>(fontStyles.size())) {
150         return;
151     }
152 
153     ButtonModel::GetInstance()->SetFontStyle(fontStyles[value]);
154 }
155 
SetFontFamily(const JSCallbackInfo& info)156 void JSButton::SetFontFamily(const JSCallbackInfo& info)
157 {
158     std::vector<std::string> fontFamilies;
159     if (!ParseJsFontFamilies(info[0], fontFamilies) || fontFamilies.empty()) {
160         auto pipelineContext = PipelineBase::GetCurrentContext();
161         CHECK_NULL_VOID(pipelineContext);
162         auto textTheme = pipelineContext->GetTheme<TextTheme>();
163         CHECK_NULL_VOID(textTheme);
164         fontFamilies = textTheme->GetTextStyle().GetFontFamilies();
165     }
166 
167     ButtonModel::GetInstance()->SetFontFamily(fontFamilies);
168 }
169 
SetTextColor(const JSCallbackInfo& info)170 void JSButton::SetTextColor(const JSCallbackInfo& info)
171 {
172     Color textColor;
173     if (!ParseJsColor(info[0], textColor)) {
174         auto buttonTheme = PipelineBase::GetCurrentContext()->GetTheme<ButtonTheme>();
175         textColor = buttonTheme->GetTextStyle().GetTextColor();
176     }
177 
178     ButtonModel::GetInstance()->SetFontColor(textColor);
179 }
180 
SetType(const JSCallbackInfo& info)181 void JSButton::SetType(const JSCallbackInfo& info)
182 {
183     int32_t value = static_cast<int32_t>(ButtonType::CAPSULE);
184     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_THIRTEEN)) {
185         value = static_cast<int32_t>(ButtonType::ROUNDED_RECTANGLE);
186     }
187     if (info[0]->IsNumber()) {
188         value = info[0]->ToNumber<int32_t>();
189     }
190     if ((ButtonType)value == ButtonType::CAPSULE || (ButtonType)value == ButtonType::CIRCLE ||
191         (ButtonType)value == ButtonType::ARC || (ButtonType)value == ButtonType::NORMAL ||
192         (ButtonType)value == ButtonType::ROUNDED_RECTANGLE) {
193         ButtonModel::GetInstance()->SetType(value);
194     }
195 }
196 
SetButtonStyle(const JSCallbackInfo& info)197 void JSButton::SetButtonStyle(const JSCallbackInfo& info)
198 {
199     int32_t value = static_cast<int32_t>(ButtonStyleMode::EMPHASIZE);
200     if (info[0]->IsNumber()) {
201         auto valueT = info[0]->ToNumber<int32_t>();
202         if (valueT >= static_cast<int32_t>(ButtonStyleMode::NORMAL) &&
203             valueT <= static_cast<int32_t>(ButtonStyleMode::TEXT)) {
204             value = valueT;
205         }
206     }
207     auto buttonStyleMode = static_cast<ButtonStyleMode>(value);
208     if (!JSButtonTheme::ApplyTheme(buttonStyleMode, isLabelButton_)) {
209         ButtonModel::GetInstance()->SetButtonStyle(buttonStyleMode);
210     }
211 }
212 
SetControlSize(const JSCallbackInfo& info)213 void JSButton::SetControlSize(const JSCallbackInfo& info)
214 {
215     int32_t value = static_cast<int32_t>(ControlSize::NORMAL);
216     if (info[0]->IsNumber()) {
217         auto valueT = info[0]->ToNumber<int32_t>();
218         if (valueT >= static_cast<int32_t>(ControlSize::SMALL) && valueT <= static_cast<int32_t>(ControlSize::NORMAL)) {
219             value = valueT;
220         }
221     }
222     ButtonModel::GetInstance()->SetControlSize(static_cast<ControlSize>(value));
223 }
224 
SetRole(const JSCallbackInfo& info)225 void JSButton::SetRole(const JSCallbackInfo& info)
226 {
227     int32_t value = static_cast<int32_t>(ButtonRole::NORMAL);
228     if (info[0]->IsNumber()) {
229         auto valueT = info[0]->ToNumber<int32_t>();
230         if (valueT >= static_cast<int32_t>(ButtonRole::NORMAL) && valueT <= static_cast<int32_t>(ButtonRole::ERROR)) {
231             value = valueT;
232         }
233     }
234     auto buttonRole = static_cast<ButtonRole>(value);
235     if (!JSButtonTheme::ApplyTheme(buttonRole, isLabelButton_)) {
236         ButtonModel::GetInstance()->SetRole(buttonRole);
237     }
238 }
239 
SetStateEffect(const JSCallbackInfo& info)240 void JSButton::SetStateEffect(const JSCallbackInfo& info)
241 {
242     bool value = info[0]->IsBoolean() ? info[0]->ToBoolean() : true;
243     ButtonModel::GetInstance()->SetStateEffect(value);
244 }
245 
GetFontContent(JSRef<JSVal>& font, ButtonParameters& buttonParameters)246 void JSButton::GetFontContent(JSRef<JSVal>& font, ButtonParameters& buttonParameters)
247 {
248     if (font->IsNull() || !font->IsObject()) {
249         return;
250     }
251     JSRef<JSObject> obj = JSRef<JSObject>::Cast(font);
252     JSRef<JSVal> size = obj->GetProperty("size");
253     CalcDimension fontSize;
254     if (ParseJsDimensionFp(size, fontSize)) {
255         buttonParameters.fontSize = fontSize;
256     }
257 
258     JSRef<JSVal> weight = obj->GetProperty("weight");
259     if (weight->IsString() || weight->IsNumber()) {
260         buttonParameters.fontWeight = ConvertStrToFontWeight(weight->ToString());
261     }
262 
263     JSRef<JSVal> family = obj->GetProperty("family");
264     std::vector<std::string> fontFamilies;
265     if (ParseJsFontFamilies(family, fontFamilies)) {
266         buttonParameters.fontFamily = fontFamilies;
267     }
268 
269     JSRef<JSVal> style = obj->GetProperty("style");
270     if (style->IsNumber()) {
271         auto value = style->ToNumber<int32_t>();
272         if (value >= 0 && value < static_cast<int32_t>(FONT_STYLES.size())) {
273             buttonParameters.fontStyle = FONT_STYLES[value];
274         }
275     }
276 }
277 
CompleteParameters(ButtonParameters& buttonParameters)278 void JSButton::CompleteParameters(ButtonParameters& buttonParameters)
279 {
280     auto buttonTheme = GetTheme<ButtonTheme>();
281     if (!buttonTheme) {
282         return;
283     }
284     auto textStyle = buttonTheme->GetTextStyle();
285     if (!buttonParameters.maxLines.has_value()) {
286         buttonParameters.maxLines = buttonTheme->GetTextMaxLines();
287     }
288     if (!buttonParameters.fontSize.has_value()) {
289         buttonParameters.fontSize = textStyle.GetFontSize();
290     }
291     if (!buttonParameters.fontWeight.has_value()) {
292         buttonParameters.fontWeight = textStyle.GetFontWeight();
293     }
294     if (!buttonParameters.fontStyle.has_value()) {
295         buttonParameters.fontStyle = textStyle.GetFontStyle();
296     }
297     if (!buttonParameters.heightAdaptivePolicy.has_value()) {
298         buttonParameters.heightAdaptivePolicy = TextHeightAdaptivePolicy::MAX_LINES_FIRST;
299     }
300     if (!buttonParameters.textOverflow.has_value()) {
301         buttonParameters.textOverflow = TextOverflow::CLIP;
302     }
303 }
304 
SetLableStyle(const JSCallbackInfo& info)305 void JSButton::SetLableStyle(const JSCallbackInfo& info)
306 {
307     if (!info[0]->IsObject()) {
308         return;
309     }
310 
311     ButtonParameters buttonParameters;
312     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
313     JSRef<JSVal> overflowValue = obj->GetProperty("overflow");
314     buttonParameters.textOverflow = TextOverflow::ELLIPSIS;
315     if (!overflowValue->IsNull() && overflowValue->IsNumber()) {
316         auto overflow = overflowValue->ToNumber<int32_t>();
317         if (overflow >= 0 && overflow < static_cast<int32_t>(TEXT_OVERFLOWS.size())) {
318             buttonParameters.textOverflow = TEXT_OVERFLOWS[overflow];
319         }
320     }
321 
322     JSRef<JSVal> maxLines = obj->GetProperty("maxLines");
323     if (!maxLines->IsNull() && maxLines->IsNumber()) {
324         buttonParameters.maxLines = Positive(maxLines->ToNumber<int32_t>()) ? maxLines->ToNumber<int32_t>() : 1;
325     }
326 
327     JSRef<JSVal> minFontSizeValue = obj->GetProperty("minFontSize");
328     CalcDimension minFontSize;
329     if (ParseJsDimensionFp(minFontSizeValue, minFontSize)) {
330         buttonParameters.minFontSize = minFontSize;
331     }
332 
333     JSRef<JSVal> maxFontSizeValue = obj->GetProperty("maxFontSize");
334     CalcDimension maxFontSize;
335     if (ParseJsDimensionFp(maxFontSizeValue, maxFontSize)) {
336         buttonParameters.maxFontSize = maxFontSize;
337     }
338 
339     JSRef<JSVal> adaptHeightValue = obj->GetProperty("heightAdaptivePolicy");
340     if (!adaptHeightValue->IsNull() && adaptHeightValue->IsNumber()) {
341         auto adaptHeight = adaptHeightValue->ToNumber<int32_t>();
342         if (adaptHeight >= 0 && adaptHeight < static_cast<int32_t>(HEIGHT_ADAPTIVE_POLICY.size())) {
343             buttonParameters.heightAdaptivePolicy = HEIGHT_ADAPTIVE_POLICY[adaptHeight];
344         }
345     }
346 
347     JSRef<JSVal> font = obj->GetProperty("font");
348     GetFontContent(font, buttonParameters);
349 
350     CompleteParameters(buttonParameters);
351     ButtonModel::GetInstance()->SetLabelStyle(buttonParameters);
352 }
353 
JsRemoteMessage(const JSCallbackInfo& info)354 void JSButton::JsRemoteMessage(const JSCallbackInfo& info)
355 {
356     RemoteCallback remoteCallback;
357     JSInteractableView::JsRemoteMessage(info, remoteCallback);
358     ButtonModel::GetInstance()->SetRemoteMessage(std::move(remoteCallback));
359 }
360 
JSBind(BindingTarget globalObj)361 void JSButton::JSBind(BindingTarget globalObj)
362 {
363     JSClass<JSButton>::Declare("Button");
364     JSClass<JSButton>::StaticMethod("fontColor", &JSButton::SetTextColor, MethodOptions::NONE);
365     JSClass<JSButton>::StaticMethod("fontSize", &JSButton::SetFontSize, MethodOptions::NONE);
366     JSClass<JSButton>::StaticMethod("fontWeight", &JSButton::SetFontWeight, MethodOptions::NONE);
367     JSClass<JSButton>::StaticMethod("fontStyle", &JSButton::SetFontStyle, MethodOptions::NONE);
368     JSClass<JSButton>::StaticMethod("fontFamily", &JSButton::SetFontFamily, MethodOptions::NONE);
369     JSClass<JSButton>::StaticMethod("type", &JSButton::SetType, MethodOptions::NONE);
370     JSClass<JSButton>::StaticMethod("stateEffect", &JSButton::SetStateEffect);
371     JSClass<JSButton>::StaticMethod("labelStyle", &JSButton::SetLableStyle, MethodOptions::NONE);
372     JSClass<JSButton>::StaticMethod("onClick", &JSButton::JsOnClick);
373     JSClass<JSButton>::StaticMethod("remoteMessage", &JSButton::JsRemoteMessage);
374     JSClass<JSButton>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
375     JSClass<JSButton>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
376     JSClass<JSButton>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
377     JSClass<JSButton>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
378     JSClass<JSButton>::StaticMethod("backgroundColor", &JSButton::JsBackgroundColor);
379     JSClass<JSButton>::StaticMethod("width", &JSButton::JsWidth);
380     JSClass<JSButton>::StaticMethod("height", &JSButton::JsHeight);
381     JSClass<JSButton>::StaticMethod("aspectRatio", &JSButton::JsAspectRatio);
382     JSClass<JSButton>::StaticMethod("borderRadius", &JSButton::JsRadius);
383     JSClass<JSButton>::StaticMethod("border", &JSButton::JsBorder);
384     JSClass<JSButton>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
385     JSClass<JSButton>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
386     JSClass<JSButton>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
387     JSClass<JSButton>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
388     JSClass<JSButton>::StaticMethod("size", &JSButton::JsSize);
389     JSClass<JSButton>::StaticMethod("padding", &JSButton::JsPadding);
390     JSClass<JSButton>::StaticMethod("buttonStyle", &JSButton::SetButtonStyle);
391     JSClass<JSButton>::StaticMethod("controlSize", &JSButton::SetControlSize);
392     JSClass<JSButton>::StaticMethod("role", &JSButton::SetRole);
393     JSClass<JSButton>::StaticMethod("createWithLabel", &JSButton::CreateWithLabel, MethodOptions::NONE);
394     JSClass<JSButton>::StaticMethod("createWithChild", &JSButton::CreateWithChild, MethodOptions::NONE);
395     JSClass<JSButton>::InheritAndBind<JSContainerBase>(globalObj);
396 }
397 
CreateWithLabel(const JSCallbackInfo& info)398 void JSButton::CreateWithLabel(const JSCallbackInfo& info)
399 {
400     std::list<RefPtr<Component>> buttonChildren;
401     CreateWithPara para = ParseCreatePara(info, true);
402     ButtonModel::GetInstance()->CreateWithLabel(para, buttonChildren);
403     ButtonModel::GetInstance()->Create(para, buttonChildren);
404     isLabelButton_ = true;
405     auto buttonRole = para.buttonRole.value_or(ButtonRole::NORMAL);
406     auto buttonStyleMode = para.buttonStyleMode.value_or(ButtonStyleMode::EMPHASIZE);
407     JSButtonTheme::ApplyTheme(buttonRole, buttonStyleMode, isLabelButton_);
408     ButtonModel::GetInstance()->SetCreateWithLabel(true);
409 }
410 
CreateWithChild(const JSCallbackInfo& info)411 void JSButton::CreateWithChild(const JSCallbackInfo& info)
412 {
413     CreateWithPara para = ParseCreatePara(info, false);
414     ButtonModel::GetInstance()->CreateWithChild(para);
415     isLabelButton_ = false;
416     auto buttonRole = para.buttonRole.value_or(ButtonRole::NORMAL);
417     auto buttonStyleMode = para.buttonStyleMode.value_or(ButtonStyleMode::EMPHASIZE);
418     JSButtonTheme::ApplyTheme(buttonRole, buttonStyleMode, isLabelButton_);
419     ButtonModel::GetInstance()->SetCreateWithLabel(false);
420 }
421 
JsPadding(const JSCallbackInfo& info)422 void JSButton::JsPadding(const JSCallbackInfo& info)
423 {
424     NG::PaddingProperty paddingNew = GetNewPadding(info);
425     Edge paddingOld = Edge(GetOldPadding(info));
426     ButtonModel::GetInstance()->Padding(paddingNew, paddingOld);
427 }
428 
GetOldPadding(const JSCallbackInfo& info)429 Edge JSButton::GetOldPadding(const JSCallbackInfo& info)
430 {
431     Edge padding;
432 
433     if (info[0]->IsNumber()) {
434         CalcDimension edgeValue;
435         if (ParseJsDimensionVp(info[0], edgeValue)) {
436             padding = Edge(edgeValue);
437         }
438     } else if (info[0]->IsObject()) {
439         JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
440         CalcDimension left = CalcDimension(0.0, DimensionUnit::VP);
441         CalcDimension top = CalcDimension(0.0, DimensionUnit::VP);
442         CalcDimension right = CalcDimension(0.0, DimensionUnit::VP);
443         CalcDimension bottom = CalcDimension(0.0, DimensionUnit::VP);
444         if (jsObj->HasProperty("top") || jsObj->HasProperty("bottom") || jsObj->HasProperty("left") ||
445             jsObj->HasProperty("right")) {
446             ParseJsDimensionVp(jsObj->GetProperty("left"), left);
447             ParseJsDimensionVp(jsObj->GetProperty("top"), top);
448             ParseJsDimensionVp(jsObj->GetProperty("right"), right);
449             ParseJsDimensionVp(jsObj->GetProperty("bottom"), bottom);
450         }
451         padding = Edge(left, top, right, bottom);
452     }
453 
454     return padding;
455 }
456 
GetNewPadding(const JSCallbackInfo& info)457 NG::PaddingProperty JSButton::GetNewPadding(const JSCallbackInfo& info)
458 {
459     NG::PaddingProperty padding = { NG::CalcLength(0.0), NG::CalcLength(0.0), NG::CalcLength(0.0),
460         NG::CalcLength(0.0) };
461     if (isLabelButton_) {
462         auto buttonTheme = GetTheme<ButtonTheme>();
463         auto defaultPadding = buttonTheme->GetPadding();
464         padding = { NG::CalcLength(defaultPadding.Left()), NG::CalcLength(defaultPadding.Right()),
465             NG::CalcLength(defaultPadding.Top()), NG::CalcLength(defaultPadding.Bottom()) };
466     }
467     if (info[0]->IsObject()) {
468         CommonCalcDimension commonCalcDimension;
469         JSRef<JSObject> paddingObj = JSRef<JSObject>::Cast(info[0]);
470         JSViewAbstract::ParseCommonMarginOrPaddingCorner(paddingObj, commonCalcDimension);
471         if (commonCalcDimension.left.has_value() || commonCalcDimension.right.has_value() ||
472             commonCalcDimension.top.has_value() || commonCalcDimension.bottom.has_value()) {
473             return SetPaddings(commonCalcDimension.top, commonCalcDimension.bottom, commonCalcDimension.left,
474                 commonCalcDimension.right);
475         }
476     }
477     CalcDimension length(-1);
478     ParseJsDimensionVp(info[0], length);
479     if (length.IsNonNegative()) {
480         padding.SetEdges(NG::CalcLength(length));
481     }
482     return padding;
483 }
484 
SetPaddings(const std::optional<CalcDimension>& top, const std::optional<CalcDimension>& bottom, const std::optional<CalcDimension>& left, const std::optional<CalcDimension>& right)485 NG::PaddingProperty JSButton::SetPaddings(const std::optional<CalcDimension>& top,
486     const std::optional<CalcDimension>& bottom, const std::optional<CalcDimension>& left,
487     const std::optional<CalcDimension>& right)
488 {
489     NG::PaddingProperty paddings;
490     if (top.has_value()) {
491         if (top.value().Unit() == DimensionUnit::CALC) {
492             paddings.top =
493                 NG::CalcLength(top.value().IsNonNegative() ? top.value().CalcValue() : CalcDimension().CalcValue());
494         } else {
495             paddings.top = NG::CalcLength(top.value().IsNonNegative() ? top.value() : CalcDimension());
496         }
497     }
498     if (bottom.has_value()) {
499         if (bottom.value().Unit() == DimensionUnit::CALC) {
500             paddings.bottom = NG::CalcLength(
501                 bottom.value().IsNonNegative() ? bottom.value().CalcValue() : CalcDimension().CalcValue());
502         } else {
503             paddings.bottom = NG::CalcLength(bottom.value().IsNonNegative() ? bottom.value() : CalcDimension());
504         }
505     }
506     if (left.has_value()) {
507         if (left.value().Unit() == DimensionUnit::CALC) {
508             paddings.left =
509                 NG::CalcLength(left.value().IsNonNegative() ? left.value().CalcValue() : CalcDimension().CalcValue());
510         } else {
511             paddings.left = NG::CalcLength(left.value().IsNonNegative() ? left.value() : CalcDimension());
512         }
513     }
514     if (right.has_value()) {
515         if (right.value().Unit() == DimensionUnit::CALC) {
516             paddings.right =
517                 NG::CalcLength(right.value().IsNonNegative() ? right.value().CalcValue() : CalcDimension().CalcValue());
518         } else {
519             paddings.right = NG::CalcLength(right.value().IsNonNegative() ? right.value() : CalcDimension());
520         }
521     }
522 
523     return paddings;
524 }
525 
JsOnClick(const JSCallbackInfo& info)526 void JSButton::JsOnClick(const JSCallbackInfo& info)
527 {
528     if (info[0]->IsUndefined() && IsDisableEventVersion()) {
529         ViewAbstractModel::GetInstance()->DisableOnClick();
530         return;
531     }
532     if (!info[0]->IsFunction()) {
533         return;
534     }
535 
536     auto jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
537     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
538     auto onTap = [execCtx = info.GetExecutionContext(), func = jsOnClickFunc, node = targetNode](GestureEvent& info) {
539         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
540         ACE_SCORING_EVENT("onClick");
541         PipelineContext::SetCallBackNode(node);
542         func->Execute(info);
543 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
544         JSInteractableView::ReportClickEvent(node);
545 #endif
546     };
547     auto onClick = [execCtx = info.GetExecutionContext(), func = jsOnClickFunc, node = targetNode](
548                        const ClickInfo* info) {
549         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
550         ACE_SCORING_EVENT("onClick");
551         PipelineContext::SetCallBackNode(node);
552         func->Execute(*info);
553 #if !defined(PREVIEW) && defined(OHOS_PLATFORM)
554         JSInteractableView::ReportClickEvent(node);
555 #endif
556     };
557 
558     double distanceThreshold = std::numeric_limits<double>::infinity();
559     if (info.Length() > 1 && info[1]->IsNumber()) {
560         distanceThreshold = info[1]->ToNumber<double>();
561         distanceThreshold = Dimension(distanceThreshold, DimensionUnit::VP).ConvertToPx();
562     }
563     ButtonModel::GetInstance()->OnClick(std::move(onTap), std::move(onClick), distanceThreshold);
564 }
565 
JsBackgroundColor(const JSCallbackInfo& info)566 void JSButton::JsBackgroundColor(const JSCallbackInfo& info)
567 {
568     Color backgroundColor;
569     bool colorFlag = ParseJsColor(info[0], backgroundColor);
570     if (!colorFlag) {
571         auto buttonTheme = GetTheme<ButtonTheme>();
572         if (buttonTheme) {
573             backgroundColor = buttonTheme->GetBgColor();
574         }
575     }
576 
577     ButtonModel::GetInstance()->BackgroundColor(backgroundColor, colorFlag);
578     info.ReturnSelf();
579 }
580 
JsWidth(const JSCallbackInfo& info)581 void JSButton::JsWidth(const JSCallbackInfo& info)
582 {
583     JSViewAbstract::JsWidth(info);
584     CalcDimension value = GetSizeValue(info);
585     if (LessNotEqual(value.Value(), 0.0)) {
586         return;
587     }
588 
589     ButtonModel::GetInstance()->SetWidth(value);
590 }
591 
JsHeight(const JSCallbackInfo& info)592 void JSButton::JsHeight(const JSCallbackInfo& info)
593 {
594     JSViewAbstract::JsHeight(info);
595     CalcDimension value = GetSizeValue(info);
596     if (LessNotEqual(value.Value(), 0.0)) {
597         return;
598     }
599 
600     ButtonModel::GetInstance()->SetHeight(value);
601 }
602 
JsAspectRatio(const JSCallbackInfo& info)603 void JSButton::JsAspectRatio(const JSCallbackInfo& info)
604 {
605     JSViewAbstract::JsAspectRatio(info);
606     double value = 0.0;
607     if (!ParseJsDouble(info[0], value)) {
608         return;
609     }
610 
611     ButtonModel::GetInstance()->SetAspectRatio(value);
612 }
613 
JsSize(const JSCallbackInfo& info)614 void JSButton::JsSize(const JSCallbackInfo& info)
615 {
616     if (!info[0]->IsObject()) {
617         JSViewAbstract::JsWidth(JSVal::Undefined());
618         JSViewAbstract::JsHeight(JSVal::Undefined());
619         return;
620     }
621     JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
622     JSViewAbstract::JsWidth(sizeObj->GetProperty("width"));
623     JSViewAbstract::JsHeight(sizeObj->GetProperty("height"));
624 }
625 
JsRadius(const JSCallbackInfo& info)626 void JSButton::JsRadius(const JSCallbackInfo& info)
627 {
628     JsRadius(info[0]);
629 }
630 
JsRadius(const JSRef<JSVal>& jsValue)631 void JSButton::JsRadius(const JSRef<JSVal>& jsValue)
632 {
633     CalcDimension radius;
634     if (ParseJsDimensionVpNG(jsValue, radius)) {
635         ButtonModel::GetInstance()->SetBorderRadius(radius);
636     } else if (jsValue->IsObject() && ((JSRef<JSObject>::Cast(jsValue)->GetPropertyValue<int32_t>(
637                                            "type", UNKNOWN_RESOURCE_TYPE)) == UNKNOWN_RESOURCE_TYPE)) {
638         JSRef<JSObject> object = JSRef<JSObject>::Cast(jsValue);
639         std::optional<CalcDimension> radiusTopLeft;
640         std::optional<CalcDimension> radiusTopRight;
641         std::optional<CalcDimension> radiusBottomLeft;
642         std::optional<CalcDimension> radiusBottomRight;
643         if (ParseAllBorderRadius(object, radiusTopLeft, radiusTopRight, radiusBottomLeft, radiusBottomRight)) {
644             ButtonModel::GetInstance()->SetLocalizedBorderRadius(
645                 radiusTopLeft, radiusTopRight, radiusBottomLeft, radiusBottomRight);
646         } else {
647             ButtonModel::GetInstance()->SetBorderRadius(
648                 radiusTopLeft, radiusTopRight, radiusBottomLeft, radiusBottomRight);
649         }
650     } else {
651         ButtonModel::GetInstance()->ResetBorderRadius();
652     }
653 }
654 
JsBorder(const JSCallbackInfo& info)655 void JSButton::JsBorder(const JSCallbackInfo& info)
656 {
657     JSViewAbstract::JsBorder(info);
658     if (!info[0]->IsObject()) {
659         return;
660     }
661     JSRef<JSObject> object = JSRef<JSObject>::Cast(info[0]);
662     CalcDimension borderRadius;
663     auto valueRadius = object->GetProperty("radius");
664     JsRadius(valueRadius);
665 }
666 
GetSizeValue(const JSCallbackInfo& info)667 CalcDimension JSButton::GetSizeValue(const JSCallbackInfo& info)
668 {
669     CalcDimension value;
670     if (!ParseJsDimensionVp(info[0], value)) {
671         return { -1.0 };
672     }
673     return value;
674 }
675 
ParseCreatePara(const JSCallbackInfo& info, bool hasLabel)676 CreateWithPara JSButton::ParseCreatePara(const JSCallbackInfo& info, bool hasLabel)
677 {
678     std::string label;
679     CreateWithPara para;
680     para.parseSuccess = false;
681     para.optionSetFirst = false;
682     if (info.Length() < 1) {
683         para.label = label;
684         return para;
685     }
686     int32_t optionIndex = 0;
687     if (hasLabel) {
688         para.parseSuccess = ParseJsString(info[0], label);
689         if (para.parseSuccess) {
690             // resource string
691             if (info[0]->IsObject() && JSRef<JSObject>::Cast(info[0])->HasProperty("id")) {
692                 optionIndex++;
693                 // string
694             } else if (info[0]->IsString()) {
695                 optionIndex++;
696             }
697         }
698         para.label = label;
699     }
700     if (optionIndex >= info.Length() || !info[optionIndex]->IsObject()) {
701         return para;
702     }
703     if (optionIndex == 0) {
704         para.optionSetFirst = true;
705     }
706     JSRef<JSObject> optionObj = JSRef<JSObject>::Cast(info[optionIndex]);
707     if (optionObj->GetProperty(JSButton::TYPE)->IsNumber()) {
708         para.type = static_cast<ButtonType>(optionObj->GetProperty(JSButton::TYPE)->ToNumber<int32_t>());
709     }
710     if (optionObj->GetProperty(JSButton::STATE_EFFECT)->IsBoolean()) {
711         para.stateEffect = optionObj->GetProperty(JSButton::STATE_EFFECT)->ToBoolean();
712     }
713     if (optionObj->HasProperty(JSButton::BUTTON_STYLE)) {
714         para.buttonStyleMode = ButtonStyleMode::EMPHASIZE;
715     }
716     if (optionObj->GetProperty(JSButton::BUTTON_STYLE)->IsNumber()) {
717         auto styleModeIntValue = optionObj->GetProperty(JSButton::BUTTON_STYLE)->ToNumber<int32_t>();
718         if (styleModeIntValue >= static_cast<int32_t>(ButtonStyleMode::NORMAL) &&
719             styleModeIntValue <= static_cast<int32_t>(ButtonStyleMode::TEXT)) {
720             para.buttonStyleMode = static_cast<ButtonStyleMode>(styleModeIntValue);
721         }
722     }
723     if (optionObj->HasProperty(JSButton::CONTROL_SIZE)) {
724         para.controlSize = ControlSize::NORMAL;
725     }
726     if (optionObj->GetProperty(JSButton::CONTROL_SIZE)->IsNumber()) {
727         auto controlSizeIntValue = optionObj->GetProperty(JSButton::CONTROL_SIZE)->ToNumber<int32_t>();
728         if (controlSizeIntValue >= static_cast<int32_t>(ControlSize::SMALL) &&
729             controlSizeIntValue <= static_cast<int32_t>(ControlSize::NORMAL)) {
730             para.controlSize = static_cast<ControlSize>(controlSizeIntValue);
731         }
732     }
733     ParseButtonRole(optionObj, para);
734     return para;
735 }
736 
ParseButtonRole(const JSRef<JSObject>& optionObj, CreateWithPara& param)737 void JSButton::ParseButtonRole(const JSRef<JSObject>& optionObj, CreateWithPara& param)
738 {
739     if (optionObj->HasProperty(JSButton::ROLE)) {
740         param.buttonRole = ButtonRole::NORMAL;
741     }
742     if (optionObj->GetProperty(JSButton::ROLE)->IsNumber()) {
743         auto buttonRoleIntValue = optionObj->GetProperty(JSButton::ROLE)->ToNumber<int32_t>();
744         if (buttonRoleIntValue >= static_cast<int32_t>(ButtonRole::NORMAL) &&
745             buttonRoleIntValue <= static_cast<int32_t>(ButtonRole::ERROR)) {
746             param.buttonRole = static_cast<ButtonRole>(buttonRoleIntValue);
747         }
748     }
749 }
750 } // namespace OHOS::Ace::Framework
751