/* * 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_text_theme.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/text/text_theme.h" #include "core/components_ng/base/view_stack_processor.h" #include "core/components_ng/event/gesture_event_hub.h" #include "core/components_ng/pattern/text/text_model.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; 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]; auto pipelineContext = PipelineContext::GetCurrentContext(); 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(); if (!tmpInfo->IsObject()) { return; } auto paramObject = JSRef::Cast(tmpInfo); auto fontSize = paramObject->GetProperty(static_cast(ArkUIIndex::SIZE)); CalcDimension size; if (ParseJsDimensionFpNG(fontSize, size, false) && size.IsNonNegative()) { font.fontSize = 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; if (JSContainerBase::ParseJsFontFamilies(fontFamily, fontFamilies)) { font.fontFamilies = 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; JSRef args = info[0]; if (!ParseJsDimensionFpNG(args, fontSize, false) || fontSize.IsNegative()) { auto pipelineContext = PipelineBase::GetCurrentContext(); 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); } 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; if (info.Length() < 1 || !ParseJsDouble(info[0], minFontScale)) { return; } if (LessOrEqual(minFontScale, 0.0f)) { TextModel::GetInstance()->SetMinFontScale(0.0f); return; } if (GreatOrEqual(minFontScale, 1.0f)) { TextModel::GetInstance()->SetMinFontScale(1.0f); return; } TextModel::GetInstance()->SetMinFontScale(static_cast(minFontScale)); } void JSText::SetMaxFontScale(const JSCallbackInfo& info) { double maxFontScale; if (info.Length() < 1 || !ParseJsDouble(info[0], maxFontScale)) { return; } if (LessOrEqual(maxFontScale, 1.0f)) { TextModel::GetInstance()->SetMaxFontScale(1.0f); return; } TextModel::GetInstance()->SetMaxFontScale(static_cast(maxFontScale)); } 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; JSRef args = info[0]; if (!ParseJsColor(args, textColor)) { auto pipelineContext = PipelineBase::GetCurrentContext(); CHECK_NULL_VOID(pipelineContext); auto theme = pipelineContext->GetTheme(); CHECK_NULL_VOID(theme); textColor = theme->GetTextStyle().GetTextColor(); } 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(); if (startIndex == -1 && endIndex == -1) { TextModel::GetInstance()->SetTextSelection(startIndex, endIndex); return; } if (startIndex >= endIndex) { return; } TextModel::GetInstance()->SetTextSelection(startIndex, endIndex); } void JSText::SetTextCaretColor(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } Color caretColor; if (!ParseJsColor(info[0], caretColor)) { auto pipelineContext = PipelineContext::GetCurrentContextSafely(); CHECK_NULL_VOID(pipelineContext); auto theme = pipelineContext->GetTheme(); CHECK_NULL_VOID(theme); caretColor = theme->GetCaretColor(); } TextModel::GetInstance()->SetTextCaretColor(caretColor); } void JSText::SetSelectedBackgroundColor(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } Color selectedColor; if (!ParseJsColor(info[0], selectedColor)) { auto pipelineContext = PipelineContext::GetCurrentContextSafely(); CHECK_NULL_VOID(pipelineContext); auto theme = pipelineContext->GetTheme(); CHECK_NULL_VOID(theme); selectedColor = theme->GetSelectedColor(); } // 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; JSRef args = info[0]; if (!ParseJsDimensionFpNG(args, value)) { value.Reset(); TextModel::GetInstance()->SetTextIndent(value); return; } TextModel::GetInstance()->SetTextIndent(value); } void JSText::SetFontStyle(int32_t value) { if (value < 0 || value >= static_cast(FONT_STYLES.size())) { if (!(AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(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 (!(AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(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]; if (!ParseJsDimensionFpNG(args, value)) { value.Reset(); TextModel::GetInstance()->SetLineHeight(value); return; } if (value.IsNegative()) { value.Reset(); } TextModel::GetInstance()->SetLineHeight(value); } void JSText::SetLineSpacing(const JSCallbackInfo& info) { CalcDimension value; JSRef args = info[0]; if (!ParseLengthMetricsToPositiveDimension(args, value)) { value.Reset(); } if (value.IsNegative()) { value.Reset(); } TextModel::GetInstance()->SetLineSpacing(value); } void JSText::SetFontFamily(const JSCallbackInfo& info) { std::vector fontFamilies; JSRef args = info[0]; ParseJsFontFamilies(args, fontFamilies); TextModel::GetInstance()->SetFontFamily(fontFamilies); } void JSText::SetMinFontSize(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } auto pipelineContext = PipelineBase::GetCurrentContext(); CHECK_NULL_VOID(pipelineContext); auto theme = pipelineContext->GetTheme(); CHECK_NULL_VOID(theme); CalcDimension minFontSize = theme->GetTextStyle().GetAdaptMinFontSize(); JSRef args = info[0]; if (!ParseJsDimensionFpNG(args, minFontSize, false)) { minFontSize = theme->GetTextStyle().GetAdaptMinFontSize(); TextModel::GetInstance()->SetAdaptMinFontSize(minFontSize); return; } if (minFontSize.IsNegative()) { minFontSize = theme->GetTextStyle().GetAdaptMinFontSize(); } TextModel::GetInstance()->SetAdaptMinFontSize(minFontSize); } void JSText::SetMaxFontSize(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } auto pipelineContext = PipelineBase::GetCurrentContext(); CHECK_NULL_VOID(pipelineContext); auto theme = pipelineContext->GetTheme(); CHECK_NULL_VOID(theme); CalcDimension maxFontSize = theme->GetTextStyle().GetAdaptMaxFontSize(); JSRef args = info[0]; if (!ParseJsDimensionFpNG(args, maxFontSize, false)) { maxFontSize = theme->GetTextStyle().GetAdaptMaxFontSize(); TextModel::GetInstance()->SetAdaptMaxFontSize(maxFontSize); return; } if (maxFontSize.IsNegative()) { maxFontSize = theme->GetTextStyle().GetAdaptMaxFontSize(); } TextModel::GetInstance()->SetAdaptMaxFontSize(maxFontSize); } void JSText::SetLetterSpacing(const JSCallbackInfo& info) { CalcDimension value; JSRef args = info[0]; if (!ParseJsDimensionFpNG(args, value, false)) { value.Reset(); TextModel::GetInstance()->SetLetterSpacing(value); return; } TextModel::GetInstance()->SetLetterSpacing(value); } void JSText::SetTextCase(int32_t value) { if (value < 0 || value >= static_cast(TEXT_CASES.size())) { if (!(AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE))) { return; } value = 0; } TextModel::GetInstance()->SetTextCase(TEXT_CASES[value]); } void JSText::SetBaselineOffset(const JSCallbackInfo& info) { CalcDimension value; JSRef args = info[0]; if (!ParseJsDimensionFpNG(args, value, false)) { value.Reset(); TextModel::GetInstance()->SetBaselineOffset(value); return; } TextModel::GetInstance()->SetBaselineOffset(value); } void JSText::SetDecoration(const JSCallbackInfo& info) { auto tmpInfo = info[0]; 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"); TextDecoration textDecoration; if (typeValue->IsNumber()) { textDecoration = static_cast(typeValue->ToNumber()); } else { auto theme = GetTheme(); CHECK_NULL_VOID(theme); textDecoration = theme->GetTextStyle().GetTextDecoration(); } Color result; if (!ParseJsColor(colorValue, result)) { auto theme = GetTheme(); CHECK_NULL_VOID(theme); if (SystemProperties::GetColorMode() == ColorMode::DARK) { result = theme->GetTextStyle().GetTextColor(); } else { result = theme->GetTextStyle().GetTextDecorationColor(); } } std::optional textDecorationStyle; if (styleValue->IsNumber()) { textDecorationStyle = static_cast(styleValue->ToNumber()); } else { textDecorationStyle = DEFAULT_TEXT_DECORATION_STYLE; } TextModel::GetInstance()->SetTextDecoration(textDecoration); TextModel::GetInstance()->SetTextDecorationColor(result); if (textDecorationStyle) { TextModel::GetInstance()->SetTextDecorationStyle(textDecorationStyle.value()); } info.ReturnSelf(); } void JSText::SetHeightAdaptivePolicy(int32_t value) { if (value < 0 || value >= static_cast(HEIGHT_ADAPTIVE_POLICY.size())) { if (!(AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(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::string label = ""; if (!node.Invalid()) { auto pattern = node.GetRawPtr()->GetPattern(); CHECK_NULL_VOID(pattern); auto layoutProperty = pattern->GetLayoutProperty(); CHECK_NULL_VOID(layoutProperty); label = layoutProperty->GetContent().value_or(""); } JSInteractableView::ReportClickEvent(node, label); #endif }; TextModel::GetInstance()->SetOnClick(std::move(onClick)); 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); }; TextModel::GetInstance()->SetOnClick(std::move(onClickId)); } #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::string data; if (info.Length() <= 0) { TextModel::GetInstance()->Create(data); 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); } } else { ParseJsString(info[0], data); TextModel::GetInstance()->Create(data); } JSTextTheme::ApplyTheme(); 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 (jsController) { jsController->SetController(controller); } } 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)); auto 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::JsOnDragEnter(const JSCallbackInfo& info) { JSRef args = info[0]; CHECK_NULL_VOID(args->IsFunction()); auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); RefPtr jsOnDragEnterFunc = AceType::MakeRefPtr(JSRef::Cast(args)); auto onDragEnterId = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragEnterFunc), node = frameNode]( const RefPtr& info, const std::string& extraParams) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("onDragEnter"); PipelineContext::SetCallBackNode(node); func->Execute(info, extraParams); }; TextModel::GetInstance()->SetOnDragEnter(std::move(onDragEnterId)); } void JSText::JsOnDragMove(const JSCallbackInfo& info) { JSRef args = info[0]; CHECK_NULL_VOID(args->IsFunction()); auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); RefPtr jsOnDragMoveFunc = AceType::MakeRefPtr(JSRef::Cast(args)); auto onDragMoveId = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragMoveFunc), node = frameNode]( const RefPtr& info, const std::string& extraParams) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("onDragMove"); PipelineContext::SetCallBackNode(node); func->Execute(info, extraParams); }; TextModel::GetInstance()->SetOnDragMove(std::move(onDragMoveId)); } void JSText::JsOnDragLeave(const JSCallbackInfo& info) { JSRef args = info[0]; CHECK_NULL_VOID(args->IsFunction()); auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); RefPtr jsOnDragLeaveFunc = AceType::MakeRefPtr(JSRef::Cast(args)); auto onDragLeaveId = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragLeaveFunc), node = frameNode]( const RefPtr& info, const std::string& extraParams) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("onDragLeave"); PipelineContext::SetCallBackNode(node); func->Execute(info, extraParams); }; TextModel::GetInstance()->SetOnDragLeave(std::move(onDragLeaveId)); } void JSText::JsOnDrop(const JSCallbackInfo& info) { JSRef args = info[0]; CHECK_NULL_VOID(args->IsFunction()); RefPtr jsOnDropFunc = AceType::MakeRefPtr(JSRef::Cast(args)); auto onDropId = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDropFunc)]( const RefPtr& info, const std::string& extraParams) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("onDrop"); func->Execute(info, extraParams); }; TextModel::GetInstance()->SetOnDrop(std::move(onDropId)); } 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 testSpanType = NG::TextSpanType::TEXT; JSRef argsSpanType = info[0]; if (argsSpanType->IsNumber()) { auto spanType = argsSpanType->ToNumber(); testSpanType = static_cast(spanType); } // 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 int32_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; if (info.Length() > resquiredParameterCount) { JSRef argsMenuOptions = info[resquiredParameterCount]; if (argsMenuOptions->IsObject()) { ParseMenuParam(info, argsMenuOptions, menuParam); } } TextModel::GetInstance()->BindSelectionMenu(testSpanType, 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::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("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); JSClass::StaticMethod("onDragStart", &JSText::JsOnDragStart); JSClass::StaticMethod("onDragEnter", &JSText::JsOnDragEnter); JSClass::StaticMethod("onDragMove", &JSText::JsOnDragMove); JSClass::StaticMethod("onDragLeave", &JSText::JsOnDragLeave); JSClass::StaticMethod("onDrop", &JSText::JsOnDrop); 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("editMenuOptions", &JSText::EditMenuOptions); JSClass::StaticMethod("responseRegion", &JSText::JsResponseRegion); JSClass::StaticMethod("halfLeading", &JSText::SetHalfLeading); JSClass::StaticMethod("enableHapticFeedback", &JSText::SetEnableHapticFeedback); 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 controller = controllerWeak_.Upgrade(); CHECK_NULL_VOID(controller); auto spanStringController = spanString->GetController(); CHECK_NULL_VOID(spanStringController); controller->SetStyledString(spanStringController); 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); } } void JSText::EditMenuOptions(const JSCallbackInfo& info) { NG::OnCreateMenuCallback onCreateMenuCallback; NG::OnMenuItemClickCallback onMenuItemClick; JSViewAbstract::ParseEditMenuOptions(info, onCreateMenuCallback, onMenuItemClick); TextModel::GetInstance()->SetSelectionMenuOptions(std::move(onCreateMenuCallback), std::move(onMenuItemClick)); } } // namespace OHOS::Ace::Framework