* Copyright (c) 2021-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "frameworks/bridge/declarative_frontend/jsview/js_button.h"
#include <limits>
#if !defined(PREVIEW) && defined(OHOS_PLATFORM)
#include "interfaces/inner_api/ui_session/ui_session_manager.h"
#endif
#include "base/geometry/dimension.h"
#include "base/log/ace_scoring_log.h"
#include "base/log/ace_trace.h"
#include "base/utils/utils.h"
#include "core/components/button/button_component.h"
#include "core/components/button/button_theme.h"
#include "core/components/common/layout/common_text_constants.h"
#include "core/components/text/text_theme.h"
#include "core/components_ng/base/view_stack_processor.h"
#include "core/components_ng/pattern/button/button_model_ng.h"
#include "frameworks/bridge/declarative_frontend/ark_theme/theme_apply/js_button_theme.h"
#include "frameworks/bridge/declarative_frontend/engine/functions/js_click_function.h"
#include "frameworks/bridge/declarative_frontend/engine/jsi/js_ui_index.h"
#include "frameworks/bridge/declarative_frontend/jsview/js_utils.h"
#include "frameworks/bridge/declarative_frontend/jsview/models/button_model_impl.h"
#include "frameworks/bridge/declarative_frontend/view_stack_processor.h"
#include "core/components_ng/pattern/button/button_layout_property.h"
namespace OHOS::Ace {
ButtonModel* ButtonModel::GetInstance()
{
#ifdef NG_BUILD
static NG::ButtonModelNG instance;
return &instance;
#else
if (Container::IsCurrentUseNewPipeline()) {
static NG::ButtonModelNG instance;
return &instance;
} else {
static Framework::ButtonModelImpl instance;
return &instance;
}
#endif
}
}
namespace OHOS::Ace::Framework {
namespace {
constexpr int32_t UNKNOWN_RESOURCE_TYPE = -1;
bool ParseJsLengthMetrics(const JSRef<JSObject>& obj, std::optional<CalcDimension>& result)
{
auto value = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::VALUE));
if (!value->IsNumber()) {
return false;
}
auto unit = DimensionUnit::VP;
auto jsUnit = obj->GetProperty(static_cast<int32_t>(ArkUIIndex::UNIT));
if (jsUnit->IsNumber()) {
unit = static_cast<DimensionUnit>(jsUnit->ToNumber<int32_t>());
}
CalcDimension dimension(value->ToNumber<double>(), unit);
result = dimension;
return true;
}
void GetBorderRadiusByLengthMetrics(
const char* key, const JSRef<JSObject>& object, std::optional<CalcDimension>& radius)
{
if (object->HasProperty(key) && object->GetProperty(key)->IsObject()) {
JSRef<JSObject> startObj = JSRef<JSObject>::Cast(object->GetProperty(key));
ParseJsLengthMetrics(startObj, radius);
}
}
void GetNormalBorderRadius(const char* key, const JSRef<JSObject>& object, std::optional<CalcDimension>& radius)
{
CalcDimension calcDimension;
auto jsVal = object->GetProperty(key);
if (!jsVal->IsUndefined() && JSViewAbstract::ParseJsDimensionVp(jsVal, calcDimension)) {
radius = calcDimension;
}
}
bool ParseAllBorderRadius(const JSRef<JSObject>& object, std::optional<CalcDimension>& topLeft,
std::optional<CalcDimension>& topRight, std::optional<CalcDimension>& bottomLeft,
std::optional<CalcDimension>& bottomRight)
{
if (object->HasProperty("topStart") || object->HasProperty("topEnd") || object->HasProperty("bottomStart") ||
object->HasProperty("bottomEnd")) {
GetBorderRadiusByLengthMetrics("topStart", object, topLeft);
GetBorderRadiusByLengthMetrics("topEnd", object, topRight);
GetBorderRadiusByLengthMetrics("bottomStart", object, bottomLeft);
GetBorderRadiusByLengthMetrics("bottomEnd", object, bottomRight);
return true;
}
GetNormalBorderRadius("topLeft", object, topLeft);
GetNormalBorderRadius("topRight", object, topRight);
GetNormalBorderRadius("bottomLeft", object, bottomLeft);
GetNormalBorderRadius("bottomRight", object, bottomRight);
return false;
}
}
const std::vector<TextOverflow> TEXT_OVERFLOWS = { TextOverflow::NONE, TextOverflow::CLIP, TextOverflow::ELLIPSIS,
TextOverflow::MARQUEE };
const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
const std::vector<TextHeightAdaptivePolicy> HEIGHT_ADAPTIVE_POLICY = { TextHeightAdaptivePolicy::MAX_LINES_FIRST,
TextHeightAdaptivePolicy::MIN_FONT_SIZE_FIRST, TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST };
bool JSButton::isLabelButton_ = false;
void JSButton::SetFontSize(const JSCallbackInfo& info)
{
auto buttonTheme = GetTheme<ButtonTheme>();
CHECK_NULL_VOID(buttonTheme);
CalcDimension fontSize = buttonTheme->GetTextStyle().GetFontSize();
if (ParseJsDimensionVpNG(info[0], fontSize) && fontSize.Unit() != DimensionUnit::PERCENT &&
GreatOrEqual(fontSize.Value(), 0.0)) {
ParseJsDimensionFp(info[0], fontSize);
} else {
fontSize = buttonTheme->GetTextStyle().GetFontSize();
}
ButtonModel::GetInstance()->SetFontSize(fontSize);
}
void JSButton::SetFontWeight(const std::string& value)
{
ButtonModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(value));
}
void JSButton::SetFontStyle(int32_t value)
{
const std::vector<FontStyle> fontStyles = { FontStyle::NORMAL, FontStyle::ITALIC };
if (value < 0 || value >= static_cast<int32_t>(fontStyles.size())) {
return;
}
ButtonModel::GetInstance()->SetFontStyle(fontStyles[value]);
}
void JSButton::SetFontFamily(const JSCallbackInfo& info)
{
std::vector<std::string> fontFamilies;
RefPtr<ResourceObject> resObj;
if (!ParseJsFontFamilies(info[0], fontFamilies, resObj) || fontFamilies.empty()) {
auto pipelineContext = PipelineBase::GetCurrentContext();
CHECK_NULL_VOID(pipelineContext);
auto textTheme = pipelineContext->GetTheme<TextTheme>();
CHECK_NULL_VOID(textTheme);
fontFamilies = textTheme->GetTextStyle().GetFontFamilies();
}
if (SystemProperties::ConfigChangePerform()) {
ButtonModel::GetInstance()->CreateWithFamiliesResourceObj(resObj, ButtonStringType::FONT_FAMILY);
}
ButtonModel::GetInstance()->SetFontFamily(fontFamilies);
}
void JSButton::SetTextColor(const JSCallbackInfo& info)
{
Color textColor;
RefPtr<ResourceObject> resObj;
if (!ParseJsColorForMaterial(info[0], textColor, resObj)) {
auto buttonTheme = GetTheme<ButtonTheme>();
if (buttonTheme) {
textColor = buttonTheme->GetTextStyle().GetTextColor();
}
}
if (SystemProperties::ConfigChangePerform()) {
ButtonModel::GetInstance()->CreateWithColorResourceObj(resObj, ButtonColorType::FONT_COLOR);
}
ButtonModel::GetInstance()->SetFontColor(textColor);
}
void JSButton::SetType(const JSCallbackInfo& info)
{
int32_t value = static_cast<int32_t>(ButtonType::CAPSULE);
if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
value = static_cast<int32_t>(ButtonType::ROUNDED_RECTANGLE);
}
if (info[0]->IsNumber()) {
value = info[0]->ToNumber<int32_t>();
}
if ((ButtonType)value == ButtonType::CAPSULE || (ButtonType)value == ButtonType::CIRCLE ||
(ButtonType)value == ButtonType::ARC || (ButtonType)value == ButtonType::NORMAL ||
(ButtonType)value == ButtonType::ROUNDED_RECTANGLE) {
ButtonModel::GetInstance()->SetType(value);
}
}
void JSButton::SetButtonStyle(const JSCallbackInfo& info)
{
int32_t value = static_cast<int32_t>(ButtonStyleMode::EMPHASIZE);
if (info[0]->IsNumber()) {
auto valueT = info[0]->ToNumber<int32_t>();
if (valueT >= static_cast<int32_t>(ButtonStyleMode::NORMAL) &&
valueT <= static_cast<int32_t>(ButtonStyleMode::TEXT)) {
value = valueT;
}
}
auto buttonStyleMode = static_cast<ButtonStyleMode>(value);
if (!JSButtonTheme::ApplyTheme(buttonStyleMode, isLabelButton_)) {
ButtonModel::GetInstance()->SetButtonStyle(buttonStyleMode);
} else {
ButtonModel::GetInstance()->SetButtonStyleOnly(buttonStyleMode);
}
}
void JSButton::SetControlSize(const JSCallbackInfo& info)
{
int32_t value = static_cast<int32_t>(ControlSize::NORMAL);
if (info[0]->IsNumber()) {
auto valueT = info[0]->ToNumber<int32_t>();
if (valueT >= static_cast<int32_t>(ControlSize::SMALL) && valueT <= static_cast<int32_t>(ControlSize::NORMAL)) {
value = valueT;
}
}
ButtonModel::GetInstance()->SetControlSize(static_cast<ControlSize>(value));
}
void JSButton::SetRole(const JSCallbackInfo& info)
{
int32_t value = static_cast<int32_t>(ButtonRole::NORMAL);
if (info[0]->IsNumber()) {
auto valueT = info[0]->ToNumber<int32_t>();
if (valueT >= static_cast<int32_t>(ButtonRole::NORMAL) && valueT <= static_cast<int32_t>(ButtonRole::ERROR)) {
value = valueT;
}
}
auto buttonRole = static_cast<ButtonRole>(value);
if (!JSButtonTheme::ApplyTheme(buttonRole, isLabelButton_)) {
ButtonModel::GetInstance()->SetRole(buttonRole);
} else {
ButtonModel::GetInstance()->SetRoleOnly(buttonRole);
}
}
void JSButton::SetMinFontScale(const JSCallbackInfo& info)
{
double minFontScale;
RefPtr<ResourceObject> resObj;
if (info.Length() < 1 || !ParseJsDouble(info[0], minFontScale, resObj)) {
if (SystemProperties::ConfigChangePerform()) {
ButtonModel::GetInstance()->CreateWithDoubleResourceObj(resObj, ButtonDoubleType::MIN_FONT_SCALE);
}
return;
}
if (SystemProperties::ConfigChangePerform()) {
ButtonModel::GetInstance()->CreateWithDoubleResourceObj(resObj, ButtonDoubleType::MIN_FONT_SCALE);
}
if (LessOrEqual(minFontScale, 0.0f)) {
ButtonModel::GetInstance()->SetMinFontScale(0.0f);
return;
}
if (GreatOrEqual(minFontScale, 1.0f)) {
ButtonModel::GetInstance()->SetMinFontScale(1.0f);
return;
}
ButtonModel::GetInstance()->SetMinFontScale(static_cast<float>(minFontScale));
}
void JSButton::SetMaxFontScale(const JSCallbackInfo& info)
{
double maxFontScale;
RefPtr<ResourceObject> resObj;
if (info.Length() < 1 || !ParseJsDouble(info[0], maxFontScale, resObj)) {
if (SystemProperties::ConfigChangePerform()) {
ButtonModel::GetInstance()->CreateWithDoubleResourceObj(resObj, ButtonDoubleType::MAX_FONT_SCALE);
}
return;
}
if (SystemProperties::ConfigChangePerform()) {
ButtonModel::GetInstance()->CreateWithDoubleResourceObj(resObj, ButtonDoubleType::MAX_FONT_SCALE);
}
if (LessOrEqual(maxFontScale, 1.0f)) {
ButtonModel::GetInstance()->SetMaxFontScale(1.0f);
return;
}
ButtonModel::GetInstance()->SetMaxFontScale(static_cast<float>(maxFontScale));
}
void JSButton::SetStateEffect(const JSCallbackInfo& info)
{
bool value = info[0]->IsBoolean() ? info[0]->ToBoolean() : true;
ButtonModel::GetInstance()->SetStateEffect(value);
}
void JSButton::GetFontContent(JSRef<JSVal>& font, ButtonParameters& buttonParameters)
{
if (font->IsNull() || !font->IsObject()) {
return;
}
JSRef<JSObject> obj = JSRef<JSObject>::Cast(font);
JSRef<JSVal> size = obj->GetProperty("size");
CalcDimension fontSize;
if (ParseJsDimensionFp(size, fontSize)) {
buttonParameters.fontSize = fontSize;
}
JSRef<JSVal> weight = obj->GetProperty("weight");
if (weight->IsString() || weight->IsNumber()) {
buttonParameters.fontWeight = ConvertStrToFontWeight(weight->ToString());
}
JSRef<JSVal> family = obj->GetProperty("family");
std::vector<std::string> fontFamilies;
if (ParseJsFontFamilies(family, fontFamilies)) {
buttonParameters.fontFamily = fontFamilies;
}
JSRef<JSVal> style = obj->GetProperty("style");
if (style->IsNumber()) {
auto value = style->ToNumber<int32_t>();
if (value >= 0 && value < static_cast<int32_t>(FONT_STYLES.size())) {
buttonParameters.fontStyle = FONT_STYLES[value];
}
}
}
void JSButton::CompleteParameters(ButtonParameters& buttonParameters)
{
auto buttonTheme = GetTheme<ButtonTheme>();
if (!buttonTheme) {
return;
}
auto textStyle = buttonTheme->GetTextStyle();
if (!buttonParameters.maxLines.has_value()) {
buttonParameters.maxLines = buttonTheme->GetTextMaxLines();
}
if (!buttonParameters.fontSize.has_value()) {
buttonParameters.fontSize = textStyle.GetFontSize();
}
if (!buttonParameters.fontWeight.has_value()) {
buttonParameters.fontWeight = textStyle.GetFontWeight();
}
if (!buttonParameters.fontStyle.has_value()) {
buttonParameters.fontStyle = textStyle.GetFontStyle();
}
if (!buttonParameters.heightAdaptivePolicy.has_value()) {
buttonParameters.heightAdaptivePolicy = TextHeightAdaptivePolicy::MAX_LINES_FIRST;
}
if (!buttonParameters.textOverflow.has_value()) {
buttonParameters.textOverflow = TextOverflow::CLIP;
}
}
bool JSButton::SetTextAlign(JSRef<JSVal>& textAlign, ButtonParameters& buttonParameters)
{
if (!textAlign->IsNull() && textAlign->IsNumber()) {
auto textAlignValue = textAlign->ToNumber<int32_t>();
if (textAlignValue >= 0 && textAlignValue < static_cast<int32_t>(TEXT_ALIGNS.size())) {
buttonParameters.textAlign = TEXT_ALIGNS[textAlignValue];
return true;
}
}
return false;
}
void JSButton::SetLableStyle(const JSCallbackInfo& info)
{
if (!info[0]->IsObject()) {
return;
}
ButtonParameters buttonParameters;
JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
JSRef<JSVal> overflowValue = obj->GetProperty("overflow");
buttonParameters.textOverflow = TextOverflow::ELLIPSIS;
if (!overflowValue->IsNull() && overflowValue->IsNumber()) {
auto overflow = overflowValue->ToNumber<int32_t>();
if (overflow >= 0 && overflow < static_cast<int32_t>(TEXT_OVERFLOWS.size())) {
buttonParameters.textOverflow = TEXT_OVERFLOWS[overflow];
}
}
JSRef<JSVal> maxLines = obj->GetProperty("maxLines");
if (!maxLines->IsNull() && maxLines->IsNumber()) {
buttonParameters.maxLines = Positive(maxLines->ToNumber<int32_t>()) ? maxLines->ToNumber<int32_t>() : 1;
}
JSRef<JSVal> minFontSizeValue = obj->GetProperty("minFontSize");
SetMinMaxFontSize(buttonParameters, minFontSizeValue, ButtonDimensionType::MIN_FONT_SIZE);
JSRef<JSVal> maxFontSizeValue = obj->GetProperty("maxFontSize");
SetMinMaxFontSize(buttonParameters, maxFontSizeValue, ButtonDimensionType::MAX_FONT_SIZE);
JSRef<JSVal> adaptHeightValue = obj->GetProperty("heightAdaptivePolicy");
if (!adaptHeightValue->IsNull() && adaptHeightValue->IsNumber()) {
auto adaptHeight = adaptHeightValue->ToNumber<int32_t>();
if (adaptHeight >= 0 && adaptHeight < static_cast<int32_t>(HEIGHT_ADAPTIVE_POLICY.size())) {
buttonParameters.heightAdaptivePolicy = HEIGHT_ADAPTIVE_POLICY[adaptHeight];
}
}
JSRef<JSVal> font = obj->GetProperty("font");
GetFontContent(font, buttonParameters);
JSRef<JSVal> textAlign = obj->GetProperty("textAlign");
if (!SetTextAlign(textAlign, buttonParameters)) {
ButtonModel::GetInstance()->ResetTextAlign();
}
CompleteParameters(buttonParameters);
ButtonModel::GetInstance()->SetLabelStyle(buttonParameters);
}
void JSButton::SetMinMaxFontSize(ButtonParameters& buttonParameters, const JSRef<JSVal>& fontSizeValue,
const ButtonDimensionType type)
{
CalcDimension fontSize;
RefPtr<ResourceObject> fontResObj;
if (ParseJsDimensionFpNG(fontSizeValue, fontSize, fontResObj, false)) {
if (type == ButtonDimensionType::MIN_FONT_SIZE) {
buttonParameters.minFontSize = fontSize;
} else if (type == ButtonDimensionType::MAX_FONT_SIZE) {
buttonParameters.maxFontSize = fontSize;
}
}
if (SystemProperties::ConfigChangePerform()) {
ButtonModel::GetInstance()->CreateWithDimensionFpResourceObj(fontResObj, type);
}
}
void JSButton::JsRemoteMessage(const JSCallbackInfo& info)
{
RemoteCallback remoteCallback;
JSInteractableView::JsRemoteMessage(info, remoteCallback);
ButtonModel::GetInstance()->SetRemoteMessage(std::move(remoteCallback));
}
void JSButton::JSBind(BindingTarget globalObj)
{
JSClass<JSButton>::Declare("Button");
JSClass<JSButton>::StaticMethod("fontColor", &JSButton::SetTextColor, MethodOptions::NONE);
JSClass<JSButton>::StaticMethod("fontSize", &JSButton::SetFontSize, MethodOptions::NONE);
JSClass<JSButton>::StaticMethod("fontWeight", &JSButton::SetFontWeight, MethodOptions::NONE);
JSClass<JSButton>::StaticMethod("fontStyle", &JSButton::SetFontStyle, MethodOptions::NONE);
JSClass<JSButton>::StaticMethod("fontFamily", &JSButton::SetFontFamily, MethodOptions::NONE);
JSClass<JSButton>::StaticMethod("type", &JSButton::SetType, MethodOptions::NONE);
JSClass<JSButton>::StaticMethod("stateEffect", &JSButton::SetStateEffect);
JSClass<JSButton>::StaticMethod("labelStyle", &JSButton::SetLableStyle, MethodOptions::NONE);
JSClass<JSButton>::StaticMethod("onClick", &JSButton::JsOnClick);
JSClass<JSButton>::StaticMethod("remoteMessage", &JSButton::JsRemoteMessage);
JSClass<JSButton>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
JSClass<JSButton>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
JSClass<JSButton>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
JSClass<JSButton>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
JSClass<JSButton>::StaticMethod("backgroundColor", &JSButton::JsBackgroundColor);
JSClass<JSButton>::StaticMethod("width", &JSButton::JsWidth);
JSClass<JSButton>::StaticMethod("height", &JSButton::JsHeight);
JSClass<JSButton>::StaticMethod("aspectRatio", &JSButton::JsAspectRatio);
JSClass<JSButton>::StaticMethod("borderRadius", &JSButton::JsRadius);
JSClass<JSButton>::StaticMethod("border", &JSButton::JsBorder);
JSClass<JSButton>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
JSClass<JSButton>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
JSClass<JSButton>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
JSClass<JSButton>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
JSClass<JSButton>::StaticMethod("size", &JSButton::JsSize);
JSClass<JSButton>::StaticMethod("padding", &JSButton::JsPadding);
JSClass<JSButton>::StaticMethod("buttonStyle", &JSButton::SetButtonStyle);
JSClass<JSButton>::StaticMethod("controlSize", &JSButton::SetControlSize);
JSClass<JSButton>::StaticMethod("role", &JSButton::SetRole);
JSClass<JSButton>::StaticMethod("createWithLabel", &JSButton::CreateWithLabel, MethodOptions::NONE);
JSClass<JSButton>::StaticMethod("createWithChild", &JSButton::CreateWithChild, MethodOptions::NONE);
JSClass<JSButton>::StaticMethod("minFontScale", &JSButton::SetMinFontScale);
JSClass<JSButton>::StaticMethod("maxFontScale", &JSButton::SetMaxFontScale);
JSClass<JSButton>::InheritAndBind<JSContainerBase>(globalObj);
}
void JSButton::CreateWithLabel(const JSCallbackInfo& info)
{
std::list<RefPtr<Component>> buttonChildren;
CreateWithPara para = ParseCreatePara(info, true);
ButtonModel::GetInstance()->CreateWithLabel(para, buttonChildren);
ButtonModel::GetInstance()->Create(para, buttonChildren);
if (SystemProperties::ConfigChangePerform()) {
RefPtr<ResourceObject> resObj;
std::string label;
ParseJsString(info[0], label, resObj);
ButtonModel::GetInstance()->CreateWithStringResourceObj(resObj, ButtonStringType::LABEL);
}
isLabelButton_ = true;
auto buttonRole = para.buttonRole.value_or(ButtonRole::NORMAL);
auto buttonStyleMode = para.buttonStyleMode.value_or(ButtonStyleMode::EMPHASIZE);
JSButtonTheme::ApplyTheme(buttonRole, buttonStyleMode, isLabelButton_);
ButtonModel::GetInstance()->SetCreateWithLabel(true);
}
void JSButton::CreateWithChild(const JSCallbackInfo& info)
{
CreateWithPara para = ParseCreatePara(info, false);
ButtonModel::GetInstance()->CreateWithChild(para);
isLabelButton_ = false;
auto buttonRole = para.buttonRole.value_or(ButtonRole::NORMAL);
auto buttonStyleMode = para.buttonStyleMode.value_or(ButtonStyleMode::EMPHASIZE);
JSButtonTheme::ApplyTheme(buttonRole, buttonStyleMode, isLabelButton_);
ButtonModel::GetInstance()->SetCreateWithLabel(false);
}
void JSButton::JsPadding(const JSCallbackInfo& info)
{
NG::PaddingProperty paddingNew = GetNewPadding(info);
Edge paddingOld = Edge(GetOldPadding(info));
ButtonModel::GetInstance()->Padding(paddingNew, paddingOld);
}
Edge JSButton::GetOldPadding(const JSCallbackInfo& info)
{
Edge padding;
if (info[0]->IsNumber()) {
CalcDimension edgeValue;
if (ParseJsDimensionVp(info[0], edgeValue)) {
padding = Edge(edgeValue);
}
} else if (info[0]->IsObject()) {
JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
CalcDimension left = CalcDimension(0.0, DimensionUnit::VP);
CalcDimension top = CalcDimension(0.0, DimensionUnit::VP);
CalcDimension right = CalcDimension(0.0, DimensionUnit::VP);
CalcDimension bottom = CalcDimension(0.0, DimensionUnit::VP);
if (jsObj->HasProperty("top") || jsObj->HasProperty("bottom") || jsObj->HasProperty("left") ||
jsObj->HasProperty("right")) {
ParseJsDimensionVp(jsObj->GetProperty("left"), left);
ParseJsDimensionVp(jsObj->GetProperty("top"), top);
ParseJsDimensionVp(jsObj->GetProperty("right"), right);
ParseJsDimensionVp(jsObj->GetProperty("bottom"), bottom);
}
padding = Edge(left, top, right, bottom);
}
return padding;
}
NG::PaddingProperty JSButton::GetNewPadding(const JSCallbackInfo& info)
{
NG::PaddingProperty padding = { NG::CalcLength(0.0), NG::CalcLength(0.0), NG::CalcLength(0.0), NG::CalcLength(0.0),
std::nullopt, std::nullopt };
if (isLabelButton_) {
auto buttonTheme = GetTheme<ButtonTheme>();
CHECK_NULL_RETURN(buttonTheme, padding);
auto defaultPadding = buttonTheme->GetPadding();
padding = { NG::CalcLength(defaultPadding.Left()), NG::CalcLength(defaultPadding.Right()),
NG::CalcLength(defaultPadding.Top()), NG::CalcLength(defaultPadding.Bottom()), std::nullopt, std::nullopt };
}
if (info[0]->IsObject()) {
CommonCalcDimension commonCalcDimension;
JSRef<JSObject> paddingObj = JSRef<JSObject>::Cast(info[0]);
JSViewAbstract::ParseCommonMarginOrPaddingCorner(paddingObj, commonCalcDimension);
if (commonCalcDimension.left.has_value() || commonCalcDimension.right.has_value() ||
commonCalcDimension.top.has_value() || commonCalcDimension.bottom.has_value()) {
return SetPaddings(commonCalcDimension.top, commonCalcDimension.bottom, commonCalcDimension.left,
commonCalcDimension.right);
}
}
CalcDimension length(-1);
ParseJsDimensionVp(info[0], length);
if (length.IsNonNegative()) {
padding.SetEdges(NG::CalcLength(length));
}
return padding;
}
NG::PaddingProperty JSButton::SetPaddings(const std::optional<CalcDimension>& top,
const std::optional<CalcDimension>& bottom, const std::optional<CalcDimension>& left,
const std::optional<CalcDimension>& right)
{
NG::PaddingProperty paddings;
if (top.has_value()) {
if (top.value().Unit() == DimensionUnit::CALC) {
paddings.top =
NG::CalcLength(top.value().IsNonNegative() ? top.value().CalcValue() : CalcDimension().CalcValue());
} else {
paddings.top = NG::CalcLength(top.value().IsNonNegative() ? top.value() : CalcDimension());
}
}
if (bottom.has_value()) {
if (bottom.value().Unit() == DimensionUnit::CALC) {
paddings.bottom = NG::CalcLength(
bottom.value().IsNonNegative() ? bottom.value().CalcValue() : CalcDimension().CalcValue());
} else {
paddings.bottom = NG::CalcLength(bottom.value().IsNonNegative() ? bottom.value() : CalcDimension());
}
}
if (left.has_value()) {
if (left.value().Unit() == DimensionUnit::CALC) {
paddings.left =
NG::CalcLength(left.value().IsNonNegative() ? left.value().CalcValue() : CalcDimension().CalcValue());
} else {
paddings.left = NG::CalcLength(left.value().IsNonNegative() ? left.value() : CalcDimension());
}
}
if (right.has_value()) {
if (right.value().Unit() == DimensionUnit::CALC) {
paddings.right =
NG::CalcLength(right.value().IsNonNegative() ? right.value().CalcValue() : CalcDimension().CalcValue());
} else {
paddings.right = NG::CalcLength(right.value().IsNonNegative() ? right.value() : CalcDimension());
}
}
return paddings;
}
void JSButton::JsOnClick(const JSCallbackInfo& info)
{
if (info[0]->IsUndefined() && IsDisableEventVersion()) {
ViewAbstractModel::GetInstance()->DisableOnClick();
return;
}
if (!info[0]->IsFunction()) {
return;
}
auto jsOnClickFunc = AceType::MakeRefPtr<JsClickFunction>(JSRef<JSFunc>::Cast(info[0]));
WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
auto onTap = [execCtx = info.GetExecutionContext(), func = jsOnClickFunc, node = targetNode](GestureEvent& info) {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
ACE_SCORING_EVENT("onClick");
PipelineContext::SetCallBackNode(node);
func->Execute(execCtx.vm_, info);
#if !defined(PREVIEW) && defined(OHOS_PLATFORM)
JSInteractableView::ReportClickEvent(node);
#endif
};
auto onClick = [execCtx = info.GetExecutionContext(), func = jsOnClickFunc, node = targetNode](
const ClickInfo* info) {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
ACE_SCORING_EVENT("onClick");
PipelineContext::SetCallBackNode(node);
func->Execute(*info);
#if !defined(PREVIEW) && defined(OHOS_PLATFORM)
JSInteractableView::ReportClickEvent(node);
#endif
};
double distanceThreshold = std::numeric_limits<double>::infinity();
if (info.Length() > 1 && info[1]->IsNumber()) {
distanceThreshold = info[1]->ToNumber<double>();
distanceThreshold = Dimension(distanceThreshold, DimensionUnit::VP).ConvertToPx();
}
ButtonModel::GetInstance()->OnClick(std::move(onTap), std::move(onClick), distanceThreshold);
}
void JSButton::JsBackgroundColor(const JSCallbackInfo& info)
{
Color backgroundColor;
RefPtr<ResourceObject> resObj;
bool colorFlag = ParseJsColor(info[0], backgroundColor, resObj);
if (!colorFlag) {
auto buttonTheme = GetTheme<ButtonTheme>();
if (buttonTheme) {
backgroundColor = buttonTheme->GetBgColor();
}
}
if (SystemProperties::ConfigChangePerform()) {
if (!NG::ViewStackProcessor::GetInstance()->IsCurrentVisualStateProcess()) {
return;
}
ButtonModel::GetInstance()->CreateWithColorResourceObj(resObj, ButtonColorType::BACKGROUND_COLOR);
}
ButtonModel::GetInstance()->BackgroundColor(backgroundColor, colorFlag);
info.ReturnSelf();
}
void JSButton::JsWidth(const JSCallbackInfo& info)
{
JSViewAbstract::JsWidth(info);
CalcDimension value = GetSizeValue(info);
if (LessNotEqual(value.Value(), 0.0)) {
return;
}
ButtonModel::GetInstance()->SetWidth(value);
}
void JSButton::JsHeight(const JSCallbackInfo& info)
{
JSViewAbstract::JsHeight(info);
CalcDimension value = GetSizeValue(info);
if (LessNotEqual(value.Value(), 0.0)) {
return;
}
ButtonModel::GetInstance()->SetHeight(value);
}
void JSButton::JsAspectRatio(const JSCallbackInfo& info)
{
JSViewAbstract::JsAspectRatio(info);
double value = 0.0;
if (!ParseJsDouble(info[0], value)) {
return;
}
ButtonModel::GetInstance()->SetAspectRatio(value);
}
void JSButton::JsSize(const JSCallbackInfo& info)
{
if (!info[0]->IsObject()) {
JSViewAbstract::JsWidth(JSVal::Undefined());
JSViewAbstract::JsHeight(JSVal::Undefined());
return;
}
JSRef<JSObject> sizeObj = JSRef<JSObject>::Cast(info[0]);
JSViewAbstract::JsWidth(sizeObj->GetProperty("width"));
JSViewAbstract::JsHeight(sizeObj->GetProperty("height"));
}
void JSButton::JsRadius(const JSCallbackInfo& info)
{
JsRadius(info[0]);
SetRenderStrategy(info);
}
void JSButton::JsRadius(const JSRef<JSVal>& jsValue)
{
CalcDimension radius;
if (ParseJsDimensionVpNG(jsValue, radius)) {
ButtonModel::GetInstance()->SetBorderRadius(radius);
} else if (jsValue->IsObject() && ((JSRef<JSObject>::Cast(jsValue)->GetPropertyValue<int32_t>(
"type", UNKNOWN_RESOURCE_TYPE)) == UNKNOWN_RESOURCE_TYPE)) {
JSRef<JSObject> object = JSRef<JSObject>::Cast(jsValue);
std::optional<CalcDimension> radiusTopLeft;
std::optional<CalcDimension> radiusTopRight;
std::optional<CalcDimension> radiusBottomLeft;
std::optional<CalcDimension> radiusBottomRight;
if (ParseAllBorderRadius(object, radiusTopLeft, radiusTopRight, radiusBottomLeft, radiusBottomRight)) {
ButtonModel::GetInstance()->SetLocalizedBorderRadius(
radiusTopLeft, radiusTopRight, radiusBottomLeft, radiusBottomRight);
} else {
ButtonModel::GetInstance()->SetBorderRadius(
radiusTopLeft, radiusTopRight, radiusBottomLeft, radiusBottomRight);
}
} else {
ButtonModel::GetInstance()->ResetBorderRadius();
}
}
void JSButton::JsBorder(const JSCallbackInfo& info)
{
JSViewAbstract::JsBorder(info);
if (!info[0]->IsObject()) {
return;
}
JSRef<JSObject> object = JSRef<JSObject>::Cast(info[0]);
CalcDimension borderRadius;
auto valueRadius = object->GetProperty("radius");
JsRadius(valueRadius);
}
CalcDimension JSButton::GetSizeValue(const JSCallbackInfo& info)
{
CalcDimension value;
if (!ParseJsDimensionVp(info[0], value)) {
return { -1.0 };
}
return value;
}
CreateWithPara JSButton::ParseCreatePara(const JSCallbackInfo& info, bool hasLabel)
{
std::string label;
CreateWithPara para;
para.parseSuccess = false;
para.optionSetFirst = false;
if (info.Length() < 1) {
para.label = label;
return para;
}
uint32_t optionIndex = 0;
if (hasLabel) {
para.parseSuccess = ParseJsString(info[0], label);
if (para.parseSuccess) {
if (info[0]->IsObject() && JSRef<JSObject>::Cast(info[0])->HasProperty("id")) {
optionIndex++;
} else if (info[0]->IsString()) {
optionIndex++;
}
}
para.label = label;
}
if (optionIndex >= info.Length() || !info[optionIndex]->IsObject()) {
return para;
}
if (optionIndex == 0) {
para.optionSetFirst = true;
}
JSRef<JSObject> optionObj = JSRef<JSObject>::Cast(info[optionIndex]);
if (optionObj->GetProperty(JSButton::TYPE)->IsNumber()) {
para.type = static_cast<ButtonType>(optionObj->GetProperty(JSButton::TYPE)->ToNumber<int32_t>());
}
if (optionObj->GetProperty(JSButton::STATE_EFFECT)->IsBoolean()) {
para.stateEffect = optionObj->GetProperty(JSButton::STATE_EFFECT)->ToBoolean();
}
if (optionObj->HasProperty(JSButton::BUTTON_STYLE)) {
para.buttonStyleMode = ButtonStyleMode::EMPHASIZE;
}
if (optionObj->GetProperty(JSButton::BUTTON_STYLE)->IsNumber()) {
auto styleModeIntValue = optionObj->GetProperty(JSButton::BUTTON_STYLE)->ToNumber<int32_t>();
if (styleModeIntValue >= static_cast<int32_t>(ButtonStyleMode::NORMAL) &&
styleModeIntValue <= static_cast<int32_t>(ButtonStyleMode::TEXT)) {
para.buttonStyleMode = static_cast<ButtonStyleMode>(styleModeIntValue);
}
}
if (optionObj->HasProperty(JSButton::CONTROL_SIZE)) {
para.controlSize = ControlSize::NORMAL;
}
if (optionObj->GetProperty(JSButton::CONTROL_SIZE)->IsNumber()) {
auto controlSizeIntValue = optionObj->GetProperty(JSButton::CONTROL_SIZE)->ToNumber<int32_t>();
if (controlSizeIntValue >= static_cast<int32_t>(ControlSize::SMALL) &&
controlSizeIntValue <= static_cast<int32_t>(ControlSize::NORMAL)) {
para.controlSize = static_cast<ControlSize>(controlSizeIntValue);
}
}
ParseButtonRole(optionObj, para);
return para;
}
void JSButton::ParseButtonRole(const JSRef<JSObject>& optionObj, CreateWithPara& param)
{
if (optionObj->HasProperty(JSButton::ROLE)) {
param.buttonRole = ButtonRole::NORMAL;
}
if (optionObj->GetProperty(JSButton::ROLE)->IsNumber()) {
auto buttonRoleIntValue = optionObj->GetProperty(JSButton::ROLE)->ToNumber<int32_t>();
if (buttonRoleIntValue >= static_cast<int32_t>(ButtonRole::NORMAL) &&
buttonRoleIntValue <= static_cast<int32_t>(ButtonRole::ERROR)) {
param.buttonRole = static_cast<ButtonRole>(buttonRoleIntValue);
}
}
}
}