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