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