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