diff --git a/frameworks/bridge/declarative_frontend/ark_component/src/ArkText.ts b/frameworks/bridge/declarative_frontend/ark_component/src/ArkText.ts index 4ec6f899d6193048a7dadd39c5ba89c5389cb980..9deb1d5f358b043e4347a91be58988b725a18a12 100644 --- a/frameworks/bridge/declarative_frontend/ark_component/src/ArkText.ts +++ b/frameworks/bridge/declarative_frontend/ark_component/src/ArkText.ts @@ -398,6 +398,23 @@ class TextMaxLinesModifier extends ModifierWithKey { } } +class TextMinLinesModifier extends ModifierWithKey { + constructor(value: number | undefined) { + super(value); + } + static identity: Symbol = Symbol('textMinLines'); + applyPeer(node: KNode, reset: boolean): void { + if (reset) { + getUINativeModule().text.resetMinLines(node); + } else { + getUINativeModule().text.setMinLines(node, this.value!); + } + } + checkObjectDiff(): boolean { + return !isBaseOrResourceEqual(this.stageValue, this.value); + } +} + class TextLetterSpacingModifier extends ModifierWithKey { constructor(value: number | string | Resource) { super(value); @@ -1053,6 +1070,10 @@ class ArkTextComponent extends ArkComponent implements TextAttribute { modifierWithKey(this._modifiersWithKeys, TextMaxLinesModifier.identity, TextMaxLinesModifier, value); return this; } + minLines(value: number): TextAttribute { + modifierWithKey(this._modifiersWithKeys, TextMinLinesModifier.identity, TextMinLinesModifier, value); + return this; + } decoration(value: { type: TextDecorationType; color?: ResourceColor; style?: TextDecorationStyle }): TextAttribute { modifierWithKey(this._modifiersWithKeys, TextDecorationModifier.identity, TextDecorationModifier, value); return this; diff --git a/frameworks/bridge/declarative_frontend/engine/arkComponent.js b/frameworks/bridge/declarative_frontend/engine/arkComponent.js index 7df3fe4c524ee6e7d83ef9cfd62e2bd8e283b204..acbbb7d521daa30906dcb5aa16491980ab029e36 100755 --- a/frameworks/bridge/declarative_frontend/engine/arkComponent.js +++ b/frameworks/bridge/declarative_frontend/engine/arkComponent.js @@ -13695,6 +13695,22 @@ class TextMaxLinesModifier extends ModifierWithKey { } } TextMaxLinesModifier.identity = Symbol('textMaxLines'); +class TextMinLinesModifier extends ModifierWithKey { + constructor(value) { + super(value); + } + applyPeer(node, reset) { + if (reset) { + getUINativeModule().text.resetMinLines(node); + } else { + getUINativeModule().text.setMinLines(node, this.value); + } + } + checkObjectDiff() { + return !isBaseOrResourceEqual(this.stageValue, this.value); + } +} +TextMinLinesModifier.identity = Symbol('textMinLines'); class TextLetterSpacingModifier extends ModifierWithKey { constructor(value) { super(value); @@ -14401,6 +14417,10 @@ class ArkTextComponent extends ArkComponent { modifierWithKey(this._modifiersWithKeys, TextMaxLinesModifier.identity, TextMaxLinesModifier, value); return this; } + minLines(value) { + modifierWithKey(this._modifiersWithKeys, TextMinLinesModifier.identity, TextMinLinesModifier, value); + return this; + } decoration(value) { modifierWithKey(this._modifiersWithKeys, TextDecorationModifier.identity, TextDecorationModifier, value); return this; diff --git a/frameworks/bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_api_impl_bridge.cpp b/frameworks/bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_api_impl_bridge.cpp index 03026a15fe2ae3605898d5fd4f4c2c1c3901a279..ef95abe9646637ec8bd780b6831dfcc67fc47d32 100644 --- a/frameworks/bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_api_impl_bridge.cpp +++ b/frameworks/bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_api_impl_bridge.cpp @@ -1489,6 +1489,10 @@ ArkUINativeModuleValue ArkUINativeModule::GetArkUINativeModule(ArkUIRuntimeCallI panda::FunctionRef::New(const_cast(vm), TextBridge::SetMaxLines)); text->Set(vm, panda::StringRef::NewFromUtf8(vm, "resetMaxLines"), panda::FunctionRef::New(const_cast(vm), TextBridge::ResetMaxLines)); + text->Set(vm, panda::StringRef::NewFromUtf8(vm, "setMinLines"), + panda::FunctionRef::New(const_cast(vm), TextBridge::SetMinLines)); + text->Set(vm, panda::StringRef::NewFromUtf8(vm, "resetMinLines"), + panda::FunctionRef::New(const_cast(vm), TextBridge::ResetMinLines)); text->Set(vm, panda::StringRef::NewFromUtf8(vm, "setMinFontSize"), panda::FunctionRef::New(const_cast(vm), TextBridge::SetMinFontSize)); text->Set(vm, panda::StringRef::NewFromUtf8(vm, "resetMinFontSize"), diff --git a/frameworks/bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_text_bridge.cpp b/frameworks/bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_text_bridge.cpp index 31f18f8e050bea5f04bab890d51097a0e419fd98..17aa52971a19b84f3469bdb812f92b75785ba7c0 100644 --- a/frameworks/bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_text_bridge.cpp +++ b/frameworks/bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_text_bridge.cpp @@ -447,6 +447,34 @@ ArkUINativeModuleValue TextBridge::ResetMaxLines(ArkUIRuntimeCallInfo* runtimeCa return panda::JSValueRef::Undefined(vm); } +ArkUINativeModuleValue TextBridge::SetMinLines(ArkUIRuntimeCallInfo* runtimeCallInfo) +{ + EcmaVM* vm = runtimeCallInfo->GetVM(); + CHECK_NULL_RETURN(vm, panda::JSValueRef::Undefined(vm)); + Local firstArg = runtimeCallInfo->GetCallArgRef(NUM_0); + Local secondArg = runtimeCallInfo->GetCallArgRef(NUM_1); + CHECK_NULL_RETURN(firstArg->IsNativePointer(vm), panda::JSValueRef::Undefined(vm)); + auto nativeNode = nodePtr(firstArg->ToNativePointer(vm)->Value()); + if (secondArg->IsNumber()) { + uint32_t minLine = secondArg->Uint32Value(vm); + GetArkUINodeModifiers()->getTextModifier()->setTextMinLines(nativeNode, minLine); + } else { + GetArkUINodeModifiers()->getTextModifier()->resetTextMinLines(nativeNode); + } + return panda::JSValueRef::Undefined(vm); +} + +ArkUINativeModuleValue TextBridge::ResetMinLines(ArkUIRuntimeCallInfo* runtimeCallInfo) +{ + EcmaVM* vm = runtimeCallInfo->GetVM(); + CHECK_NULL_RETURN(vm, panda::JSValueRef::Undefined(vm)); + Local firstArg = runtimeCallInfo->GetCallArgRef(NUM_0); + CHECK_NULL_RETURN(firstArg->IsNativePointer(vm), panda::JSValueRef::Undefined(vm)); + auto nativeNode = nodePtr(firstArg->ToNativePointer(vm)->Value()); + GetArkUINodeModifiers()->getTextModifier()->resetTextMinLines(nativeNode); + return panda::JSValueRef::Undefined(vm); +} + ArkUINativeModuleValue TextBridge::SetMinFontSize(ArkUIRuntimeCallInfo* runtimeCallInfo) { EcmaVM* vm = runtimeCallInfo->GetVM(); diff --git a/frameworks/bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_text_bridge.h b/frameworks/bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_text_bridge.h index 6f86377c717d2229b3194fd4122d65705329e6e6..aae3f8c7f210ca565b6394526a372f9257f27cab 100644 --- a/frameworks/bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_text_bridge.h +++ b/frameworks/bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_text_bridge.h @@ -43,6 +43,8 @@ public: static ArkUINativeModuleValue ResetTextCase(ArkUIRuntimeCallInfo* runtimeCallInfo); static ArkUINativeModuleValue SetMaxLines(ArkUIRuntimeCallInfo* runtimeCallInfo); static ArkUINativeModuleValue ResetMaxLines(ArkUIRuntimeCallInfo* runtimeCallInfo); + static ArkUINativeModuleValue SetMinLines(ArkUIRuntimeCallInfo* runtimeCallInfo); + static ArkUINativeModuleValue ResetMinLines(ArkUIRuntimeCallInfo* runtimeCallInfo); static ArkUINativeModuleValue SetMinFontSize(ArkUIRuntimeCallInfo* runtimeCallInfo); static ArkUINativeModuleValue ReSetMinFontSize(ArkUIRuntimeCallInfo* runtimeCallInfo); static ArkUINativeModuleValue SetDraggable(ArkUIRuntimeCallInfo* runtimeCallInfo); diff --git a/frameworks/bridge/declarative_frontend/jsview/js_text.cpp b/frameworks/bridge/declarative_frontend/jsview/js_text.cpp index 540adb095f4fa330763da6a205bf48821028a565..183f63eabcaa539def906adfaf698a877c928f33 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_text.cpp +++ b/frameworks/bridge/declarative_frontend/jsview/js_text.cpp @@ -94,6 +94,7 @@ const std::vector TEXT_SELECTABLE_MODE = { TextSelectableMod TextSelectableMode::SELECTABLE_FOCUSABLE, TextSelectableMode::UNSELECTABLE }; constexpr TextDecorationStyle DEFAULT_TEXT_DECORATION_STYLE = TextDecorationStyle::SOLID; const int32_t DEFAULT_VARIABLE_FONT_WEIGHT = 400; +constexpr uint32_t MIN_LINES = 0; }; // namespace void JSText::SetWidth(const JSCallbackInfo& info) @@ -475,6 +476,18 @@ void JSText::SetMaxLines(const JSCallbackInfo& info) TextModel::GetInstance()->SetMaxLines(value); } +void JSText::SetMinLines(const JSCallbackInfo& info) +{ + auto minLines = MIN_LINES; + if (info.Length() == 1) { + auto tmpInfo = info[0]; + if (tmpInfo->IsNumber() && tmpInfo->ToNumber() > 0) { + minLines = tmpInfo->ToNumber(); + } + } + TextModel::GetInstance()->SetMinLines(minLines); +} + void JSText::SetTextIndent(const JSCallbackInfo& info) { CalcDimension value; @@ -1283,6 +1296,7 @@ void JSText::JSBind(BindingTarget globalObj) JSClass::StaticMethod("copyOption", &JSText::SetCopyOption); JSClass::StaticMethod("onClick", &JSText::JsOnClick); JSClass::StaticMethod("onCopy", &JSText::SetOnCopy); + JSClass::StaticMethod("minLines", &JSText::SetMinLines); JSClass::StaticMethod("onAttach", &JSInteractableView::JsOnAttach); JSClass::StaticMethod("onAppear", &JSInteractableView::JsOnAppear); JSClass::StaticMethod("onDetach", &JSInteractableView::JsOnDetach); diff --git a/frameworks/bridge/declarative_frontend/jsview/js_text.h b/frameworks/bridge/declarative_frontend/jsview/js_text.h index 6a347fb947c5e3227fba5bc271bc9d2494e7db2f..915ffd90f6fba5cc3796f9e8405512679943baf9 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_text.h +++ b/frameworks/bridge/declarative_frontend/jsview/js_text.h @@ -43,6 +43,7 @@ public: static void SetTextShadow(const JSCallbackInfo& info); static void SetTextOverflow(const JSCallbackInfo& info); static void SetMaxLines(const JSCallbackInfo& info); + static void SetMinLines(const JSCallbackInfo& info); static void SetTextIndent(const JSCallbackInfo& info); static void SetFontStyle(int32_t value); static void SetAlign(const JSCallbackInfo& info); diff --git a/frameworks/core/components_ng/pattern/text/text_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/text/text_layout_algorithm.cpp index 0cbf2801cb42f063206843cef238f3ea4cf44326..9049fe98907715e8cce34969266b76cefb4e4e59 100644 --- a/frameworks/core/components_ng/pattern/text/text_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/text/text_layout_algorithm.cpp @@ -33,6 +33,7 @@ namespace OHOS::Ace::NG { namespace { constexpr int32_t HUNDRED = 100; constexpr int32_t TWENTY = 20; +constexpr uint32_t DEFAULT_MIN_LINES = 1; uint32_t GetAdaptedMaxLines(const TextStyle& textStyle, const LayoutConstraintF& contentConstraint) { @@ -90,6 +91,30 @@ TextLayoutAlgorithm::TextLayoutAlgorithm() = default; void TextLayoutAlgorithm::OnReset() {} +void TextLayoutAlgorithm::CalcHeightWithMinLines( + float& heightFinal, LayoutWrapper* layoutWrapper, const LayoutConstraintF& contentConstraint) +{ + CHECK_NULL_VOID(layoutWrapper); + auto textLayoutProperty = DynamicCast(layoutWrapper->GetLayoutProperty()); + CHECK_NULL_VOID(textLayoutProperty); + if (textLayoutProperty && textLayoutProperty->HasMinLines() && + textLayoutProperty->GetMinLines().value() >= DEFAULT_MIN_LINES) { + auto lineCount = std::max(static_cast(paragraphManager_->GetLineCount()), DEFAULT_MIN_LINES); + auto paragraphHeight = paragraphManager_->GetHeight(); + auto perLineHeight = paragraphHeight / lineCount; + auto minLines = textLayoutProperty->GetMinLines().value(); + if (textLayoutProperty->HasMaxLines()) { + minLines = std::min(minLines, textLayoutProperty->GetMaxLines().value()); + } + auto finalMinHeight = perLineHeight * minLines; + finalMinHeight = + std::clamp(finalMinHeight, contentConstraint.minSize.Height(), contentConstraint.maxSize.Height()); + if (GreatNotEqual(finalMinHeight, heightFinal)) { + heightFinal = finalMinHeight; + } + } +} + std::optional TextLayoutAlgorithm::MeasureContent( const LayoutConstraintF& constraint, LayoutWrapper* layoutWrapper) { @@ -158,6 +183,7 @@ std::optional TextLayoutAlgorithm::MeasureContent( } else { heightFinal = std::min(heightFinal, contentConstraint.maxSize.Height()); } + CalcHeightWithMinLines(heightFinal, layoutWrapper, contentConstraint); if (host->GetTag() == V2::TEXT_ETS_TAG && textLayoutProperty->GetContent().value_or(u"").empty() && NonPositive(longestLine)) { ACE_SCOPED_TRACE("TextHeightFinal [%f], TextContentWidth [%f], FontSize [%lf]", heightFinal, maxWidth, diff --git a/frameworks/core/components_ng/pattern/text/text_layout_algorithm.h b/frameworks/core/components_ng/pattern/text/text_layout_algorithm.h index af16ed4f5818a3731bbdc9f535fd1d172f50f1b0..75bd434ce98e60377fac1e2647409f4ba96099e8 100644 --- a/frameworks/core/components_ng/pattern/text/text_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/text/text_layout_algorithm.h @@ -139,6 +139,8 @@ private: LayoutConstraintF CalcContentConstraint(const LayoutConstraintF& constraint, LayoutWrapper* layoutWrapper); std::optional GetCalcLayoutConstraintLength(LayoutWrapper* layoutWrapper, bool isMax, bool isWidth); void MeasureWithFixAtIdealSize(LayoutWrapper* layoutWrapper); + void CalcHeightWithMinLines( + float& height, LayoutWrapper* layoutWrapper, const LayoutConstraintF& contentConstraint); RefPtr showSelect_; std::optional cachedCalcContentConstraint_; bool isFixIdealSizeAndNoMaxWidth_ = false; diff --git a/frameworks/core/components_ng/pattern/text/text_layout_property.h b/frameworks/core/components_ng/pattern/text/text_layout_property.h index 850ab8f712df907328e0f1decb2fade727dfedd1..00553c7951086e4ddd6276d4de9ca6a7df76ef2a 100644 --- a/frameworks/core/components_ng/pattern/text/text_layout_property.h +++ b/frameworks/core/components_ng/pattern/text/text_layout_property.h @@ -189,6 +189,7 @@ public: ACE_DEFINE_PROPERTY_ITEM_WITH_GROUP(TextLineStyle, TextOverflow, TextOverflow, PROPERTY_UPDATE_MEASURE); ACE_DEFINE_PROPERTY_ITEM_WITH_GROUP(TextLineStyle, EllipsisMode, EllipsisMode, PROPERTY_UPDATE_MEASURE); ACE_DEFINE_PROPERTY_ITEM_WITH_GROUP(TextLineStyle, MaxLines, uint32_t, PROPERTY_UPDATE_MEASURE); + ACE_DEFINE_PROPERTY_ITEM_WITH_GROUP(TextLineStyle, MinLines, uint32_t, PROPERTY_UPDATE_MEASURE); ACE_DEFINE_PROPERTY_ITEM_WITH_GROUP(TextLineStyle, AllowScale, bool, PROPERTY_UPDATE_MEASURE); ACE_DEFINE_TEXT_PROPERTY_ITEM_WITH_GROUP( TextLineStyle, HeightAdaptivePolicy, TextHeightAdaptivePolicy, PROPERTY_UPDATE_MEASURE); diff --git a/frameworks/core/components_ng/pattern/text/text_model.h b/frameworks/core/components_ng/pattern/text/text_model.h index 136555bb89cfcf394b2f5c6a3fb38649700643b1..a1206c524008f9de28ef3ccb426addb0fa245069 100644 --- a/frameworks/core/components_ng/pattern/text/text_model.h +++ b/frameworks/core/components_ng/pattern/text/text_model.h @@ -160,6 +160,7 @@ public: virtual void SetTextAlign(TextAlign value) = 0; virtual void SetTextOverflow(TextOverflow value) = 0; virtual void SetMaxLines(uint32_t value) = 0; + virtual void SetMinLines(uint32_t value) {}; virtual void SetTextIndent(const Dimension& value) = 0; virtual void SetLineHeight(const Dimension& value) = 0; virtual void SetLineSpacing(const Dimension& value) = 0; diff --git a/frameworks/core/components_ng/pattern/text/text_model_ng.cpp b/frameworks/core/components_ng/pattern/text/text_model_ng.cpp index a0c31567542b8966a275d0f0294b30cf5afe7a34..c990a8bef776b650bd8ed66846c818d998d297cf 100644 --- a/frameworks/core/components_ng/pattern/text/text_model_ng.cpp +++ b/frameworks/core/components_ng/pattern/text/text_model_ng.cpp @@ -29,6 +29,7 @@ namespace OHOS::Ace::NG { +constexpr uint32_t DEFAULT_MIN_LINES = 0; constexpr int32_t DEFAULT_ALPHA = 255; constexpr float DEFAULT_OPACITY = 0.2; @@ -345,6 +346,11 @@ void TextModelNG::SetMaxLines(uint32_t value) ACE_UPDATE_LAYOUT_PROPERTY(TextLayoutProperty, MaxLines, value); } +void TextModelNG::SetMinLines(uint32_t value) +{ + ACE_UPDATE_LAYOUT_PROPERTY(TextLayoutProperty, MinLines, value); +} + void TextModelNG::SetTextIndent(const Dimension& value) { ACE_UPDATE_LAYOUT_PROPERTY(TextLayoutProperty, TextIndent, value); @@ -595,6 +601,11 @@ void TextModelNG::SetMaxLines(FrameNode* frameNode, uint32_t value) ACE_UPDATE_NODE_LAYOUT_PROPERTY(TextLayoutProperty, MaxLines, value, frameNode); } +void TextModelNG::SetMinLines(FrameNode* frameNode, uint32_t value) +{ + ACE_UPDATE_NODE_LAYOUT_PROPERTY(TextLayoutProperty, MinLines, value, frameNode); +} + void TextModelNG::SetAdaptMinFontSize(FrameNode* frameNode, const Dimension& value) { ACE_UPDATE_NODE_LAYOUT_PROPERTY(TextLayoutProperty, AdaptMinFontSize, value, frameNode); @@ -946,6 +957,17 @@ uint32_t TextModelNG::GetMaxLines(FrameNode* frameNode) return textLineStyle->GetMaxLines().value_or(defaultMaxLines); } +uint32_t TextModelNG::GetMinLines(FrameNode* frameNode) +{ + uint32_t defaultMinLines = DEFAULT_MIN_LINES; + CHECK_NULL_RETURN(frameNode, defaultMinLines); + auto layoutProperty = frameNode->GetLayoutProperty(); + CHECK_NULL_RETURN(layoutProperty, defaultMinLines); + auto& textLineStyle = layoutProperty->GetTextLineStyle(); + CHECK_NULL_RETURN(textLineStyle, defaultMinLines); + return textLineStyle->GetMinLines().value_or(defaultMinLines); +} + TextAlign TextModelNG::GetTextAlign(FrameNode* frameNode) { CHECK_NULL_RETURN(frameNode, OHOS::Ace::TextAlign::START); diff --git a/frameworks/core/components_ng/pattern/text/text_model_ng.h b/frameworks/core/components_ng/pattern/text/text_model_ng.h index 7228c4e46e538ebc56aed19900d0b95f0af19e5d..a95119ea63c640e6b1e3fc2b5324f8ca136082bc 100644 --- a/frameworks/core/components_ng/pattern/text/text_model_ng.h +++ b/frameworks/core/components_ng/pattern/text/text_model_ng.h @@ -51,6 +51,7 @@ public: void ReSetTextContentAlign() override; void SetTextOverflow(TextOverflow value) override; void SetMaxLines(uint32_t value) override; + void SetMinLines(uint32_t value) override; void SetTextIndent(const Dimension& value) override; void SetLineHeight(const Dimension& value) override; void SetLineSpacing(const Dimension& value) override; @@ -123,6 +124,7 @@ public: static void SetTextDecorationStyle(FrameNode* frameNode, TextDecorationStyle value); static void SetTextCase(FrameNode* frameNode, TextCase value); static void SetMaxLines(FrameNode* frameNode, uint32_t value); + static void SetMinLines(FrameNode* frameNode, uint32_t value); static void SetAdaptMinFontSize(FrameNode* frameNode, const Dimension& value); static void SetAdaptMaxFontSize(FrameNode* frameNode, const Dimension& value); static void SetFontFamily(FrameNode* frameNode, const std::vector& value); @@ -165,6 +167,7 @@ public: static TextCase GetTextCase(FrameNode* frameNode); static Dimension GetLetterSpacing(FrameNode* frameNode); static uint32_t GetMaxLines(FrameNode* frameNode); + static uint32_t GetMinLines(FrameNode* frameNode); static TextAlign GetTextAlign(FrameNode* frameNode); static TextContentAlign GetTextContentAlign(FrameNode* frameNode); static TextOverflow GetTextOverflow(FrameNode* frameNode); diff --git a/frameworks/core/components_ng/pattern/text/text_pattern.cpp b/frameworks/core/components_ng/pattern/text/text_pattern.cpp index 14780293e39018003663203736cf0fe5ee69545a..35e62325ec09ae03d5563a989f0171c141b199f9 100644 --- a/frameworks/core/components_ng/pattern/text/text_pattern.cpp +++ b/frameworks/core/components_ng/pattern/text/text_pattern.cpp @@ -70,6 +70,7 @@ const OffsetF DEFAULT_NEGATIVE_CARET_OFFSET {-1.0f, -1.0f}; constexpr int MAX_SELECTED_AI_ENTITY = 1; constexpr int32_t PREVIEW_MENU_DELAY = 600; constexpr int32_t DRAG_NODE_HIDE = 300; +constexpr int32_t DEFAULT_MIN_LINES = 0; const std::unordered_map TEXT_DETECT_MAP = { { TextDataDetectType::PHONE_NUMBER, "phoneNum" }, { TextDataDetectType::URL, "url" }, @@ -4068,6 +4069,7 @@ void TextPattern::ToJsonValue(std::unique_ptr& json, const InspectorF } else { json->PutExtAttr("actualFontSize", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str(), filter); } + json->PutExtAttr("minLines", std::to_string(GetMinLines()).c_str(), filter); json->PutExtAttr("font", GetFontInJson().c_str(), filter); json->PutExtAttr("bindSelectionMenu", GetBindSelectionMenuInJson().c_str(), filter); json->PutExtAttr("caretColor", GetCaretColor().c_str(), filter); @@ -4117,6 +4119,13 @@ std::string TextPattern::GetBindSelectionMenuInJson() const return StringUtils::RestoreBackslash(jsonArray->ToString()); } +uint32_t TextPattern::GetMinLines() const +{ + auto layoutProperty = GetLayoutProperty(); + CHECK_NULL_RETURN(layoutProperty, DEFAULT_MIN_LINES); + return layoutProperty->GetMinLines().value_or(DEFAULT_MIN_LINES); +} + std::string TextPattern::GetFontInJson() const { auto textLayoutProp = GetLayoutProperty(); diff --git a/frameworks/core/components_ng/pattern/text/text_pattern.h b/frameworks/core/components_ng/pattern/text/text_pattern.h index 13e0783c4e4506e5e5629a3957d4be810d847ba9..87d701fc3619a4e6eaccc00d36e7fd8078fa21d6 100644 --- a/frameworks/core/components_ng/pattern/text/text_pattern.h +++ b/frameworks/core/components_ng/pattern/text/text_pattern.h @@ -418,6 +418,7 @@ public: virtual void SetResultObjectText(ResultObject& resultObject, const RefPtr& spanItem); ResultObject GetSymbolSpanResultObject(RefPtr uinode, int32_t index, int32_t start, int32_t end); ResultObject GetImageResultObject(RefPtr uinode, int32_t index, int32_t start, int32_t end); + uint32_t GetMinLines() const; std::string GetFontInJson() const; std::string GetBindSelectionMenuInJson() const; std::unique_ptr GetShaderStyleInJson() const; diff --git a/frameworks/core/components_ng/pattern/text/text_styles.h b/frameworks/core/components_ng/pattern/text/text_styles.h index 5259d423700debaacee2f8f3d402f453e026b739..5381811048999db5e35f8ebc767c08f78212f24c 100644 --- a/frameworks/core/components_ng/pattern/text/text_styles.h +++ b/frameworks/core/components_ng/pattern/text/text_styles.h @@ -247,6 +247,7 @@ struct TextLineStyle { ACE_DEFINE_PROPERTY_GROUP_ITEM(TextVerticalAlign, TextVerticalAlign); ACE_DEFINE_PROPERTY_GROUP_ITEM(MaxLength, uint32_t); ACE_DEFINE_PROPERTY_GROUP_ITEM(MaxLines, uint32_t); + ACE_DEFINE_PROPERTY_GROUP_ITEM(MinLines, uint32_t); ACE_DEFINE_PROPERTY_GROUP_ITEM(OverflowMode, OverflowMode); ACE_DEFINE_PROPERTY_GROUP_ITEM(HeightAdaptivePolicy, TextHeightAdaptivePolicy); ACE_DEFINE_PROPERTY_GROUP_ITEM(TextIndent, Dimension); diff --git a/frameworks/core/interfaces/arkoala/arkoala_api.h b/frameworks/core/interfaces/arkoala/arkoala_api.h index 955c25f08875fac546b116c2b4615a4e4de9158e..babd5efb2708aec4f62fd872cdc7b00ea60143f3 100644 --- a/frameworks/core/interfaces/arkoala/arkoala_api.h +++ b/frameworks/core/interfaces/arkoala/arkoala_api.h @@ -2971,6 +2971,8 @@ struct ArkUITextModifier { void (*resetTextCase)(ArkUINodeHandle node); void (*setTextMaxLines)(ArkUINodeHandle node, ArkUI_Uint32 maxLine); void (*resetTextMaxLines)(ArkUINodeHandle node); + void (*setTextMinLines)(ArkUINodeHandle node, ArkUI_Uint32 minLine); + void (*resetTextMinLines)(ArkUINodeHandle node); void (*setTextMinFontSize)(ArkUINodeHandle node, const ArkUI_Float32 number, ArkUI_Int32 unit, void* minFontSizeRawPtr); void (*resetTextMinFontSize)(ArkUINodeHandle node); void (*setTextDraggable)(ArkUINodeHandle node, ArkUI_Uint32 draggable); @@ -3022,6 +3024,7 @@ struct ArkUITextModifier { ArkUI_Int32 (*getTextTextCase)(ArkUINodeHandle node); ArkUI_Float32 (*getTextLetterSpacing)(ArkUINodeHandle node); ArkUI_Int32 (*getTextMaxLines)(ArkUINodeHandle node); + ArkUI_Int32 (*getTextMinLines)(ArkUINodeHandle node); ArkUI_Int32 (*getTextAlign)(ArkUINodeHandle node); ArkUI_Int32 (*getTextContentAlign)(ArkUINodeHandle node); ArkUI_Int32 (*getTextTextOverflow)(ArkUINodeHandle node); diff --git a/frameworks/core/interfaces/native/node/node_text_modifier.cpp b/frameworks/core/interfaces/native/node/node_text_modifier.cpp index be655ac77e5a241faeef3ec75bc9d647e5a6e801..b128e1f3fa214f8d3972087d6f00cb7d0ae73936 100644 --- a/frameworks/core/interfaces/native/node/node_text_modifier.cpp +++ b/frameworks/core/interfaces/native/node/node_text_modifier.cpp @@ -39,6 +39,7 @@ constexpr Color DEFAULT_DECORATION_COLOR = Color(0xff000000); constexpr TextDecorationStyle DEFAULT_DECORATION_STYLE = TextDecorationStyle::SOLID; constexpr TextCase DEFAULT_TEXT_CASE = TextCase::NORMAL; constexpr uint32_t DEFAULT_MAX_LINE = Infinity(); +constexpr uint32_t DEFAULT_MIN_LINE = 0; constexpr bool DEFAULT_TEXT_DRAGGABLE = false; constexpr bool DEFAULT_TEXT_SENSITIVE = false; constexpr Dimension DEFAULT_MAX_FONT_SIZE; @@ -484,6 +485,27 @@ void ResetTextMaxLines(ArkUINodeHandle node) TextModelNG::SetMaxLines(frameNode, DEFAULT_MAX_LINE); } +void SetTextMinLines(ArkUINodeHandle node, ArkUI_Uint32 minLine) +{ + auto* frameNode = reinterpret_cast(node); + CHECK_NULL_VOID(frameNode); + TextModelNG::SetMinLines(frameNode, minLine); +} + +int32_t GetTextMinLines(ArkUINodeHandle node) +{ + auto* frameNode = reinterpret_cast(node); + CHECK_NULL_RETURN(frameNode, DEFAULT_MIN_LINE); + return TextModelNG::GetMinLines(frameNode); +} + +void ResetTextMinLines(ArkUINodeHandle node) +{ + auto* frameNode = reinterpret_cast(node); + CHECK_NULL_VOID(frameNode); + TextModelNG::SetMinLines(frameNode, DEFAULT_MIN_LINE); +} + void SetTextMinFontSize(ArkUINodeHandle node, ArkUI_Float32 number, const ArkUI_Int32 unit, void* minFontSizeRawPtr) { auto* frameNode = reinterpret_cast(node); @@ -1879,6 +1901,8 @@ const ArkUITextModifier* GetTextModifier() .resetTextCase = ResetTextTextCase, .setTextMaxLines = SetTextMaxLines, .resetTextMaxLines = ResetTextMaxLines, + .setTextMinLines = SetTextMinLines, + .resetTextMinLines = ResetTextMinLines, .setTextMinFontSize = SetTextMinFontSize, .resetTextMinFontSize = ResetTextMinFontSize, .setTextDraggable = SetTextDraggable, @@ -1926,6 +1950,7 @@ const ArkUITextModifier* GetTextModifier() .getTextTextCase = GetTextTextCase, .getTextLetterSpacing = GetTextLetterSpacing, .getTextMaxLines = GetTextMaxLines, + .getTextMinLines = GetTextMinLines, .getTextAlign = GetTextAlign, .getTextContentAlign = GetTextContentAlign, .getTextTextOverflow = GetTextTextOverflow, diff --git a/interfaces/native/native_node.h b/interfaces/native/native_node.h index 6cdbee3946b7b4ea24aa97c17aff801abec5b237..27c2cc5cc0580a52bc6d39d576bda902f1a0757e 100644 --- a/interfaces/native/native_node.h +++ b/interfaces/native/native_node.h @@ -2570,6 +2570,19 @@ typedef enum { * @since 21 */ NODE_TEXT_CONTENT_ALIGN = 1036, + + /** + * @brief Sets the minimum number of lines in the text. + * This attribute can be set, reset, and obtained as required through APIs. + * + * Format of the {@link ArkUI_AttributeItem} parameter for setting the attribute:\n + * .value[0].i32: minimum number of lines in the text.\n + * \n + * Format of the return value {@link ArkUI_AttributeItem}:\n + * .value[0].i32: minimum number of lines in the text.\n + * + */ + NODE_TEXT_MIN_LINES = 1037, /** * @brief Defines the text content attribute, which can be set, reset, and obtained as required through APIs. diff --git a/interfaces/native/node/style_modifier.cpp b/interfaces/native/node/style_modifier.cpp index 679f8fe243d05d2efd5f1be564f02a043d2c477f..f9ad70ab3c57cb920aaa0eb2ecb2e06e686c9acd 100644 --- a/interfaces/native/node/style_modifier.cpp +++ b/interfaces/native/node/style_modifier.cpp @@ -11905,6 +11905,34 @@ void ResetTextContentAlign(ArkUI_NodeHandle node) fullImpl->getNodeModifiers()->getTextModifier()->resetTextContentAlign(node->uiNodeHandle); } +int32_t SetTextMinLines(ArkUI_NodeHandle node, const ArkUI_AttributeItem* item) +{ + auto* fullImpl = GetFullImpl(); + auto actualSize = CheckAttributeItemArray(item, REQUIRED_ONE_PARAM); + if (actualSize < 0) { + return ERROR_CODE_PARAM_INVALID; + } + fullImpl->getNodeModifiers()->getTextModifier()->setTextMinLines( + node->uiNodeHandle, (int)item->value[0].f32); + return ERROR_CODE_NO_ERROR; +} + +const ArkUI_AttributeItem* GetTextMinLines(ArkUI_NodeHandle node) +{ + float resultValue = NUM_0; + if (node->type == ARKUI_NODE_TEXT) { + resultValue = GetFullImpl()->getNodeModifiers()->getTextModifier()->getTextMinLines(node->uiNodeHandle); + } + g_numberValues[0].i32 = resultValue; + return &g_attributeItem; +} + +void ResetTextMinLines(ArkUI_NodeHandle node) +{ + auto* fullImpl = GetFullImpl(); + fullImpl->getNodeModifiers()->getTextModifier()->resetTextMinLines(node->uiNodeHandle); +} + const ArkUI_AttributeItem* GetTextEllipsisMode(ArkUI_NodeHandle node) { auto fullImpl = GetFullImpl(); @@ -16504,7 +16532,7 @@ int32_t SetTextAttribute(ArkUI_NodeHandle node, int32_t subTypeId, const ArkUI_A SetTextHeightAdaptivePolicy, SetTextIndent, SetTextWordBreak, SetTextEllipsisMode, SetLineSpacing, SetFontFeature, SetTextEnableDateDetector, SetTextDataDetectorConfig, SetTextSelectedBackgroundColor, SetTextContentWithStyledString, SetHalfLeading, SetImmutableFontWeight, SetLineCount, SetOptimizeTrailingSpace, - SetTextLinearGradient, SetTextRadialGradient, SetTextVerticalAlign, SetTextContentAlign }; + SetTextLinearGradient, SetTextRadialGradient, SetTextVerticalAlign, SetTextContentAlign, SetTextMinLines}; if (static_cast(subTypeId) >= sizeof(setters) / sizeof(Setter*)) { TAG_LOGE(AceLogTag::ACE_NATIVE_NODE, "text node attribute: %{public}d NOT IMPLEMENT", subTypeId); return ERROR_CODE_NATIVE_IMPL_TYPE_NOT_SUPPORTED; @@ -16520,7 +16548,7 @@ const ArkUI_AttributeItem* GetTextAttribute(ArkUI_NodeHandle node, int32_t subTy GetTextHeightAdaptivePolicy, GetTextIndent, GetTextWordBreak, GetTextEllipsisMode, GetLineSpacing, GetFontFeature, GetTextEnableDateDetector, GetTextDataDetectorConfig, GetTextSelectedBackgroundColor, nullptr, GetHalfLeading, GetFontWeight, GetLineCount, GetOptimizeTrailingSpace, GetTextLinearGradient, - GetTextRadialGradient, GetTextVerticalAlign, GetTextContentAlign }; + GetTextRadialGradient, GetTextVerticalAlign, GetTextContentAlign, GetTextMinLines}; if (static_cast(subTypeId) >= sizeof(getters) / sizeof(Getter*) || !getters[subTypeId]) { TAG_LOGE(AceLogTag::ACE_NATIVE_NODE, "text node attribute: %{public}d NOT IMPLEMENT", subTypeId); return nullptr; @@ -16538,7 +16566,7 @@ void ResetTextAttribute(ArkUI_NodeHandle node, int32_t subTypeId) ResetTextWordBreak, ResetTextEllipsisMode, ResetLineSpacing, ResetFontFeature, ResetTextEnableDateDetector, ResetTextDataDetectorConfig, ResetTextSelectedBackgroundColor, ResetTextContentWithStyledString, ResetHalfLeading, ResetFontWeight, ResetLineCount, ResetOptimizeTrailingSpace, ResetTextLinearGradient, - ResetTextRadialGradient, ResetTextVerticalAlign, ResetTextContentAlign }; + ResetTextRadialGradient, ResetTextVerticalAlign, ResetTextContentAlign, ResetTextMinLines}; if (static_cast(subTypeId) >= sizeof(resetters) / sizeof(Resetter*)) { TAG_LOGE(AceLogTag::ACE_NATIVE_NODE, "text node attribute: %{public}d NOT IMPLEMENT", subTypeId); return; diff --git a/test/unittest/core/pattern/text/text_base.cpp b/test/unittest/core/pattern/text/text_base.cpp index 9b872c186e5335d5e5f45c25f1b707b9a784eab9..3b2af858c9d12cde5ec95cec3c63927fa378a523 100644 --- a/test/unittest/core/pattern/text/text_base.cpp +++ b/test/unittest/core/pattern/text/text_base.cpp @@ -206,6 +206,9 @@ RefPtr TextBases::CreateTextParagraph(const std::u16string& createVal if (testProperty.maxLinesValue.has_value()) { textModel.SetMaxLines(testProperty.maxLinesValue.value()); } + if (testProperty.minLinesValue.has_value()) { + textModel.SetMinLines(testProperty.minLinesValue.value()); + } if (testProperty.lineHeightValue.has_value()) { textModel.SetLineHeight(testProperty.lineHeightValue.value()); } diff --git a/test/unittest/core/pattern/text/text_base.h b/test/unittest/core/pattern/text/text_base.h index 6da1ced7a9c480d7df215ec2bc633730120e57a6..f719fd36b68b36810ad01fdbb40c7c570c74168c 100644 --- a/test/unittest/core/pattern/text/text_base.h +++ b/test/unittest/core/pattern/text/text_base.h @@ -33,6 +33,8 @@ constexpr float RK356_WIDTH = 720.0f; constexpr float RK356_HEIGHT = 1136.0f; constexpr float RK356_LOW_WIDTH = 50.0f; constexpr float RK356_LOW_HEIGHT = 20.0f; +constexpr float RK356_MAX_WIDTH = 100.0f; +constexpr float RK356_MAX_HEIGHT = 80.0f; constexpr float TEXT_WIDTH = 100.0f; constexpr float TEXT_HEIGHT = 75.0f; constexpr float LARGE_WIDTH = 1000000.0f; @@ -70,6 +72,7 @@ constexpr Dimension ADAPT_FONT_SIZE_STEP_VALUE = Dimension(10, DimensionUnit::PX const std::string CREATE_VALUE = "Hello World"; const std::u16string CREATE_VALUE_W = u"Hello World"; const SizeF CONTAINER_SIZE(RK356_WIDTH, RK356_HEIGHT); +const SizeF CONTAINER_MAX_SIZE(RK356_MAX_WIDTH, RK356_MAX_HEIGHT); const SizeF CONTAINER_LOW_SIZE(RK356_LOW_WIDTH, RK356_LOW_HEIGHT); const SizeF TEXT_SIZE(TEXT_WIDTH, TEXT_HEIGHT); const SizeF LARGE_CONTAINER_SIZE(LARGE_WIDTH, TEXT_HEIGHT); @@ -161,6 +164,7 @@ struct TestProperty { std::optional textAlignValue = std::nullopt; std::optional textOverflowValue = std::nullopt; std::optional maxLinesValue = std::nullopt; + std::optional minLinesValue = std::nullopt; std::optional lineHeightValue = std::nullopt; std::optional lineSpacingValue = std::nullopt; std::optional isOnlyBetweenLines = std::nullopt; diff --git a/test/unittest/core/pattern/text/text_testthree_ng.cpp b/test/unittest/core/pattern/text/text_testthree_ng.cpp index 580eb2ea438faf409337a9c1338e388db8f8699b..fbf10d9d53edb78f230938d384fc5c98647bdc0e 100644 --- a/test/unittest/core/pattern/text/text_testthree_ng.cpp +++ b/test/unittest/core/pattern/text/text_testthree_ng.cpp @@ -2130,6 +2130,7 @@ HWTEST_F(TextTestThreeNg, TextModelNgProperty001, TestSize.Level1) TextModelNG::SetTextDecorationStyle(node, TextDecorationStyle::SOLID); TextModelNG::SetTextCase(node, TextCase::UPPERCASE); TextModelNG::SetMaxLines(node, 10); // 10 means maxlines. + TextModelNG::SetMinLines(node, 2); // 2 means minlines. TextModelNG::SetLineSpacing(node, ADAPT_LINE_SPACING_VALUE, true); /** @@ -2148,6 +2149,7 @@ HWTEST_F(TextTestThreeNg, TextModelNgProperty001, TestSize.Level1) EXPECT_EQ(layoutProperty->GetTextDecorationStyle().value(), TextDecorationStyle::SOLID); EXPECT_EQ(layoutProperty->GetTextCase().value(), TextCase::UPPERCASE); EXPECT_EQ(layoutProperty->GetMaxLines().value(), 10); + EXPECT_EQ(layoutProperty->GetMinLines().value(), 2); EXPECT_EQ(layoutProperty->GetLineSpacing().value(), ADAPT_LINE_SPACING_VALUE); EXPECT_EQ(layoutProperty->GetIsOnlyBetweenLines().value(), true); } @@ -2722,4 +2724,177 @@ HWTEST_F(TextTestThreeNg, TextModelGetShaderStyleInJson001, TestSize.Level1) std::string json = pattern->GetShaderStyleInJson()->ToString(); EXPECT_EQ(json, "{}"); } + +/** + * @tc.name: TextModelNGMinLines001 + * @tc.desc: Test TextModelNGMinLines + * @tc.type: FUNC + */ +HWTEST_F(TextTestThreeNg, TextMinLines001, TestSize.Level1) +{ + TextModelNG textModelNG; + textModelNG.Create(CREATE_VALUE_W); + auto frameNode = ViewStackProcessor::GetInstance()->GetMainFrameNode(); + ASSERT_NE(frameNode, nullptr); + auto pattern = frameNode->GetPattern(); + ASSERT_NE(pattern, nullptr); + auto layoutProperty = frameNode->GetLayoutProperty(); + ASSERT_NE(layoutProperty, nullptr); + auto textLayoutProperty = AceType::DynamicCast(layoutProperty); + ASSERT_NE(textLayoutProperty, nullptr); + textModelNG.SetMinLines(5); + EXPECT_EQ(textLayoutProperty->GetMinLines().value(), 5); + textLayoutProperty->UpdateMinLines(9); + EXPECT_EQ(TextModelNG::GetMinLines(frameNode), 9); +} + +HWTEST_F(TextTestThreeNg, CalcHeightWithMinLines001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create textFrameNode. + */ + auto textFrameNode = FrameNode::CreateFrameNode(V2::TEXT_ETS_TAG, 0, AceType::MakeRefPtr()); + ASSERT_NE(textFrameNode, nullptr); + RefPtr geometryNode = AceType::MakeRefPtr(); + ASSERT_NE(geometryNode, nullptr); + RefPtr layoutWrapperNode = + AceType::MakeRefPtr(textFrameNode, geometryNode, textFrameNode->GetLayoutProperty()); + auto textPattern = textFrameNode->GetPattern(); + ASSERT_NE(textPattern, nullptr); + auto textLayoutProperty = textPattern->GetLayoutProperty(); + ASSERT_NE(textLayoutProperty, nullptr); + + /** + * @tc.steps: step2. set textLayoutProperty. + */ + textLayoutProperty->UpdateContent(CREATE_VALUE_W); + LayoutConstraintF parentLayoutConstraint; + parentLayoutConstraint.maxSize = CONTAINER_MAX_SIZE; + parentLayoutConstraint.minSize = CONTAINER_LOW_SIZE; + + /** + * @tc.steps: step3. create textLayoutAlgorithm and call MeasureContent function. + * @tc.expected: The width of the return value of MeasureContent is equal to 100.0f + */ + auto layoutWrapper = textFrameNode->GetLayoutAlgorithm(); + ASSERT_NE(layoutWrapper, nullptr); + auto textLayoutAlgorithm = AceType::DynamicCast(layoutWrapper->GetLayoutAlgorithm()); + ASSERT_NE(textLayoutAlgorithm, nullptr); + auto paragraph = MockParagraph::GetOrCreateMockParagraph(); + EXPECT_CALL(*paragraph, GetHeight()).WillRepeatedly(Return(10.f)); + EXPECT_CALL(*paragraph, GetLineCount()).WillRepeatedly(Return(1)); + textLayoutAlgorithm->paragraphManager_->AddParagraph({ .paragraph = paragraph, .start = 0, .end = 100 }); + auto height = 10.0f; + auto firstHeight = height; + textLayoutProperty->UpdateMinLines(5); + textLayoutAlgorithm->CalcHeightWithMinLines(height, AccessibilityManager::RawPtr(layoutWrapperNode), + parentLayoutConstraint); + auto secondHeight = height; + EXPECT_EQ(secondHeight, firstHeight * 5); + textLayoutProperty->UpdateMaxLines(3); + textLayoutAlgorithm->CalcHeightWithMinLines(height, AccessibilityManager::RawPtr(layoutWrapperNode), + parentLayoutConstraint); + auto thirdHeight = height; + EXPECT_EQ(thirdHeight, firstHeight * 5); + textLayoutProperty->UpdateMaxLines(6); + textLayoutAlgorithm->CalcHeightWithMinLines(height, AccessibilityManager::RawPtr(layoutWrapperNode), + parentLayoutConstraint); + auto fourthHeight = height; + EXPECT_EQ(fourthHeight, firstHeight * 5); +} + +HWTEST_F(TextTestThreeNg, CalcHeightWithMinLines002, TestSize.Level1) +{ + /** + * @tc.steps: step1. create textFrameNode. + */ + auto textFrameNode = FrameNode::CreateFrameNode(V2::TEXT_ETS_TAG, 0, AceType::MakeRefPtr()); + ASSERT_NE(textFrameNode, nullptr); + RefPtr geometryNode = AceType::MakeRefPtr(); + ASSERT_NE(geometryNode, nullptr); + RefPtr layoutWrapperNode = + AceType::MakeRefPtr(textFrameNode, geometryNode, textFrameNode->GetLayoutProperty()); + auto textPattern = textFrameNode->GetPattern(); + ASSERT_NE(textPattern, nullptr); + auto textLayoutProperty = textPattern->GetLayoutProperty(); + ASSERT_NE(textLayoutProperty, nullptr); + + /** + * @tc.steps: step2. set textLayoutProperty. + */ + textLayoutProperty->UpdateContent(CREATE_VALUE_W); + LayoutConstraintF parentLayoutConstraint; + parentLayoutConstraint.maxSize = CONTAINER_MAX_SIZE; + parentLayoutConstraint.minSize = CONTAINER_LOW_SIZE; + /** + * @tc.steps: step3. create textLayoutAlgorithm and call MeasureContent function. + * @tc.expected: The width of the return value of MeasureContent is equal to 100.0f + */ + auto layoutWrapper = textFrameNode->GetLayoutAlgorithm(); + ASSERT_NE(layoutWrapper, nullptr); + auto textLayoutAlgorithm = AceType::DynamicCast(layoutWrapper->GetLayoutAlgorithm()); + ASSERT_NE(textLayoutAlgorithm, nullptr); + auto paragraph = MockParagraph::GetOrCreateMockParagraph(); + EXPECT_CALL(*paragraph, GetHeight()).WillRepeatedly(Return(10.f)); + EXPECT_CALL(*paragraph, GetLineCount()).WillRepeatedly(Return(1)); + textLayoutAlgorithm->paragraphManager_->AddParagraph({ .paragraph = paragraph, .start = 0, .end = 100 }); + auto height = 10.0f; + auto firstHeight = height; + textLayoutProperty->UpdateMinLines(1); + textLayoutAlgorithm->CalcHeightWithMinLines(height, AccessibilityManager::RawPtr(layoutWrapperNode), + parentLayoutConstraint); + auto secondHeight = height; + EXPECT_EQ(secondHeight, firstHeight * 2); + textLayoutProperty->UpdateMinLines(10); + textLayoutAlgorithm->CalcHeightWithMinLines(height, AccessibilityManager::RawPtr(layoutWrapperNode), + parentLayoutConstraint); + auto thirdHeight = height; + EXPECT_EQ(thirdHeight, firstHeight * 8); +} + +HWTEST_F(TextTestThreeNg, CalcHeightWithMinLines003, TestSize.Level1) +{ + /** + * @tc.steps: step1. create textFrameNode. + */ + auto textFrameNode = FrameNode::CreateFrameNode(V2::TEXT_ETS_TAG, 0, AceType::MakeRefPtr()); + ASSERT_NE(textFrameNode, nullptr); + RefPtr geometryNode = AceType::MakeRefPtr(); + ASSERT_NE(geometryNode, nullptr); + RefPtr layoutWrapperNode = + AceType::MakeRefPtr(textFrameNode, geometryNode, textFrameNode->GetLayoutProperty()); + auto textPattern = textFrameNode->GetPattern(); + ASSERT_NE(textPattern, nullptr); + auto textLayoutProperty = textPattern->GetLayoutProperty(); + ASSERT_NE(textLayoutProperty, nullptr); + + /** + * @tc.steps: step2. set textLayoutProperty. + */ + textLayoutProperty->UpdateContent(CREATE_VALUE_W); + LayoutConstraintF parentLayoutConstraint; + parentLayoutConstraint.maxSize = CONTAINER_MAX_SIZE; + parentLayoutConstraint.minSize = CONTAINER_LOW_SIZE; + + /** + * @tc.steps: step3. create textLayoutAlgorithm and call MeasureContent function. + * @tc.expected: The width of the return value of MeasureContent is equal to 100.0f + */ + auto layoutWrapper = textFrameNode->GetLayoutAlgorithm(); + ASSERT_NE(layoutWrapper, nullptr); + auto textLayoutAlgorithm = AceType::DynamicCast(layoutWrapper->GetLayoutAlgorithm()); + ASSERT_NE(textLayoutAlgorithm, nullptr); + auto paragraph = MockParagraph::GetOrCreateMockParagraph(); + EXPECT_CALL(*paragraph, GetHeight()).WillRepeatedly(Return(10.f)); + EXPECT_CALL(*paragraph, GetLineCount()).WillRepeatedly(Return(1)); + textLayoutAlgorithm->paragraphManager_->AddParagraph({ .paragraph = paragraph, .start = 0, .end = 100 }); + auto height = 50.0f; + auto originHeight = height; + textLayoutProperty->UpdateMinLines(3); + textLayoutAlgorithm->CalcHeightWithMinLines(height, AccessibilityManager::RawPtr(layoutWrapperNode), + parentLayoutConstraint); + auto lastHeight = height; + EXPECT_EQ(lastHeight, originHeight); +} + } // namespace OHOS::Ace::NG