/* * Copyright (c) 2021-2023 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_text.h" #include #include #include #include #if !defined(PREVIEW) && defined(OHOS_PLATFORM) #include "core/components_ng/pattern/text/text_layout_property.h" #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 "bridge/common/utils/utils.h" #include "bridge/declarative_frontend/ark_theme/theme_apply/js_theme_utils.h" #include "bridge/declarative_frontend/engine/functions/js_click_function.h" #include "bridge/declarative_frontend/engine/functions/js_drag_function.h" #include "bridge/declarative_frontend/engine/functions/js_function.h" #include "bridge/declarative_frontend/engine/jsi/js_ui_index.h" #include "bridge/declarative_frontend/jsview/js_interactable_view.h" #include "bridge/declarative_frontend/jsview/js_layout_manager.h" #include "bridge/declarative_frontend/jsview/js_text.h" #include "bridge/declarative_frontend/jsview/js_utils.h" #include "bridge/declarative_frontend/jsview/js_view_abstract.h" #include "bridge/declarative_frontend/jsview/js_view_common_def.h" #include "bridge/declarative_frontend/jsview/models/text_model_impl.h" #include "bridge/declarative_frontend/style_string/js_span_string.h" #include "bridge/declarative_frontend/view_stack_processor.h" #include "core/common/container.h" #include "core/components/common/layout/constants.h" #include "core/components/common/properties/text_style_parser.h" #include "core/components_ng/pattern/text/text_model_ng.h" #include "core/event/ace_event_handler.h" #include "core/pipeline/pipeline_base.h" namespace OHOS::Ace { std::unique_ptr TextModel::instance_ = nullptr; std::mutex TextModel::mutex_; TextModel* TextModel::GetInstance() { #ifdef NG_BUILD static NG::TextModelNG instance; return &instance; #else if (Container::IsCurrentUseNewPipeline()) { static NG::TextModelNG instance; return &instance; } else { static Framework::TextModelImpl instance; return &instance; } #endif } } // namespace OHOS::Ace namespace OHOS::Ace::Framework { namespace { const std::vector TEXT_CASES = { TextCase::NORMAL, TextCase::LOWERCASE, TextCase::UPPERCASE }; const std::vector TEXT_OVERFLOWS = { TextOverflow::NONE, TextOverflow::CLIP, TextOverflow::ELLIPSIS, TextOverflow::MARQUEE }; const std::vector FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC }; const std::vector TEXT_ALIGNS = { TextAlign::START, TextAlign::CENTER, TextAlign::END, TextAlign::JUSTIFY, TextAlign::LEFT, TextAlign::RIGHT }; const std::vector HEIGHT_ADAPTIVE_POLICY = { TextHeightAdaptivePolicy::MAX_LINES_FIRST, TextHeightAdaptivePolicy::MIN_FONT_SIZE_FIRST, TextHeightAdaptivePolicy::LAYOUT_CONSTRAINT_FIRST }; const std::vector LINE_BREAK_STRATEGY_TYPES = { LineBreakStrategy::GREEDY, LineBreakStrategy::HIGH_QUALITY, LineBreakStrategy::BALANCED }; const std::vector ELLIPSIS_MODALS = { EllipsisMode::HEAD, EllipsisMode::MIDDLE, EllipsisMode::TAIL }; const std::vector TEXT_SELECTABLE_MODE = { TextSelectableMode::SELECTABLE_UNFOCUSABLE, TextSelectableMode::SELECTABLE_FOCUSABLE, TextSelectableMode::UNSELECTABLE }; constexpr TextDecorationStyle DEFAULT_TEXT_DECORATION_STYLE = TextDecorationStyle::SOLID; const int32_t DEFAULT_VARIABLE_FONT_WEIGHT = 400; }; // namespace void JSText::SetWidth(const JSCallbackInfo& info) { JSViewAbstract::JsWidth(info); TextModel::GetInstance()->OnSetWidth(); } void JSText::SetHeight(const JSCallbackInfo& info) { JSViewAbstract::JsHeight(info); TextModel::GetInstance()->OnSetHeight(); } void JSText::SetFont(const JSCallbackInfo& info) { Font font; auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck(); CHECK_NULL_VOID(pipelineContext); auto theme = pipelineContext->GetTheme(); CHECK_NULL_VOID(theme); font.fontSize = theme->GetTextStyle().GetFontSize(); font.fontWeight = theme->GetTextStyle().GetFontWeight(); font.fontFamilies = theme->GetTextStyle().GetFontFamilies(); font.fontStyle = theme->GetTextStyle().GetFontStyle(); GetFontInfo(info, font); TextModel::GetInstance()->SetFont(font); if (info.Length() < 2) { // 2 : two args return; } auto tmpInfo = info[1]; if (!tmpInfo->IsObject()) { return; } auto paramObject = JSRef::Cast(tmpInfo); auto enableVariableFontWeight = paramObject->GetProperty("enableVariableFontWeight"); if (enableVariableFontWeight->IsBoolean()) { TextModel::GetInstance()->SetEnableVariableFontWeight(enableVariableFontWeight->ToBoolean()); } else { TextModel::GetInstance()->SetEnableVariableFontWeight(false); } } void JSText::GetFontInfo(const JSCallbackInfo& info, Font& font) { auto tmpInfo = info[0]; if (!tmpInfo->IsObject()) { return; } auto paramObject = JSRef::Cast(tmpInfo); auto fontSize = paramObject->GetProperty(static_cast(ArkUIIndex::SIZE)); CalcDimension size; RefPtr fontSizeResObj; UnRegisterResource("FontSize"); if (ParseJsDimensionFpNG(fontSize, size, fontSizeResObj, false) && size.IsNonNegative()) { font.fontSize = size; if (SystemProperties::ConfigChangePerform() && fontSizeResObj) { RegisterResource("FontSize", fontSizeResObj, size); } } std::string weight; auto fontWeight = paramObject->GetProperty(static_cast(ArkUIIndex::WEIGHT)); if (!fontWeight->IsNull()) { int32_t variableFontWeight = DEFAULT_VARIABLE_FONT_WEIGHT; ParseJsInt32(fontWeight, variableFontWeight); TextModel::GetInstance()->SetVariableFontWeight(variableFontWeight); if (fontWeight->IsNumber()) { weight = std::to_string(fontWeight->ToNumber()); } else { JSContainerBase::ParseJsString(fontWeight, weight); } font.fontWeight = ConvertStrToFontWeight(weight); } auto fontFamily = paramObject->GetProperty(static_cast(ArkUIIndex::FAMILY)); if (!fontFamily->IsNull()) { std::vector fontFamilies; RefPtr fontFamiliesResObj; UnRegisterResource("FontFamily"); if (JSContainerBase::ParseJsFontFamilies(fontFamily, fontFamilies, fontFamiliesResObj)) { font.fontFamilies = fontFamilies; if (SystemProperties::ConfigChangePerform() && fontFamiliesResObj) { RegisterResource>( "FontFamily", fontFamiliesResObj, fontFamilies); } } } auto style = paramObject->GetProperty(static_cast(ArkUIIndex::STYLE)); if (!style->IsNull() || style->IsNumber()) { font.fontStyle = static_cast(style->ToNumber()); } } void JSText::SetFontSize(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } CalcDimension fontSize; RefPtr resObj; UnRegisterResource("FontSize"); JSRef args = info[0]; if (!ParseJsDimensionFpNG(args, fontSize, resObj, false) || fontSize.IsNegative()) { auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck(); CHECK_NULL_VOID(pipelineContext); auto theme = pipelineContext->GetTheme(); CHECK_NULL_VOID(theme); fontSize = theme->GetTextStyle().GetFontSize(); TextModel::GetInstance()->SetFontSize(fontSize); return; } TextModel::GetInstance()->SetFontSize(fontSize); if (SystemProperties::ConfigChangePerform() && resObj) { RegisterResource("FontSize", resObj, fontSize); } } void JSText::SetFontWeight(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } JSRef args = info[0]; std::string fontWeight; int32_t variableFontWeight = DEFAULT_VARIABLE_FONT_WEIGHT; ParseJsInt32(args, variableFontWeight); TextModel::GetInstance()->SetVariableFontWeight(variableFontWeight); if (args->IsNumber()) { fontWeight = args->ToString(); } else { ParseJsString(args, fontWeight); } TextModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(fontWeight)); if (info.Length() < 2) { // 2 : two args return; } auto tmpInfo = info[1]; if (!tmpInfo->IsObject()) { return; } auto paramObject = JSRef::Cast(tmpInfo); auto enableVariableFontWeight = paramObject->GetProperty("enableVariableFontWeight"); if (enableVariableFontWeight->IsBoolean()) { TextModel::GetInstance()->SetEnableVariableFontWeight(enableVariableFontWeight->ToBoolean()); } else { TextModel::GetInstance()->SetEnableVariableFontWeight(false); } } void JSText::SetMinFontScale(const JSCallbackInfo& info) { double minFontScale; RefPtr resourceObject; UnRegisterResource("MinFontScale"); if (info.Length() < 1 || !ParseJsDouble(info[0], minFontScale, resourceObject)) { return; } auto minFontScaleValue = static_cast(std::clamp(minFontScale, 0.0, 1.0)); if (SystemProperties::ConfigChangePerform() && resourceObject) { RegisterResource("MinFontScale", resourceObject, minFontScaleValue); } TextModel::GetInstance()->SetMinFontScale(minFontScaleValue); } void JSText::SetMaxFontScale(const JSCallbackInfo& info) { double maxFontScale; RefPtr resourceObject; UnRegisterResource("MaxFontScale"); if (info.Length() < 1 || !ParseJsDouble(info[0], maxFontScale, resourceObject)) { return; } auto maxFontScaleValue = static_cast(std::max(maxFontScale, 1.0)); if (SystemProperties::ConfigChangePerform() && resourceObject) { RegisterResource("MaxFontScale", resourceObject, maxFontScaleValue); } TextModel::GetInstance()->SetMaxFontScale(maxFontScaleValue); } void JSText::SetForegroundColor(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } ForegroundColorStrategy strategy; if (ParseJsColorStrategy(info[0], strategy)) { TextModel::GetInstance()->SetTextColor(Color::FOREGROUND); ViewAbstractModel::GetInstance()->SetForegroundColorStrategy(strategy); return; } SetTextColor(info); } void JSText::SetTextColor(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } Color textColor; RefPtr resourceObject; UnRegisterResource("TextColor"); JSRef args = info[0]; if (!ParseJsColor(args, textColor, resourceObject)) { TextModel::GetInstance()->ResetTextColor(); return; } if (SystemProperties::ConfigChangePerform() && resourceObject) { RegisterResource("TextColor", resourceObject, textColor); } TextModel::GetInstance()->SetTextColor(textColor); } void JSText::SetTextShadow(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } std::vector shadows; JSRef args = info[0]; ParseTextShadowFromShadowObject(args, shadows); TextModel::GetInstance()->SetTextShadow(shadows); } void JSText::SetTextOverflow(const JSCallbackInfo& info) { do { auto tmpInfo = info[0]; if (!tmpInfo->IsObject()) { break; } JSRef obj = JSRef::Cast(tmpInfo); JSRef overflowValue = obj->GetProperty("overflow"); if (!overflowValue->IsNumber() && !overflowValue->IsUndefined()) { break; } auto overflow = overflowValue->ToNumber(); if(overflowValue->IsUndefined()) { overflow = 0; } else if (overflow < 0 || overflow >= static_cast(TEXT_OVERFLOWS.size())) { break; } TextModel::GetInstance()->SetTextOverflow(TEXT_OVERFLOWS[overflow]); } while (false); info.SetReturnValue(info.This()); } void JSText::SetWordBreak(const JSCallbackInfo& info) { JSRef args = info[0]; if (!args->IsNumber()) { return; } uint32_t index = args->ToNumber(); if (index < WORD_BREAK_TYPES.size()) { TextModel::GetInstance()->SetWordBreak(WORD_BREAK_TYPES[index]); } } void JSText::SetEllipsisMode(const JSCallbackInfo& info) { JSRef args = info[0]; if (!args->IsNumber()) { return; } uint32_t index = args->ToNumber(); if (index < ELLIPSIS_MODALS.size()) { TextModel::GetInstance()->SetEllipsisMode(ELLIPSIS_MODALS[index]); } } void JSText::SetLineBreakStrategy(const JSCallbackInfo& info) { if (info.Length() < 1) { TextModel::GetInstance()->SetLineBreakStrategy(LineBreakStrategy::GREEDY); return; } if (!info[0]->IsNumber()) { TextModel::GetInstance()->SetLineBreakStrategy(LineBreakStrategy::GREEDY); return; } auto index = info[0]->ToNumber(); if (index < 0 || index >= static_cast(LINE_BREAK_STRATEGY_TYPES.size())) { TextModel::GetInstance()->SetLineBreakStrategy(LineBreakStrategy::GREEDY); return; } TextModel::GetInstance()->SetLineBreakStrategy(LINE_BREAK_STRATEGY_TYPES[index]); } void JSText::SetTextSelection(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } JSRef argsStartIndex = info[0]; JSRef argsEndIndex = info[1]; if (!argsStartIndex->IsNumber() || !argsEndIndex->IsNumber()) { return; } auto startIndex = argsStartIndex->ToNumber(); auto endIndex = argsEndIndex->ToNumber(); TextModel::GetInstance()->SetTextSelection(startIndex, endIndex); } void JSText::SetTextCaretColor(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } Color caretColor; RefPtr resObj; UnRegisterResource("TextCaretColor"); if (!ParseJsColor(info[0], caretColor, resObj)) { auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck(); CHECK_NULL_VOID(pipelineContext); auto theme = pipelineContext->GetTheme(); CHECK_NULL_VOID(theme); caretColor = theme->GetCaretColor(); } if (SystemProperties::ConfigChangePerform() && resObj) { RegisterResource("TextCaretColor", resObj, caretColor); } TextModel::GetInstance()->SetTextCaretColor(caretColor); } void JSText::SetSelectedBackgroundColor(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } Color selectedColor; RefPtr resObj; UnRegisterResource("SelectedBackgroundColor"); if (!ParseJsColor(info[0], selectedColor, resObj)) { auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck(); CHECK_NULL_VOID(pipelineContext); auto theme = pipelineContext->GetTheme(); CHECK_NULL_VOID(theme); selectedColor = theme->GetSelectedColor(); } if (SystemProperties::ConfigChangePerform() && resObj) { RegisterResource("SelectedBackgroundColor", resObj, selectedColor); } // Alpha = 255 means opaque if (selectedColor.GetAlpha() == JSThemeUtils::DEFAULT_ALPHA) { // Default setting of 20% opacity selectedColor = selectedColor.ChangeOpacity(JSThemeUtils::DEFAULT_OPACITY); } TextModel::GetInstance()->SetSelectedBackgroundColor(selectedColor); } void JSText::SetTextSelectableMode(const JSCallbackInfo& info) { if (info.Length() < 1) { TextModel::GetInstance()->SetTextSelectableMode(TextSelectableMode::SELECTABLE_UNFOCUSABLE); return; } if (!info[0]->IsNumber()) { TextModel::GetInstance()->SetTextSelectableMode(TextSelectableMode::SELECTABLE_UNFOCUSABLE); return; } auto index = info[0]->ToNumber(); if (index < 0 || index >= static_cast(TEXT_SELECTABLE_MODE.size())) { TextModel::GetInstance()->SetTextSelectableMode(TextSelectableMode::SELECTABLE_UNFOCUSABLE); return; } TextModel::GetInstance()->SetTextSelectableMode(TEXT_SELECTABLE_MODE[index]); } void JSText::SetMaxLines(const JSCallbackInfo& info) { JSRef args = info[0]; auto value = Infinity(); if (args->ToString() != "Infinity") { ParseJsInt32(args, value); } TextModel::GetInstance()->SetMaxLines(value); } void JSText::SetTextIndent(const JSCallbackInfo& info) { CalcDimension value; RefPtr resObj; JSRef args = info[0]; if (!ParseJsDimensionFpNG(args, value, resObj)) { value.Reset(); TextModel::GetInstance()->SetTextIndent(value); UnRegisterResource("TextIndent"); return; } if (SystemProperties::ConfigChangePerform() && resObj) { RegisterResource("TextIndent", resObj, value); } TextModel::GetInstance()->SetTextIndent(value); } void JSText::SetFontStyle(int32_t value) { if (value < 0 || value >= static_cast(FONT_STYLES.size())) { if (!(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE))) { return; } value = 0; } TextModel::GetInstance()->SetItalicFontStyle(FONT_STYLES[value]); } void JSText::SetTextAlign(int32_t value) { if (value < 0 || value >= static_cast(TEXT_ALIGNS.size())) { if (!(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE))) { return; } value = 0; } TextModel::GetInstance()->SetTextAlign(TEXT_ALIGNS[value]); } void JSText::SetAlign(const JSCallbackInfo& info) { JSViewAbstract::JsAlign(info); JSRef args = info[0]; if (!args->IsNumber()) { return; } TextModel::GetInstance()->OnSetAlign(); } void JSText::SetLineHeight(const JSCallbackInfo& info) { CalcDimension value; JSRef args = info[0]; RefPtr resObj; if (!ParseJsDimensionFpNG(args, value, resObj)) { value.Reset(); TextModel::GetInstance()->SetLineHeight(value); UnRegisterResource("LineHeight"); return; } if (SystemProperties::ConfigChangePerform() && resObj) { RegisterResource("LineHeight", resObj, value); } if (value.IsNegative()) { value.Reset(); } TextModel::GetInstance()->SetLineHeight(value); } void JSText::SetLineSpacing(const JSCallbackInfo& info) { CalcDimension value; JSRef args = info[0]; UnRegisterResource("LineSpacing"); RefPtr resObj; if (!ParseLengthMetricsToPositiveDimension(args, value, resObj)) { value.Reset(); } if (value.IsNegative()) { value.Reset(); } TextModel::GetInstance()->SetLineSpacing(value); if (SystemProperties::ConfigChangePerform() && resObj) { RegisterResource("LineSpacing", resObj, value); } if (info.Length() < 2) { // 2 : two args TextModel::GetInstance()->SetIsOnlyBetweenLines(false); return; } auto jsonValue = info[1]; if (!jsonValue->IsObject()) { TextModel::GetInstance()->SetIsOnlyBetweenLines(false); return; } auto paramObject = JSRef::Cast(jsonValue); auto param = paramObject->GetProperty("onlyBetweenLines"); if (!param->IsBoolean() || param->IsUndefined() || param->IsNull()) { TextModel::GetInstance()->SetIsOnlyBetweenLines(false); } else { auto isOnlyBetweenLines = param->ToBoolean(); TextModel::GetInstance()->SetIsOnlyBetweenLines(isOnlyBetweenLines); } } void JSText::SetFontFamily(const JSCallbackInfo& info) { std::vector fontFamilies; RefPtr resObj; UnRegisterResource("FontFamily"); JSRef args = info[0]; ParseJsFontFamilies(args, fontFamilies, resObj); if (SystemProperties::ConfigChangePerform() && resObj) { RegisterResource>("FontFamily", resObj, fontFamilies); } TextModel::GetInstance()->SetFontFamily(fontFamilies); } void JSText::SetMinFontSize(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck(); CHECK_NULL_VOID(pipelineContext); auto theme = pipelineContext->GetTheme(); CHECK_NULL_VOID(theme); CalcDimension minFontSize = theme->GetTextStyle().GetAdaptMinFontSize(); JSRef args = info[0]; RefPtr resObj; if (!ParseJsDimensionFpNG(args, minFontSize, resObj, false)) { minFontSize = theme->GetTextStyle().GetAdaptMinFontSize(); TextModel::GetInstance()->SetAdaptMinFontSize(minFontSize); UnRegisterResource("AdaptMinFontSize"); return; } if (SystemProperties::ConfigChangePerform() && resObj) { RegisterResource("AdaptMinFontSize", resObj, minFontSize); } if (minFontSize.IsNegative()) { minFontSize = theme->GetTextStyle().GetAdaptMinFontSize(); } TextModel::GetInstance()->SetAdaptMinFontSize(minFontSize); } void JSText::SetMaxFontSize(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } auto pipelineContext = PipelineContext::GetCurrentContextSafelyWithCheck(); CHECK_NULL_VOID(pipelineContext); auto theme = pipelineContext->GetTheme(); CHECK_NULL_VOID(theme); CalcDimension maxFontSize = theme->GetTextStyle().GetAdaptMaxFontSize(); JSRef args = info[0]; RefPtr resObj; if (!ParseJsDimensionFpNG(args, maxFontSize, resObj, false)) { maxFontSize = theme->GetTextStyle().GetAdaptMaxFontSize(); TextModel::GetInstance()->SetAdaptMaxFontSize(maxFontSize); UnRegisterResource("AdaptMaxFontSize"); return; } if (maxFontSize.IsNegative()) { maxFontSize = theme->GetTextStyle().GetAdaptMaxFontSize(); } if (SystemProperties::ConfigChangePerform() && resObj) { RegisterResource("AdaptMaxFontSize", resObj, maxFontSize); } TextModel::GetInstance()->SetAdaptMaxFontSize(maxFontSize); } void JSText::SetLetterSpacing(const JSCallbackInfo& info) { CalcDimension value; JSRef args = info[0]; RefPtr resObj; UnRegisterResource("LetterSpacing"); if (!ParseJsDimensionFpNG(args, value, resObj, false)) { value.Reset(); TextModel::GetInstance()->SetLetterSpacing(value); return; } if (SystemProperties::ConfigChangePerform() && resObj) { RegisterResource("LetterSpacing", resObj, value); } TextModel::GetInstance()->SetLetterSpacing(value); } void JSText::SetTextCase(int32_t value) { if (value < 0 || value >= static_cast(TEXT_CASES.size())) { if (!(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE))) { return; } value = 0; } TextModel::GetInstance()->SetTextCase(TEXT_CASES[value]); } void JSText::SetBaselineOffset(const JSCallbackInfo& info) { CalcDimension value; JSRef args = info[0]; RefPtr resObj; if (!ParseJsDimensionFpNG(args, value, resObj, false)) { value.Reset(); TextModel::GetInstance()->SetBaselineOffset(value); UnRegisterResource("BaselineOffset"); return; } if (SystemProperties::ConfigChangePerform() && resObj) { RegisterResource("BaselineOffset", resObj, value); } TextModel::GetInstance()->SetBaselineOffset(value); } void JSText::SetDecoration(const JSCallbackInfo& info) { auto tmpInfo = info[0]; UnRegisterResource("TextDecorationColor"); if (tmpInfo->IsUndefined()) { TextModel::GetInstance()->SetTextDecoration(TextDecoration::NONE); info.ReturnSelf(); return; } if (!tmpInfo->IsObject()) { info.ReturnSelf(); return; } JSRef obj = JSRef::Cast(tmpInfo); JSRef typeValue = obj->GetProperty("type"); JSRef colorValue = obj->GetProperty("color"); JSRef styleValue = obj->GetProperty("style"); JSRef thicknessScaleValue = obj->GetProperty("thicknessScale"); TextDecoration textDecoration; if (typeValue->IsNumber()) { textDecoration = static_cast(typeValue->ToNumber()); } else { auto theme = GetTheme(); CHECK_NULL_VOID(theme); textDecoration = theme->GetTextDecoration(); } Color result; RefPtr resObj; if (!ParseJsColor(colorValue, result, resObj)) { auto theme = GetTheme(); CHECK_NULL_VOID(theme); if (Container::CurrentColorMode() == ColorMode::DARK) { result = theme->GetTextStyle().GetTextColor(); } else { result = theme->GetTextStyle().GetTextDecorationColor(); } } if (SystemProperties::ConfigChangePerform() && resObj) { RegisterResource("TextDecorationColor", resObj, result); } auto style = styleValue->IsNumber() ? styleValue->ToNumber() : static_cast(DEFAULT_TEXT_DECORATION_STYLE); float lineThicknessScale = thicknessScaleValue->IsNumber() ? thicknessScaleValue->ToNumber() : 1.0f; lineThicknessScale = lineThicknessScale < 0 ? 1.0f : lineThicknessScale; TextModel::GetInstance()->SetTextDecoration(textDecoration); TextModel::GetInstance()->SetTextDecorationColor(result); TextModel::GetInstance()->SetTextDecorationStyle(static_cast(style)); TextModel::GetInstance()->SetLineThicknessScale(lineThicknessScale); info.ReturnSelf(); } void JSText::SetHeightAdaptivePolicy(int32_t value) { if (value < 0 || value >= static_cast(HEIGHT_ADAPTIVE_POLICY.size())) { if (!(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE))) { return; } value = 0; } TextModel::GetInstance()->SetHeightAdaptivePolicy(HEIGHT_ADAPTIVE_POLICY[value]); } void JSText::JsOnClick(const JSCallbackInfo& info) { JSRef args = info[0]; if (Container::IsCurrentUseNewPipeline()) { if (args->IsUndefined() && IsDisableEventVersion()) { TextModel::GetInstance()->ClearOnClick(); return; } if (!args->IsFunction()) { return; } auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto jsOnClickFunc = AceType::MakeRefPtr(JSRef::Cast(args)); auto onClick = [execCtx = info.GetExecutionContext(), func = jsOnClickFunc, node = frameNode] (BaseEventInfo* info) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); auto* clickInfo = TypeInfoHelper::DynamicCast(info); ACE_SCORING_EVENT("Text.onClick"); PipelineContext::SetCallBackNode(node); func->Execute(*clickInfo); #if !defined(PREVIEW) && defined(OHOS_PLATFORM) std::u16string label = u""; auto frameNode = node.Upgrade(); if (frameNode) { auto pattern = frameNode->GetPattern(); CHECK_NULL_VOID(pattern); auto layoutProperty = pattern->GetLayoutProperty(); CHECK_NULL_VOID(layoutProperty); label = layoutProperty->GetContent().value_or(u""); } JSInteractableView::ReportClickEvent(node, label); #endif }; double distanceThreshold = std::numeric_limits::infinity(); if (info.Length() > 1 && info[1]->IsNumber()) { distanceThreshold = info[1]->ToNumber(); distanceThreshold = Dimension(distanceThreshold, DimensionUnit::VP).ConvertToPx(); } TextModel::GetInstance()->SetOnClick(std::move(onClick), distanceThreshold); auto focusHub = NG::ViewStackProcessor::GetInstance()->GetOrCreateMainFrameNodeFocusHub(); CHECK_NULL_VOID(focusHub); focusHub->SetFocusable(true, false); } else { JsOnClickWithoutNGBUILD(info); } } void JSText::JsOnClickWithoutNGBUILD(const JSCallbackInfo& info) { #ifndef NG_BUILD JSRef args = info[0]; if (args->IsFunction()) { auto inspector = ViewStackProcessor::GetInstance()->GetInspectorComposedComponent(); auto impl = inspector ? inspector->GetInspectorFunctionImpl() : nullptr; auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); RefPtr jsOnClickFunc = AceType::MakeRefPtr(JSRef::Cast(args)); auto onClickId = [execCtx = info.GetExecutionContext(), func = std::move(jsOnClickFunc), impl, node = frameNode](const BaseEventInfo* info) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); const auto* clickInfo = TypeInfoHelper::DynamicCast(info); auto newInfo = *clickInfo; if (impl) { impl->UpdateEventInfo(newInfo); } ACE_SCORING_EVENT("Text.onClick"); PipelineContext::SetCallBackNode(node); func->Execute(newInfo); }; double distanceThreshold = std::numeric_limits::infinity(); if (info.Length() > 1 && info[1]->IsNumber()) { distanceThreshold = info[1]->ToNumber(); distanceThreshold = Dimension(distanceThreshold, DimensionUnit::VP).ConvertToPx(); } TextModel::GetInstance()->SetOnClick(std::move(onClickId), distanceThreshold); } #endif } void JSText::JsRemoteMessage(const JSCallbackInfo& info) { JSInteractableView::JsCommonRemoteMessage(info); auto callback = JSInteractableView::GetRemoteMessageEventCallback(info); TextModel::GetInstance()->SetRemoteMessage(std::move(callback)); } void JSText::Create(const JSCallbackInfo& info) { std::u16string data; if (info.Length() <= 0) { TextModel::GetInstance()->Create(data); UnRegisterResource("Content"); return; } if (info[0]->IsObject() && JSRef::Cast(info[0])->Unwrap()) { auto *spanString = JSRef::Cast(info[0])->Unwrap(); if (spanString == nullptr) { return; } auto spanStringController = spanString->GetController(); if (spanStringController) { TextModel::GetInstance()->Create(spanStringController); } else { TextModel::GetInstance()->Create(data); } UnRegisterResource("Content"); } else { RefPtr resObj; auto ret = ParseJsString(info[0], data, resObj); UtfUtils::HandleInvalidUTF16(reinterpret_cast(data.data()), data.length(), 0); TextModel::GetInstance()->Create(data); UnRegisterResource("Content"); if (ret && SystemProperties::ConfigChangePerform() && resObj) { RegisterResource("Content", resObj, data); } } if (info.Length() <= 1 || !info[1]->IsObject()) { return; } JSTextController* jsController = nullptr; auto paramObject = JSRef::Cast(info[1]); auto controllerObj = paramObject->GetProperty("controller"); if (!controllerObj->IsUndefined() && !controllerObj->IsNull() && controllerObj->IsObject()) { jsController = JSRef::Cast(controllerObj)->Unwrap(); } RefPtr controller = TextModel::GetInstance()->GetTextController(); if (!controller) { TAG_LOGW(AceLogTag::ACE_TEXT, "JSText::Create controller is null"); } if (jsController) { jsController->SetController(controller); if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_FIFTEEN)) { auto styledString = jsController->GetStyledString(); if (styledString && controller) { controller->SetStyledString(styledString, false); jsController->ClearStyledString(); } } } } void JSText::SetCopyOption(const JSCallbackInfo& info) { if (info.Length() == 0) { return; } auto copyOptions = CopyOptions::None; auto tmpInfo = info[0]; if (tmpInfo->IsNumber()) { auto emunNumber = tmpInfo->ToNumber(); copyOptions = static_cast(emunNumber); } TextModel::GetInstance()->SetCopyOption(copyOptions); } void JSText::SetOnCopy(const JSCallbackInfo& info) { JSRef args = info[0]; CHECK_NULL_VOID(args->IsFunction()); JsEventCallback callback(info.GetExecutionContext(), JSRef::Cast(args)); TextModel::GetInstance()->SetOnCopy(std::move(callback)); } void JSText::JsOnDragStart(const JSCallbackInfo& info) { JSRef args = info[0]; CHECK_NULL_VOID(args->IsFunction()); RefPtr jsOnDragStartFunc = AceType::MakeRefPtr(JSRef::Cast(args)); WeakPtr frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onDragStart = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragStartFunc), targetNode = frameNode]( const RefPtr& info, const std::string& extraParams) -> NG::DragDropBaseInfo { NG::DragDropBaseInfo itemInfo; JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, itemInfo); PipelineContext::SetCallBackNode(targetNode); auto ret = func->Execute(info, extraParams); if (!ret->IsObject()) { return itemInfo; } auto node = ParseDragNode(ret); if (node) { itemInfo.node = node; return itemInfo; } auto builderObj = JSRef::Cast(ret); #if defined(PIXEL_MAP_SUPPORTED) auto pixmap = builderObj->GetProperty("pixelMap"); itemInfo.pixelMap = CreatePixelMapFromNapiValue(pixmap); #endif auto extraInfo = builderObj->GetProperty("extraInfo"); ParseJsString(extraInfo, itemInfo.extraInfo); node = ParseDragNode(builderObj->GetProperty("builder")); itemInfo.node = node; return itemInfo; }; TextModel::GetInstance()->SetOnDragStart(std::move(onDragStart)); } void JSText::JsFocusable(const JSCallbackInfo& info) { auto tmpInfo = info[0]; if (!tmpInfo->IsBoolean()) { return; } JSInteractableView::SetFocusable(tmpInfo->ToBoolean()); JSInteractableView::SetFocusNode(false); } void JSText::JsDraggable(const JSCallbackInfo& info) { auto tmpInfo = info[0]; if (!tmpInfo->IsBoolean()) { return; } ViewAbstractModel::GetInstance()->SetDraggable(tmpInfo->ToBoolean()); } void JSText::JsEnableDataDetector(const JSCallbackInfo& info) { if (info.Length() < 1) { LOGI("The argv is wrong, it is supposed to have at least 1 argument"); return; } auto tmpInfo = info[0]; if (!tmpInfo->IsBoolean()) { TextModel::GetInstance()->SetTextDetectEnable(false); return; } auto enable = tmpInfo->ToBoolean(); TextModel::GetInstance()->SetTextDetectEnable(enable); } void JSText::JsDataDetectorConfig(const JSCallbackInfo& info) { if (info.Length() < 1) { LOGI("The argv is wrong, it is supposed to have at least 1 argument"); return; } JSRef args = info[0]; if (!args->IsObject()) { return; } TextDetectConfig textDetectConfig; if (!ParseDataDetectorConfig(info, textDetectConfig)) { return; } TextModel::GetInstance()->SetTextDetectConfig(textDetectConfig); } void JSText::BindSelectionMenu(const JSCallbackInfo& info) { // TextSpanType NG::TextSpanType textSpanType = NG::TextSpanType::TEXT; bool isValidTextSpanType = true; JSRef argsSpanType = info[0]; if (argsSpanType->IsNumber()) { auto spanTypeId = argsSpanType->ToNumber(); isValidTextSpanType = NG::TextSpanTypeMapper::GetTextSpanTypeFromJsType(spanTypeId, textSpanType); } // Builder JSRef argsMenuObj = info[1]; if (!argsMenuObj->IsObject()) { return; } JSRef menuObj = JSRef::Cast(argsMenuObj); auto builder = menuObj->GetProperty("builder"); if (!builder->IsFunction()) { return; } auto builderFunc = AceType::MakeRefPtr(JSRef::Cast(builder)); CHECK_NULL_VOID(builderFunc); // TextResponseType uint32_t resquiredParameterCount = 3; JSRef argsResponse = info[resquiredParameterCount - 1]; NG::TextResponseType responseType = NG::TextResponseType::LONG_PRESS; if (argsResponse->IsNumber()) { auto response = argsResponse->ToNumber(); responseType = static_cast(response); } WeakPtr frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); std::function buildFunc = [execCtx = info.GetExecutionContext(), func = std::move(builderFunc), node = frameNode]() { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("BindSelectionMenu"); PipelineContext::SetCallBackNode(node); func->Execute(); }; // SelectionMenuOptions NG::SelectMenuParam menuParam; menuParam.isValid = isValidTextSpanType; if (info.Length() > static_cast(resquiredParameterCount)) { JSRef argsMenuOptions = info[resquiredParameterCount]; if (argsMenuOptions->IsObject()) { ParseMenuParam(info, argsMenuOptions, menuParam); } } TextModel::GetInstance()->BindSelectionMenu(textSpanType, responseType, buildFunc, menuParam); } void JSText::SetOnTextSelectionChange(const JSCallbackInfo& info) { JSRef args = info[0]; CHECK_NULL_VOID(args->IsFunction()); JsEventCallback callback(info.GetExecutionContext(), JSRef::Cast(args)); TextModel::GetInstance()->SetOnTextSelectionChange(std::move(callback)); } void JSText::JsClip(const JSCallbackInfo& info) { JSViewAbstract::JsClip(info); JSRef args = info[0]; if (args->IsBoolean()) { TextModel::GetInstance()->SetClipEdge(args->ToBoolean()); } } void JSText::SetFontFeature(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } if (!info[0]->IsString() && !info[0]->IsObject()) { return; } std::string fontFeatureSettings = info[0]->ToString(); TextModel::GetInstance()->SetFontFeature(ParseFontFeatureSettings(fontFeatureSettings)); } void JSText::JsResponseRegion(const JSCallbackInfo& info) { JSViewAbstract::JsResponseRegion(info); TextModel::GetInstance()->SetResponseRegion(true); } void JSText::SetHalfLeading(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } auto halfLeading = info[0]; if (!halfLeading->IsBoolean()) { TextModel::GetInstance()->SetHalfLeading(false); return; } auto enable = halfLeading->ToBoolean(); TextModel::GetInstance()->SetHalfLeading(enable); } void JSText::SetEnableHapticFeedback(const JSCallbackInfo& info) { bool state = true; if (info.Length() > 0 && info[0]->IsBoolean()) { state = info[0]->ToBoolean(); } TextModel::GetInstance()->SetEnableHapticFeedback(state); } void JSText::SetOptimizeTrailingSpace(const JSCallbackInfo& info) { bool state = false; if (info.Length() > 0 && info[0]->IsBoolean()) { state = info[0]->ToBoolean(); } TextModel::GetInstance()->SetOptimizeTrailingSpace(state); } void JSText::SetEnableAutoSpacing(const JSCallbackInfo& info) { bool enabled = false; if (info.Length() > 0 && info[0]->IsBoolean()) { enabled = info[0]->ToBoolean(); } TextModel::GetInstance()->SetEnableAutoSpacing(enabled); } void JSText::SetTextVerticalAlign(const JSCallbackInfo& info) { TextVerticalAlign verticalAlign = TextVerticalAlign::BASELINE; if (info.Length() > 0 && info[0]->IsNumber()) { verticalAlign = static_cast(info[0]->ToNumber()); } TextModel::GetInstance()->SetTextVerticalAlign(verticalAlign); } void JSText::SetShaderStyle(const JSCallbackInfo& info) { if (info.Length() < 1 || !info[0]->IsObject()) { TextModel::GetInstance()->ResetGradientShaderStyle(); return; } NG::Gradient gradient; ParseShaderStyle(info, gradient); } void JSText::ParseShaderStyle(const JSCallbackInfo& info, NG::Gradient& gradient) { CalcDimension value; if (info.Length() < 1 || (info.Length() > 0 && !info[0]->IsObject())) { TextModel::GetInstance()->ResetGradientShaderStyle(); return; } auto shaderStyleObj = JSRef::Cast(info[0]); if (shaderStyleObj->HasProperty("options")) { auto optionsValue = shaderStyleObj->GetProperty("options"); shaderStyleObj = JSRef::Cast(optionsValue); } if (shaderStyleObj->HasProperty("center") && shaderStyleObj->HasProperty("radius")) { NewRadialGradient(shaderStyleObj, gradient); TextModel::GetInstance()->SetGradientShaderStyle(gradient); } else if (shaderStyleObj->HasProperty("colors")) { NewLinearGradient(shaderStyleObj, gradient); TextModel::GetInstance()->SetGradientShaderStyle(gradient); } else if (shaderStyleObj->HasProperty("color")) { Color textColor; auto infoColor = shaderStyleObj->GetProperty("color"); ParseJsColor(infoColor, textColor); TextModel::GetInstance()->SetColorShaderStyle(textColor); } else { TextModel::GetInstance()->ResetGradientShaderStyle(); } } void JSText::SetContentTransition(const JSCallbackInfo& info) { if (info.Length() > 0 && !info[0]->IsObject()) { TextModel::GetInstance()->ResetContentTransition(); return; } auto contentTransitionObj = JSRef::Cast(info[0]); TextFlipDirection direction = TextFlipDirection::DOWN; bool enableBlur = false; if (contentTransitionObj->HasProperty("flipDirection")) { auto directionObj = contentTransitionObj->GetProperty("flipDirection"); if (directionObj->IsNumber()) { direction = static_cast(directionObj->ToNumber()); } } if (contentTransitionObj->HasProperty("enableBlur")) { auto enableBlurObj = contentTransitionObj->GetProperty("enableBlur"); if (enableBlurObj->IsBoolean()) { enableBlur = enableBlurObj->ToBoolean(); } } TextModel::GetInstance()->SetContentTransition(TextEffectStrategy::FLIP, direction, enableBlur); } void JSText::JSBind(BindingTarget globalObj) { JSClass::Declare("Text"); MethodOptions opt = MethodOptions::NONE; JSClass::StaticMethod("create", &JSText::Create, opt); JSClass::StaticMethod("width", &JSText::SetWidth); JSClass::StaticMethod("height", &JSText::SetHeight); JSClass::StaticMethod("font", &JSText::SetFont, opt); JSClass::StaticMethod("fontColor", &JSText::SetTextColor, opt); JSClass::StaticMethod("textShadow", &JSText::SetTextShadow, opt); JSClass::StaticMethod("fontSize", &JSText::SetFontSize, opt); JSClass::StaticMethod("fontWeight", &JSText::SetFontWeight, opt); JSClass::StaticMethod("minFontScale", &JSText::SetMinFontScale, opt); JSClass::StaticMethod("maxFontScale", &JSText::SetMaxFontScale, opt); JSClass::StaticMethod("wordBreak", &JSText::SetWordBreak, opt); JSClass::StaticMethod("lineBreakStrategy", &JSText::SetLineBreakStrategy, opt); JSClass::StaticMethod("selection", &JSText::SetTextSelection, opt); JSClass::StaticMethod("ellipsisMode", &JSText::SetEllipsisMode, opt); JSClass::StaticMethod("textSelectable", &JSText::SetTextSelectableMode, opt); JSClass::StaticMethod("maxLines", &JSText::SetMaxLines, opt); JSClass::StaticMethod("textIndent", &JSText::SetTextIndent); JSClass::StaticMethod("textOverflow", &JSText::SetTextOverflow, opt); JSClass::StaticMethod("fontStyle", &JSText::SetFontStyle, opt); JSClass::StaticMethod("align", &JSText::SetAlign, opt); JSClass::StaticMethod("textAlign", &JSText::SetTextAlign, opt); JSClass::StaticMethod("lineHeight", &JSText::SetLineHeight, opt); JSClass::StaticMethod("lineSpacing", &JSText::SetLineSpacing, opt); JSClass::StaticMethod("fontFamily", &JSText::SetFontFamily, opt); JSClass::StaticMethod("minFontSize", &JSText::SetMinFontSize, opt); JSClass::StaticMethod("maxFontSize", &JSText::SetMaxFontSize, opt); JSClass::StaticMethod("letterSpacing", &JSText::SetLetterSpacing, opt); JSClass::StaticMethod("textCase", &JSText::SetTextCase, opt); JSClass::StaticMethod("baselineOffset", &JSText::SetBaselineOffset, opt); JSClass::StaticMethod("caretColor", &JSText::SetTextCaretColor); JSClass::StaticMethod("selectedBackgroundColor", &JSText::SetSelectedBackgroundColor); JSClass::StaticMethod("decoration", &JSText::SetDecoration); JSClass::StaticMethod("heightAdaptivePolicy", &JSText::SetHeightAdaptivePolicy); JSClass::StaticMethod("contentTransition", &JSText::SetContentTransition); JSClass::StaticMethod("onTouch", &JSInteractableView::JsOnTouch); JSClass::StaticMethod("onHover", &JSInteractableView::JsOnHover); JSClass::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey); JSClass::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete); JSClass::StaticMethod("remoteMessage", &JSText::JsRemoteMessage); JSClass::StaticMethod("copyOption", &JSText::SetCopyOption); JSClass::StaticMethod("onClick", &JSText::JsOnClick); JSClass::StaticMethod("onCopy", &JSText::SetOnCopy); JSClass::StaticMethod("onAttach", &JSInteractableView::JsOnAttach); JSClass::StaticMethod("onAppear", &JSInteractableView::JsOnAppear); JSClass::StaticMethod("onDetach", &JSInteractableView::JsOnDetach); JSClass::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear); if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_FIFTEEN)) { JSClass::StaticMethod("onDragStart", &JSText::JsOnDragStart); } else { JSClass::StaticMethod("onDragStart", &JSViewAbstract::JsOnDragStart); } JSClass::StaticMethod("focusable", &JSText::JsFocusable); JSClass::StaticMethod("draggable", &JSText::JsDraggable); JSClass::StaticMethod("enableDataDetector", &JSText::JsEnableDataDetector); JSClass::StaticMethod("dataDetectorConfig", &JSText::JsDataDetectorConfig); JSClass::StaticMethod("bindSelectionMenu", &JSText::BindSelectionMenu); JSClass::StaticMethod("onTextSelectionChange", &JSText::SetOnTextSelectionChange); JSClass::StaticMethod("clip", &JSText::JsClip); JSClass::StaticMethod("foregroundColor", &JSText::SetForegroundColor); JSClass::StaticMethod("fontFeature", &JSText::SetFontFeature); JSClass::StaticMethod("marqueeOptions", &JSText::SetMarqueeOptions); JSClass::StaticMethod("onMarqueeStateChange", &JSText::SetOnMarqueeStateChange); JSClass::StaticMethod("editMenuOptions", &JSText::EditMenuOptions); JSClass::StaticMethod("responseRegion", &JSText::JsResponseRegion); JSClass::StaticMethod("halfLeading", &JSText::SetHalfLeading); JSClass::StaticMethod("enableHapticFeedback", &JSText::SetEnableHapticFeedback); JSClass::StaticMethod("optimizeTrailingSpace", &JSText::SetOptimizeTrailingSpace); JSClass::StaticMethod("enableAutoSpacing", &JSText::SetEnableAutoSpacing); JSClass::StaticMethod("textVerticalAlign", &JSText::SetTextVerticalAlign); JSClass::StaticMethod("shaderStyle", &JSText::SetShaderStyle); JSClass::InheritAndBind(globalObj); } void JSTextController::CloseSelectionMenu() { auto controller = controllerWeak_.Upgrade(); CHECK_NULL_VOID(controller); controller->CloseSelectionMenu(); } void JSTextController::GetLayoutManager(const JSCallbackInfo& args) { JSRef obj = JSClass::NewInstance(); auto jsLayoutManager = Referenced::Claim(obj->Unwrap()); CHECK_NULL_VOID(jsLayoutManager); jsLayoutManager->IncRefCount(); auto controller = controllerWeak_.Upgrade(); CHECK_NULL_VOID(controller); auto layoutInfoInterface = controller->GetLayoutInfoInterface(); jsLayoutManager->SetLayoutInfoInterface(layoutInfoInterface); args.SetReturnValue(obj); } void JSTextController::SetStyledString(const JSCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsObject()) { JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed."); return; } auto* spanString = JSRef::Cast(info[0])->Unwrap(); if (!spanString) { JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter check failed."); return; } auto spanStringController = spanString->GetController(); CHECK_NULL_VOID(spanStringController); auto controller = controllerWeak_.Upgrade(); if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_FIFTEEN) && !controller) { styledString_ = spanStringController; } CHECK_NULL_VOID(controller); controller->SetStyledString(spanStringController, true); auto thisObj = info.This(); thisObj->SetPropertyObject("STYLED_STRING_IN_CONTROLLER", info[0]); } void JSTextController::JSBind(BindingTarget globalObj) { JSClass::Declare("TextController"); JSClass::Method("closeSelectionMenu", &JSTextController::CloseSelectionMenu); JSClass::CustomMethod("setStyledString", &JSTextController::SetStyledString); JSClass::CustomMethod("getLayoutManager", &JSTextController::GetLayoutManager); JSClass::Bind(globalObj, JSTextController::Constructor, JSTextController::Destructor); } void JSText::ParseMenuParam( const JSCallbackInfo& info, const JSRef& menuOptions, NG::SelectMenuParam& menuParam) { WeakPtr frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onAppearValue = menuOptions->GetProperty("onAppear"); if (onAppearValue->IsFunction()) { RefPtr jsOnAppearFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(onAppearValue)); auto onAppear = [execCtx = info.GetExecutionContext(), func = std::move(jsOnAppearFunc), node = frameNode]( int32_t start, int32_t end) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("onAppear"); JSRef params[2]; params[0] = JSRef::Make(ToJSValue(start)); params[1] = JSRef::Make(ToJSValue(end)); PipelineContext::SetCallBackNode(node); func->ExecuteJS(2, params); }; menuParam.onAppear = std::move(onAppear); } auto onDisappearValue = menuOptions->GetProperty("onDisappear"); if (onDisappearValue->IsFunction()) { RefPtr jsOnDisAppearFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(onDisappearValue)); auto onDisappear = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDisAppearFunc), node = frameNode]() { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("onDisappear"); PipelineContext::SetCallBackNode(node); func->Execute(); }; menuParam.onDisappear = std::move(onDisappear); } menuParam.onMenuShow = ParseMenuCallback(frameNode, menuOptions, info, "onMenuShow"); menuParam.onMenuHide = ParseMenuCallback(frameNode, menuOptions, info, "onMenuHide"); menuParam.previewMenuOptions = ParsePreviewMenuOptions(menuOptions); } std::function JSText::ParseMenuCallback(const WeakPtr& frameNode, const JSRef& menuOptions, const JSCallbackInfo& info, const std::string& name) { auto onMenuCallbackValue = menuOptions->GetProperty(name.c_str()); if (onMenuCallbackValue->IsFunction()) { RefPtr jsOnMenuCallbackFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(onMenuCallbackValue)); auto onMenuCallback = [execCtx = info.GetExecutionContext(), func = std::move(jsOnMenuCallbackFunc), node = frameNode, eventName = name](int32_t start, int32_t end) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT(eventName); JSRef params[2]; params[0] = JSRef::Make(ToJSValue(start)); params[1] = JSRef::Make(ToJSValue(end)); PipelineContext::SetCallBackNode(node); func->ExecuteJS(2, params); }; return onMenuCallback; } return nullptr; } NG::PreviewMenuOptions JSText::ParsePreviewMenuOptions(const JSRef& menuOptions) { NG::PreviewMenuOptions previewMenuOptions; auto jsPreviewMenuOp = menuOptions->GetProperty("previewMenuOptions"); CHECK_EQUAL_RETURN(jsPreviewMenuOp->IsObject(), false, previewMenuOptions); auto jsPreviewMenuOpObj = JSRef::Cast(jsPreviewMenuOp); CHECK_EQUAL_RETURN(jsPreviewMenuOpObj->IsUndefined(), true, previewMenuOptions); JSRef jsHapticFeedbackMode = jsPreviewMenuOpObj->GetProperty("hapticFeedbackMode"); CHECK_EQUAL_RETURN(jsHapticFeedbackMode->IsNumber(), false, previewMenuOptions); auto hapticFeedbackMode = static_cast(jsHapticFeedbackMode->ToNumber()); if (hapticFeedbackMode >= HapticFeedbackMode::DISABLED && hapticFeedbackMode <= HapticFeedbackMode::AUTO) { previewMenuOptions.hapticFeedbackMode = hapticFeedbackMode; } return previewMenuOptions; } void JSText::SetMarqueeOptions(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } auto args = info[0]; NG::TextMarqueeOptions options; if (!args->IsObject()) { TextModel::GetInstance()->SetMarqueeOptions(options); return; } auto paramObject = JSRef::Cast(args); ParseMarqueeParam(paramObject, options); TextModel::GetInstance()->SetMarqueeOptions(options); } void JSText::ParseMarqueeParam(const JSRef& paramObject, NG::TextMarqueeOptions& options) { auto getStart = paramObject->GetProperty("start"); if (getStart->IsBoolean()) { options.UpdateTextMarqueeStart(getStart->ToBoolean()); } auto getLoop = paramObject->GetProperty("loop"); if (getLoop->IsNumber()) { int32_t loop = static_cast(getLoop->ToNumber()); if (loop == std::numeric_limits::max() || loop <= 0) { loop = -1; } options.UpdateTextMarqueeLoop(loop); } auto getStep = paramObject->GetProperty("step"); if (getStep->IsNumber()) { auto step = getStep->ToNumber(); if (GreatNotEqual(step, 0.0)) { options.UpdateTextMarqueeStep(Dimension(step, DimensionUnit::VP).ConvertToPx()); } } auto delay = paramObject->GetProperty("delay"); if (delay->IsNumber()) { auto delayDouble = delay->ToNumber(); auto delayValue = static_cast(delayDouble); if (delayValue < 0) { delayValue = 0; } options.UpdateTextMarqueeDelay(delayValue); } auto getFromStart = paramObject->GetProperty("fromStart"); if (getFromStart->IsBoolean()) { options.UpdateTextMarqueeDirection( getFromStart->ToBoolean() ? MarqueeDirection::DEFAULT : MarqueeDirection::DEFAULT_REVERSE); } auto getFadeout = paramObject->GetProperty("fadeout"); if (getFadeout->IsBoolean()) { options.UpdateTextMarqueeFadeout(getFadeout->ToBoolean()); } auto getStartPolicy = paramObject->GetProperty("marqueeStartPolicy"); if (getStartPolicy->IsNumber()) { auto startPolicy = static_cast(getStartPolicy->ToNumber()); options.UpdateTextMarqueeStartPolicy(startPolicy); } } void JSText::SetOnMarqueeStateChange(const JSCallbackInfo& info) { if (!info[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(info[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onMarqueeStateChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( int32_t value) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Text.onMarqueeStateChange"); PipelineContext::SetCallBackNode(node); auto newJSVal = JSRef::Make(ToJSValue(value)); func->ExecuteJS(1, &newJSVal); }; TextModel::GetInstance()->SetOnMarqueeStateChange(std::move(onMarqueeStateChange)); } void JSText::EditMenuOptions(const JSCallbackInfo& info) { NG::OnCreateMenuCallback onCreateMenuCallback; NG::OnMenuItemClickCallback onMenuItemClick; NG::OnPrepareMenuCallback onPrepareMenuCallback; JSViewAbstract::ParseEditMenuOptions(info, onCreateMenuCallback, onMenuItemClick, onPrepareMenuCallback); TextModel::GetInstance()->SetSelectionMenuOptions( std::move(onCreateMenuCallback), std::move(onMenuItemClick), std::move(onPrepareMenuCallback)); } } // namespace OHOS::Ace::Framework