From 0c985cb7725f80becfb1815003a9fc26017af61e Mon Sep 17 00:00:00 2001 From: zhanghang Date: Wed, 11 Jun 2025 11:33:09 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20bindtips=E6=94=AF=E6=8C=81=E8=B7=9F?= =?UTF-8?q?=E9=9A=8F=E9=BC=A0=E6=A0=87tdd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhanghang Change-Id: I379250b46cbceec43ba0432f555d28d7d50bebf3 --- adapter/ohos/entrance/ace_container.h | 8 + frameworks/core/common/container.h | 4 + .../bubble/bubble_layout_algorithm.cpp | 47 +- .../pattern/bubble/bubble_layout_algorithm.h | 1 + test/mock/core/common/mock_container.h | 1 + ...w_abstract_test_ng_for_property_config.cpp | 1 + .../core/event/input_event_test_ng.cpp | 74 +++ .../pattern/bubble/bubble_tips_test_ng.cpp | 479 +++++++++++++++++- 8 files changed, 611 insertions(+), 4 deletions(-) diff --git a/adapter/ohos/entrance/ace_container.h b/adapter/ohos/entrance/ace_container.h index 8a037cbfe3d..97101c91f58 100644 --- a/adapter/ohos/entrance/ace_container.h +++ b/adapter/ohos/entrance/ace_container.h @@ -817,6 +817,14 @@ public: return uiWindow_->GetFreeMultiWindowModeEnabledState(); } + Rect GetGlobalScaledRect() const override + { + CHECK_NULL_RETURN(uiWindow_, Rect()); + Rosen::Rect rect{}; + uiWindow_->GetGlobalScaledRect(rect); + return Rect(rect.posX_, rect.posY_, rect.width_, rect.height_); + } + bool IsWaterfallWindow() const override { CHECK_NULL_RETURN(uiWindow_, false); diff --git a/frameworks/core/common/container.h b/frameworks/core/common/container.h index 556ef609699..a74749efc72 100755 --- a/frameworks/core/common/container.h +++ b/frameworks/core/common/container.h @@ -697,6 +697,10 @@ public: { return false; } + virtual Rect GetGlobalScaledRect() const + { + return Rect(); + } virtual Rect GetUIExtensionHostWindowRect() { diff --git a/frameworks/core/components_ng/pattern/bubble/bubble_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/bubble/bubble_layout_algorithm.cpp index a7bd5fdc43e..55655853c2b 100644 --- a/frameworks/core/components_ng/pattern/bubble/bubble_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/bubble/bubble_layout_algorithm.cpp @@ -331,6 +331,47 @@ void BubbleLayoutAlgorithm::FitAvailableRect(LayoutWrapper* layoutWrapper, bool } } +void BubbleLayoutAlgorithm::FitMouseOffset(LayoutWrapper* layoutWrapper) +{ + CHECK_EQUAL_VOID(followCursor_, false); + CHECK_EQUAL_VOID(expandDisplay_, true); + CHECK_NULL_VOID(layoutWrapper); + auto host = layoutWrapper->GetHostNode(); + CHECK_NULL_VOID(host); + RefPtr pipelineContext = host->GetContextRefPtr(); + CHECK_NULL_VOID(pipelineContext); + auto containerId = pipelineContext->GetInstanceId(); + auto container = AceEngine::Get().GetContainer(containerId); + CHECK_NULL_VOID(container); + RefPtr parentContainer; + if (container->IsSubContainer()) { + auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(containerId); + parentContainer = AceEngine::Get().GetContainer(parentContainerId); + CHECK_NULL_VOID(parentContainer); + pipelineContext = AceType::DynamicCast(parentContainer->GetPipelineContext()); + CHECK_NULL_VOID(pipelineContext); + } + CHECK_NULL_VOID(parentContainer); + auto displayWindowRect = pipelineContext->GetDisplayWindowRectInfo(); + + float scaleX = 1.0f; + float scaleY = 1.0f; + if (container->IsSubContainer()) { + auto rect = parentContainer->GetGlobalScaledRect(); + if (!NearZero(displayWindowRect.Width())) { + scaleX = rect.Width() / static_cast(displayWindowRect.Width()); + } + if (!NearZero(displayWindowRect.Height())) { + scaleY = rect.Height() / static_cast(displayWindowRect.Height()); + } + } + if (!NearZero(scaleX) && !NearZero(scaleY)) { + targetOffset_ -= OffsetF(displayWindowRect.GetOffset().GetX(), displayWindowRect.GetOffset().GetY()); + targetOffset_.SetX(targetOffset_.GetX() / scaleX); + targetOffset_.SetY(targetOffset_.GetY() / scaleY); + } +} + void BubbleLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) { CHECK_NULL_VOID(layoutWrapper); @@ -345,6 +386,7 @@ void BubbleLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper) auto bubbleNode = layoutWrapper->GetHostNode(); CHECK_NULL_VOID(bubbleNode); FitAvailableRect(layoutWrapper, showInSubWindow); + FitMouseOffset(layoutWrapper); const auto& layoutConstraint = bubbleLayoutProperty->GetLayoutConstraint(); if (!layoutConstraint) { LOGE("fail to measure bubble due to layoutConstraint is nullptr"); @@ -1181,10 +1223,9 @@ void BubbleLayoutAlgorithm::MeasureTipsRegion( childPosition = GetPositionWithPlacementNew(childSize, topPosition, bottomPosition, arrowOffset); UpdateChildPosition(childPosition); position = FitToScreenNew(childPosition, ALIGNMENT_STEP_OFFSET, i, childSize, avoidArrowOffset, false); - if (NearEqual(position, OffsetF(0.0f, 0.0f))) { - continue; + if (!NearEqual(position, OffsetF(0.0f, 0.0f))) { + break; } - break; } if (placement_ == Placement::NONE) { SizeF newSize; diff --git a/frameworks/core/components_ng/pattern/bubble/bubble_layout_algorithm.h b/frameworks/core/components_ng/pattern/bubble/bubble_layout_algorithm.h index 56b7b8f094e..2b8d98fea8f 100644 --- a/frameworks/core/components_ng/pattern/bubble/bubble_layout_algorithm.h +++ b/frameworks/core/components_ng/pattern/bubble/bubble_layout_algorithm.h @@ -262,6 +262,7 @@ private: void UpdateHostWindowRect(); void HandleKeyboard(LayoutWrapper* layoutWrapper, bool showInSubWindow); void FitAvailableRect(LayoutWrapper* layoutWrapper, bool showInSubWindow); + void FitMouseOffset(LayoutWrapper* layoutWrapper); void UpdateTextNodeMaxLines(const RefPtr& childWrapper, const LayoutConstraintF& layoutConstraint); void MeasureTipsRegion(const RefPtr& childWrapper, const LayoutConstraintF& childContraint); diff --git a/test/mock/core/common/mock_container.h b/test/mock/core/common/mock_container.h index 243b22f5073..38c72a79e69 100644 --- a/test/mock/core/common/mock_container.h +++ b/test/mock/core/common/mock_container.h @@ -153,6 +153,7 @@ public: MOCK_METHOD(void, TriggerGarbageCollection, (), (override)); MOCK_METHOD(bool, WindowIsShow, (), (const, override)); MOCK_METHOD(bool, IsMainWindow, (), (const, override)); + MOCK_METHOD(Rect, GetGlobalScaledRect, (), (const, override)); static RefPtr container_; static ColorMode mockColorMode_; diff --git a/test/unittest/core/base/view_abstract_test_ng_for_property_config.cpp b/test/unittest/core/base/view_abstract_test_ng_for_property_config.cpp index fabab2be653..6596082a571 100644 --- a/test/unittest/core/base/view_abstract_test_ng_for_property_config.cpp +++ b/test/unittest/core/base/view_abstract_test_ng_for_property_config.cpp @@ -1490,6 +1490,7 @@ HWTEST_F(ViewAbstractTestNg, ViewAbstractAddHoverEventForTipsTest002, TestSize.L ASSERT_NE(overlayManager, nullptr); auto popupInfo = overlayManager->GetPopupInfo(targetNode->GetId()); + param->SetAnchorType(TipsAnchorType::CURSOR); ViewAbstract::AddHoverEventForTips(param, targetNode, popupInfo, false); auto eventHub = targetNode->GetOrCreateEventHub(); ASSERT_NE(eventHub, nullptr); diff --git a/test/unittest/core/event/input_event_test_ng.cpp b/test/unittest/core/event/input_event_test_ng.cpp index 918bfe9400c..26341aeaf9a 100644 --- a/test/unittest/core/event/input_event_test_ng.cpp +++ b/test/unittest/core/event/input_event_test_ng.cpp @@ -294,6 +294,59 @@ HWTEST_F(InputEventTestNg, InputEventTest009, TestSize.Level1) EXPECT_NE(inputEventHub, nullptr); } +/** + * @tc.name: InputEventTest010 + * @tc.desc: test InputEvent + * @tc.type: FUNC + */ +HWTEST_F(InputEventTestNg, InputEventTest010, TestSize.Level1) +{ + auto eventHub = AceType::MakeRefPtr(); + auto frameNode = AceType::MakeRefPtr(V2::TEXT_ETS_TAG, -1, AceType::MakeRefPtr()); + eventHub->AttachHost(frameNode); + auto inputEventHub = AceType::MakeRefPtr(AceType::WeakClaim(AceType::RawPtr(eventHub))); + EXPECT_NE(inputEventHub, nullptr); + inputEventHub->mouseEventActuator_ = + AceType::MakeRefPtr(AceType::WeakClaim(AceType::RawPtr(inputEventHub))); + int32_t count = 0; + MouseInfo mouse; + TouchTestResult result; + auto getEventTargetImpl = eventHub->CreateGetEventTargetImpl(); + inputEventHub->mouseEventActuator_->OnCollectMouseEventForTips(COORDINATE_OFFSET, getEventTargetImpl, result); + + auto mouseTask = [&count](MouseInfo& info) { count++; }; + auto mouseEvent1 = AceType::MakeRefPtr(mouseTask); + mouseEvent1->SetIstips(true); + mouseEvent1->SetTipsFollowCursor(true); + inputEventHub->mouseEventActuator_->inputEvents_.clear(); + inputEventHub->AddOnMouseEvent(std::move(mouseEvent1)); + inputEventHub->mouseEventActuator_->OnCollectMouseEventForTips(COORDINATE_OFFSET, getEventTargetImpl, result); + inputEventHub->mouseEventActuator_->mouseEventTarget_->onMouseCallback_(mouse); + EXPECT_EQ(count, 1); + + auto mouseEvent2 = AceType::MakeRefPtr(mouseTask); + mouseEvent2->SetIstips(true); + inputEventHub->mouseEventActuator_->inputEvents_.clear(); + inputEventHub->AddOnMouseEvent(std::move(mouseEvent2)); + inputEventHub->mouseEventActuator_->OnCollectMouseEventForTips(COORDINATE_OFFSET, getEventTargetImpl, result); + inputEventHub->mouseEventActuator_->mouseEventTarget_->onMouseCallback_(mouse); + EXPECT_EQ(count, 1); + + auto mouseEvent3 = AceType::MakeRefPtr(mouseTask); + mouseEvent3->SetTipsFollowCursor(true); + inputEventHub->mouseEventActuator_->inputEvents_.clear(); + inputEventHub->AddOnMouseEvent(std::move(mouseEvent3)); + inputEventHub->mouseEventActuator_->OnCollectMouseEventForTips(COORDINATE_OFFSET, getEventTargetImpl, result); + inputEventHub->mouseEventActuator_->mouseEventTarget_->onMouseCallback_(mouse); + EXPECT_EQ(count, 1); + + inputEventHub->mouseEventActuator_->inputEvents_.clear(); + inputEventHub->AddOnMouseEvent(nullptr); + inputEventHub->mouseEventActuator_->OnCollectMouseEventForTips(COORDINATE_OFFSET, getEventTargetImpl, result); + inputEventHub->mouseEventActuator_->mouseEventTarget_->onMouseCallback_(mouse); + EXPECT_EQ(count, 1); +} + /** * @tc.name: OnCollectPenHoverEventTest001 * @tc.desc: test InputEvent @@ -572,4 +625,25 @@ HWTEST_F(InputEventTestNg, OnCollectAxisEvent001, TestSize.Level1) inputEventHub->axisEventActuator_->axisEventTarget_->onAxisCallback_(axisInfo); EXPECT_NE(inputEventHub, nullptr); } + +/** + * @tc.name: ProcessTipsMouseTestHit001 + * @tc.desc: test ProcessTipsMouseTestHit + * @tc.type: FUNC + */ +HWTEST_F(InputEventTestNg, ProcessTipsMouseTestHit001, TestSize.Level1) +{ + auto eventHub = AceType::MakeRefPtr(); + auto frameNode = AceType::MakeRefPtr(V2::TEXT_ETS_TAG, -1, AceType::MakeRefPtr()); + eventHub->AttachHost(frameNode); + + auto inputEventHub = AceType::MakeRefPtr(AceType::WeakClaim(AceType::RawPtr(eventHub))); + EXPECT_NE(inputEventHub, nullptr); + inputEventHub->mouseEventActuator_ = + AceType::MakeRefPtr(AceType::WeakClaim(AceType::RawPtr(inputEventHub))); + + TouchTestResult result; + inputEventHub->ProcessTipsMouseTestHit(COORDINATE_OFFSET, result); + EXPECT_EQ(result.size(), 0); +} } // namespace OHOS::Ace::NG \ No newline at end of file diff --git a/test/unittest/core/pattern/bubble/bubble_tips_test_ng.cpp b/test/unittest/core/pattern/bubble/bubble_tips_test_ng.cpp index 4473393c5fb..27cc5d8d7a9 100755 --- a/test/unittest/core/pattern/bubble/bubble_tips_test_ng.cpp +++ b/test/unittest/core/pattern/bubble/bubble_tips_test_ng.cpp @@ -23,7 +23,10 @@ #include "test/mock/core/render/mock_paragraph.h" #include "base/memory/ace_type.h" +#include "base/subwindow/subwindow_manager.h" #include "base/utils/system_properties.h" +#include "core/common/ace_engine.h" +#include "core/components/theme/shadow_theme.h" #include "core/components_ng/base/frame_node.h" #include "core/components_ng/pattern/bubble/bubble_event_hub.h" #include "core/components_ng/pattern/bubble/bubble_layout_algorithm.h" @@ -32,6 +35,7 @@ #include "core/components_ng/pattern/bubble/bubble_render_property.h" #include "core/components_ng/pattern/bubble/bubble_view.h" #include "core/components_ng/pattern/button/button_pattern.h" +#include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h" using namespace testing; using namespace testing::ext; @@ -51,9 +55,14 @@ constexpr float LINE_HEIGHT = 16.0f; constexpr float TEXT_WIDTH = 14.0f; constexpr float TEXT_PADDING = 12.0f; constexpr int32_t DOUBLE = 2; +constexpr float WIDTH = 100.0f; +constexpr float HEIGHT = 100.0f; constexpr Dimension TIPS_MARGIN_SPACE = 8.0_vp; constexpr Dimension MOUSE_WIDTH = 16.0_vp; constexpr Dimension MOUSE_HEIGHT = 24.0_vp; +constexpr Dimension KEYBOARD_SPACE = 8.0_vp; +const SizeF WRAPPER_SIZE = { 1000.0f, 1000.0f }; +const SafeAreaInsets::Inset KEYBOARD_INSET = { .start = 500.f, .end = 1000.f }; } // namespace class BubbleTipsTestNg : public testing::Test { public: @@ -69,10 +78,12 @@ protected: SizeF ConstructParagraphs(const std::u16string& text, int32_t lineCount); RefPtr MeasureTipsRegion(const std::u16string& text, const Offset& mouseOffset); static LayoutConstraintF childLayoutConstraint; + static RefPtr popupTheme; RefPtr pManager = AceType::MakeRefPtr(); }; LayoutConstraintF BubbleTipsTestNg::childLayoutConstraint; +RefPtr BubbleTipsTestNg::popupTheme; void BubbleTipsTestNg::SetUpTestCase() { @@ -84,6 +95,7 @@ void BubbleTipsTestNg::SetUpTestCase() MockContainer::Current()->pipelineContext_->dipScale_ = 1.5f; auto themeManager = AceType::MakeRefPtr(); MockPipelineContext::GetCurrent()->SetThemeManager(themeManager); + BubbleTipsTestNg::popupTheme = AceType::MakeRefPtr(); EXPECT_CALL(*themeManager, GetTheme(_)).WillRepeatedly([](ThemeType type) -> RefPtr { if (type == TextTheme::TypeId()) { return AceType::MakeRefPtr(); @@ -91,8 +103,10 @@ void BubbleTipsTestNg::SetUpTestCase() return AceType::MakeRefPtr(); } else if (type == ButtonTheme::TypeId()) { return AceType::MakeRefPtr(); + } else if (type == ShadowTheme::TypeId()) { + return AceType::MakeRefPtr(); } else { - return AceType::MakeRefPtr(); + return BubbleTipsTestNg::popupTheme; } }); childLayoutConstraint.maxSize = MAX_SIZE; @@ -128,6 +142,7 @@ RefPtr BubbleTipsTestNg::CreateTipsNode(const RefPtr& par auto tipsNode = BubbleView::CreateBubbleNode(targetTag, targetId, param, AceType::MakeRefPtr(spanString)); auto bubblePattern = tipsNode->GetPattern(); + bubblePattern->mouseOffset_ = Offset(); tipsNode->layoutAlgorithm_ = AceType::MakeRefPtr(bubblePattern->CreateLayoutAlgorithm()); auto layoutAlgorithm = AceType::DynamicCast(tipsNode->layoutAlgorithm_->GetLayoutAlgorithm()); @@ -247,4 +262,466 @@ HWTEST_F(BubbleTipsTestNg, MesureTipsRegion001, TestSize.Level1) layoutAlgorithm = MeasureTipsRegion(TIPS_MSG_4, offset); EXPECT_EQ(layoutAlgorithm->tipsPlacement_, Placement::BOTTOM); } + +/** + * @tc.name: ResetTipsMaxLines001 + * @tc.desc: Test ResetTipsMaxLines. + * @tc.type: FUNC + */ +HWTEST_F(BubbleTipsTestNg, ResetTipsMaxLines001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create bubble and get frameNode. + */ + auto tipsNode = CreateTipsNode(CreateTipsParamForCursor(), TIPS_MSG_1); + tipsNode->geometryNode_ = AceType::MakeRefPtr(); + tipsNode->RemoveChildAtIndex(0); + auto columnNode = + FrameNode::GetOrCreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), + []() { return AceType::MakeRefPtr(true); }); + auto buttonNode = FrameNode::GetOrCreateFrameNode(V2::BUTTON_ETS_TAG, + ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr(); }); + buttonNode->MountToParent(columnNode); + columnNode->MountToParent(tipsNode); + + auto bubbleProp = AceType::DynamicCast(tipsNode->GetLayoutProperty()); + bubbleProp->layoutConstraint_ = LayoutConstraintF(); + bubbleProp->contentConstraint_ = LayoutConstraintF(); + + const auto& children = tipsNode->GetAllChildrenWithBuild(); + auto childWrapper = children.front(); + auto node = childWrapper->GetAllChildrenWithBuild().front(); + auto layoutProps = AceType::DynamicCast(node->GetLayoutProperty()); + EXPECT_EQ(layoutProps, nullptr); + + /** + * @tc.steps: step2. test ResetTipsMaxLines. + */ + auto layoutAlgorithm = + AceType::DynamicCast(tipsNode->layoutAlgorithm_->GetLayoutAlgorithm()); + layoutAlgorithm->Measure(AceType::RawPtr(tipsNode)); + layoutAlgorithm->followCursor_ = true; + layoutAlgorithm->targetOffset_ = OffsetF(DEVICE_WIDTH * HALF, DEVICE_HEIGHT * HALF); + layoutAlgorithm->targetSize_ = SizeF(MOUSE_WIDTH.ConvertToPx(), MOUSE_HEIGHT.ConvertToPx()); + layoutAlgorithm->followTransformOfTarget_ = false; + layoutAlgorithm->InitTargetSizeAndPosition(false, nullptr); + EXPECT_EQ(layoutAlgorithm->targetOffset_, OffsetF(DEVICE_WIDTH * HALF, DEVICE_HEIGHT * HALF)); +} + +/** + * @tc.name: ResetTipsMaxLines002 + * @tc.desc: Test ResetTipsMaxLines. + * @tc.type: FUNC + */ +HWTEST_F(BubbleTipsTestNg, ResetTipsMaxLines002, TestSize.Level1) +{ + /** + * @tc.steps: step1. create bubble and get frameNode. + */ + auto tipsNode = CreateTipsNode(CreateTipsParamForCursor(), TIPS_MSG_1); + auto bubbleProp = AceType::DynamicCast(tipsNode->GetLayoutProperty()); + bubbleProp->layoutConstraint_ = LayoutConstraintF(); + bubbleProp->contentConstraint_ = LayoutConstraintF(); + + auto layoutAlgorithm = + AceType::DynamicCast(tipsNode->layoutAlgorithm_->GetLayoutAlgorithm()); + const auto& children = tipsNode->GetAllChildrenWithBuild(); + auto childWrapper = children.front(); + auto text = childWrapper->GetAllChildrenWithBuild().front(); + auto layoutProps = AceType::DynamicCast(text->GetLayoutProperty()); + /** + * @tc.steps: step2. test ResetTipsMaxLines. + */ + layoutProps->UpdateMaxLines(DOUBLE); + layoutAlgorithm->Measure(AceType::RawPtr(tipsNode)); + EXPECT_NE(layoutProps->GetMaxLinesValue(0), DOUBLE); +} + +/** + * @tc.name: TipsFitAvailableRect001 + * @tc.desc: Test FitAvailableRect for tips. + * @tc.type: FUNC + */ +HWTEST_F(BubbleTipsTestNg, TipsFitAvailableRect001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create bubble and get frameNode. + */ + auto targetNode = CreateTargetNode(); + auto id = targetNode->GetId(); + auto targetTag = targetNode->GetTag(); + auto popupId = ElementRegister::GetInstance()->MakeUniqueId(); + auto frameNode = + FrameNode::CreateFrameNode(V2::POPUP_ETS_TAG, popupId, AceType::MakeRefPtr(id, targetTag)); + auto bubblePattern = frameNode->GetPattern(); + ASSERT_NE(bubblePattern, nullptr); + auto layoutAlgorithm = AceType::DynamicCast(bubblePattern->CreateLayoutAlgorithm()); + ASSERT_NE(layoutAlgorithm, nullptr); + RefPtr geometryNode = AceType::MakeRefPtr(); + ASSERT_NE(geometryNode, nullptr); + RefPtr layoutWrapper = + AceType::MakeRefPtr(frameNode, geometryNode, frameNode->GetLayoutProperty()); + ASSERT_NE(layoutWrapper, nullptr); + auto pipelineContext = frameNode->GetContextRefPtr(); + ASSERT_NE(pipelineContext, nullptr); + /** + * @tc.steps: step2. test FitAvailableRect. + */ + pipelineContext->UpdateDisplayAvailableRect(Rect(0.0f, 0.0f, 0.0f, 0.0f)); + layoutAlgorithm->followCursor_ = true; + layoutAlgorithm->expandDisplay_ = true; + layoutAlgorithm->FitAvailableRect(AceType::RawPtr(layoutWrapper), true); + EXPECT_EQ(layoutAlgorithm->wrapperSize_, SizeF(0.0f, 0.0f)); +} + +/** + * @tc.name: TipsHandleKeyboardTest + * @tc.desc: Test HandleKeyboard function. + * @tc.type: FUNC + */ +HWTEST_F(BubbleTipsTestNg, TipsHandleKeyboardTest, TestSize.Level1) +{ + /** + * @tc.steps: step1. create bubble and get frameNode. + */ + auto targetNode = CreateTargetNode(); + auto targetId = targetNode->GetId(); + auto targetTag = targetNode->GetTag(); + auto popupId = ElementRegister::GetInstance()->MakeUniqueId(); + auto frameNode = + FrameNode::CreateFrameNode(V2::POPUP_ETS_TAG, popupId, AceType::MakeRefPtr(targetId, targetTag)); + ASSERT_NE(frameNode, nullptr); + + auto bubblePattern = frameNode->GetPattern(); + ASSERT_NE(bubblePattern, nullptr); + bubblePattern->SetAvoidKeyboard(true); + auto layoutAlgorithm = AceType::DynamicCast(bubblePattern->CreateLayoutAlgorithm()); + ASSERT_NE(layoutAlgorithm, nullptr); + RefPtr geometryNode = AceType::MakeRefPtr(); + EXPECT_FALSE(geometryNode == nullptr); + RefPtr layoutWrapper = + AceType::MakeRefPtr(frameNode, geometryNode, frameNode->GetLayoutProperty()); + + /** + * @tc.steps: step2. test HandleKeyboard. + */ + auto pipeline = MockPipelineContext::GetCurrentContext(); + CHECK_NULL_VOID(pipeline); + auto manager = pipeline->GetSafeAreaManager(); + manager->keyboardInset_ = KEYBOARD_INSET; + layoutAlgorithm->wrapperSize_ = WRAPPER_SIZE; + layoutAlgorithm->marginBottom_ = .0f; + layoutAlgorithm->followCursor_ = true; + layoutAlgorithm->HandleKeyboard(AceType::RawPtr(layoutWrapper), false); + EXPECT_EQ(layoutAlgorithm->wrapperSize_.Height(), + WRAPPER_SIZE.Height() - KEYBOARD_INSET.Length() + KEYBOARD_SPACE.ConvertToPx()); + layoutAlgorithm->GetChildPositionNew(SizeF(), frameNode->GetLayoutProperty(), nullptr); + + layoutAlgorithm->wrapperSize_ = WRAPPER_SIZE; + layoutAlgorithm->marginBottom_ = .0f; + layoutAlgorithm->followCursor_ = false; + layoutAlgorithm->HandleKeyboard(AceType::RawPtr(layoutWrapper), false); + EXPECT_EQ(layoutAlgorithm->wrapperSize_.Height(), WRAPPER_SIZE.Height() - KEYBOARD_INSET.Length()); +} + +/** + * @tc.name: UpdateTextNodeMaxLinesTest001 + * @tc.desc: Test UpdateTextNodeMaxLines function. + * @tc.type: FUNC + */ +HWTEST_F(BubbleTipsTestNg, UpdateTextNodeMaxLinesTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create bubble and get frameNode. + */ + auto tipsNode = CreateTipsNode(CreateTipsParamForCursor(), TIPS_MSG_1); + auto layoutAlgorithm = + AceType::DynamicCast(tipsNode->layoutAlgorithm_->GetLayoutAlgorithm()); + pManager->SetParagraphs({}); + auto paragraph = MockParagraph::GetOrCreateMockParagraph(); + EXPECT_CALL(*paragraph, GetLineCount()).WillRepeatedly(Return(1)); + EXPECT_CALL(*paragraph, GetHeight()).WillRepeatedly(Return(LINE_HEIGHT)); + pManager->AddParagraph({ .paragraph = paragraph, .start = 0, .end = TIPS_MSG_1.length() }); + + /** + * @tc.steps: step2. test UpdateTextNodeMaxLines. + */ + LayoutConstraintF constraint; + constraint.maxSize = WRAPPER_SIZE; + layoutAlgorithm->UpdateTextNodeMaxLines(tipsNode->GetChildByIndex(0), constraint); + auto text = tipsNode->GetChildByIndex(0)->GetChildByIndex(0); + auto layoutProps = AceType::DynamicCast(text->GetLayoutProperty()); + EXPECT_EQ(layoutProps->GetMaxLines(), std::floor(WRAPPER_SIZE.Height() / LINE_HEIGHT) - 1); + + tipsNode->RemoveChildAtIndex(0); + auto columnNode = + FrameNode::GetOrCreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), + []() { return AceType::MakeRefPtr(true); }); + auto buttonNode = FrameNode::GetOrCreateFrameNode(V2::BUTTON_ETS_TAG, + ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr(); }); + buttonNode->MountToParent(columnNode); + columnNode->MountToParent(tipsNode); + + /** + * @tc.steps: step3. test UpdateTextNodeMaxLines return. + */ + layoutAlgorithm->UpdateTextNodeMaxLines(columnNode, LayoutConstraintF()); +} + +/** + * @tc.name: CalculateTipsDirectionsTest001 + * @tc.desc: Test CalculateTipsDirections function. + * @tc.type: FUNC + */ +HWTEST_F(BubbleTipsTestNg, CalculateTipsDirectionsTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create bubble and get frameNode. + */ + auto tipsNode = CreateTipsNode(CreateTipsParamForCursor(), TIPS_MSG_1); + auto layoutAlgorithm = + AceType::DynamicCast(tipsNode->layoutAlgorithm_->GetLayoutAlgorithm()); + layoutAlgorithm->wrapperRect_ = { 0, 0, DEVICE_WIDTH, DEVICE_HEIGHT }; + layoutAlgorithm->wrapperSize_ = { DEVICE_WIDTH, DEVICE_HEIGHT }; + layoutAlgorithm->targetSize_ = SizeF(MOUSE_WIDTH.ConvertToPx(), MOUSE_HEIGHT.ConvertToPx()); + + /** + * @tc.steps: step2. test CalculateTipsDirections. + */ + SizeF newSize; + layoutAlgorithm->targetOffset_ = { TIPS_MARGIN_SPACE.ConvertToPx(), DEVICE_HEIGHT - BOTTOM_INSET }; + auto placement = layoutAlgorithm->CalculateTipsDirections(newSize); + EXPECT_EQ(placement, Placement::TOP_LEFT); + + layoutAlgorithm->targetOffset_ = { TOP_INSET, TIPS_MARGIN_SPACE.ConvertToPx() }; + placement = layoutAlgorithm->CalculateTipsDirections(newSize); + EXPECT_EQ(placement, Placement::BOTTOM_LEFT); + + layoutAlgorithm->targetOffset_ = { DEVICE_WIDTH - TIPS_MARGIN_SPACE.ConvertToPx() - MOUSE_WIDTH.ConvertToPx(), + DEVICE_HEIGHT - BOTTOM_INSET * DOUBLE }; + layoutAlgorithm->isHalfFoldHover_ = true; + placement = layoutAlgorithm->CalculateTipsDirections(newSize); + EXPECT_EQ(placement, Placement::LEFT_TOP); +} + +/** + * @tc.name: GetPositionWithPlacementLeftTopTest001 + * @tc.desc: Test GetPositionWithPlacementLeftTop function. + * @tc.type: FUNC + */ +HWTEST_F(BubbleTipsTestNg, GetPositionWithPlacementLeftTopTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create bubble and get frameNode. + */ + auto tipsNode = CreateTipsNode(CreateTipsParamForCursor(), TIPS_MSG_1); + auto layoutAlgorithm = + AceType::DynamicCast(tipsNode->layoutAlgorithm_->GetLayoutAlgorithm()); + layoutAlgorithm->wrapperRect_ = { 0, 0, DEVICE_WIDTH, DEVICE_HEIGHT }; + layoutAlgorithm->wrapperSize_ = { DEVICE_WIDTH, DEVICE_HEIGHT }; + layoutAlgorithm->targetSize_ = SizeF(MOUSE_WIDTH.ConvertToPx(), MOUSE_HEIGHT.ConvertToPx()); + layoutAlgorithm->targetOffset_ = { DEVICE_WIDTH, DEVICE_HEIGHT * HALF }; + layoutAlgorithm->targetSpace_ = Dimension(); + layoutAlgorithm->resetTipsSize_ = true; + /** + * @tc.steps: step2. test GetPositionWithPlacementLeftTop. + */ + OffsetF topPosition; + OffsetF bottomPosition; + OffsetF arrowPosition; + auto position = + layoutAlgorithm->GetPositionWithPlacementLeftTop(MAX_SIZE, topPosition, bottomPosition, arrowPosition); + EXPECT_EQ(position.GetX(), DEVICE_WIDTH - MAX_SIZE.Width()); + EXPECT_EQ(position.GetY(), DEVICE_HEIGHT - layoutAlgorithm->marginBottom_ - MAX_SIZE.Height()); + + layoutAlgorithm->isHalfFoldHover_ = true; + position = layoutAlgorithm->GetPositionWithPlacementLeftTop(MAX_SIZE, topPosition, bottomPosition, arrowPosition); + EXPECT_EQ(position.GetX(), DEVICE_WIDTH - MAX_SIZE.Width()); + EXPECT_EQ(position.GetY(), layoutAlgorithm->wrapperRect_.Bottom() - MAX_SIZE.Height()); +} + +/** + * @tc.name: GetPositionWithPlacementRightTopTest001 + * @tc.desc: Test GetPositionWithPlacementRightTop function. + * @tc.type: FUNC + */ +HWTEST_F(BubbleTipsTestNg, GetPositionWithPlacementRightTopTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create bubble and get frameNode. + */ + auto tipsNode = CreateTipsNode(CreateTipsParamForCursor(), TIPS_MSG_1); + auto layoutAlgorithm = + AceType::DynamicCast(tipsNode->layoutAlgorithm_->GetLayoutAlgorithm()); + layoutAlgorithm->wrapperRect_ = { 0, 0, DEVICE_WIDTH, DEVICE_HEIGHT }; + layoutAlgorithm->wrapperSize_ = { DEVICE_WIDTH, DEVICE_HEIGHT }; + layoutAlgorithm->targetSize_ = SizeF(MOUSE_WIDTH.ConvertToPx(), MOUSE_HEIGHT.ConvertToPx()); + layoutAlgorithm->targetOffset_ = { TIPS_MARGIN_SPACE.ConvertToPx(), DEVICE_HEIGHT * HALF }; + layoutAlgorithm->targetSpace_ = Dimension(); + layoutAlgorithm->resetTipsSize_ = true; + /** + * @tc.steps: step2. test GetPositionWithPlacementRightTop. + */ + OffsetF topPosition; + OffsetF bottomPosition; + OffsetF arrowPosition; + auto position = + layoutAlgorithm->GetPositionWithPlacementRightTop(MAX_SIZE, topPosition, bottomPosition, arrowPosition); + EXPECT_EQ(position.GetX(), TIPS_MARGIN_SPACE.ConvertToPx() + MOUSE_WIDTH.ConvertToPx()); + EXPECT_EQ(position.GetY(), DEVICE_HEIGHT - layoutAlgorithm->marginBottom_ - MAX_SIZE.Height()); + + layoutAlgorithm->isHalfFoldHover_ = true; + position = layoutAlgorithm->GetPositionWithPlacementRightTop(MAX_SIZE, topPosition, bottomPosition, arrowPosition); + EXPECT_EQ(position.GetX(), TIPS_MARGIN_SPACE.ConvertToPx() + MOUSE_WIDTH.ConvertToPx()); + EXPECT_EQ(position.GetY(), layoutAlgorithm->wrapperRect_.Bottom() - MAX_SIZE.Height()); +} + +/** + * @tc.name: IsPaintDoubleBorderTest001 + * @tc.desc: Test IsPaintDoubleBorder function. + * @tc.type: FUNC + */ +HWTEST_F(BubbleTipsTestNg, IsPaintDoubleBorderTest001, TestSize.Level1) +{ + /** + * @tc.steps: step1. create bubble and get frameNode. + */ + const int32_t version = 20; + auto param = CreateTipsParamForCursor(); + auto tipsNode = CreateTipsNode(param, TIPS_MSG_1); + BubbleView::UpdateCommonParam(tipsNode->GetId(), param); + auto pipeline = tipsNode->GetContext(); + pipeline->minPlatformVersion_ = version; + auto pattern = tipsNode->GetPattern(); + auto paintMethod = AceType::DynamicCast(pattern->CreateNodePaintMethod()); + tipsNode->geometryNode_ = AceType::MakeRefPtr(); + auto paintWrapper = AceType::MakeRefPtr( + tipsNode->GetRenderContext(), tipsNode->geometryNode_, tipsNode->paintProperty_); + /** + * @tc.steps: step2. test IsPaintDoubleBorder. + */ + EXPECT_EQ(paintMethod->IsPaintDoubleBorder(AceType::RawPtr(paintWrapper)), false); + popupTheme->tipsDoubleBorderEnable_ = true; + EXPECT_EQ(paintMethod->IsPaintDoubleBorder(AceType::RawPtr(paintWrapper)), true); +} + +/** + * @tc.name: FitMouseOffset001 + * @tc.desc: Test FitMouseOffset. + * @tc.type: FUNC + */ +HWTEST_F(BubbleTipsTestNg, FitMouseOffset001, TestSize.Level1) +{ + auto targetNode = CreateTargetNode(); + auto id = targetNode->GetId(); + auto targetTag = targetNode->GetTag(); + auto popupId = ElementRegister::GetInstance()->MakeUniqueId(); + auto frameNode = + FrameNode::CreateFrameNode(V2::POPUP_ETS_TAG, popupId, AceType::MakeRefPtr(id, targetTag)); + auto bubblePattern = frameNode->GetPattern(); + ASSERT_NE(bubblePattern, nullptr); + auto layoutAlgorithm = AceType::DynamicCast(bubblePattern->CreateLayoutAlgorithm()); + ASSERT_NE(layoutAlgorithm, nullptr); + RefPtr geometryNode = AceType::MakeRefPtr(); + ASSERT_NE(geometryNode, nullptr); + RefPtr layoutWrapper = + AceType::MakeRefPtr(frameNode, geometryNode, frameNode->GetLayoutProperty()); + ASSERT_NE(layoutWrapper, nullptr); + MockPipelineContext::GetCurrent()->displayWindowRectInfo_ = Rect(0.0, 0.0, WIDTH, HEIGHT); + layoutAlgorithm->followCursor_ = true; + layoutAlgorithm->expandDisplay_ = false; + const double offset = 10.0; + const double size = 20.0; + const int32_t parentId = 1; + const Rect subWindow(offset, offset, size, size); + const OffsetF targetPosition(75.0, 75.0); + RefPtr pipelineContext = targetNode->GetContextRefPtr(); + auto containerId = pipelineContext->GetInstanceId(); + AceType::DynamicCast(AceEngine::Get().GetContainer(containerId))->isSubContainer_ = true; + MockContainer::Current()->pipelineContext_ = MockPipelineContext::GetCurrentContext(); + SubwindowManager::GetInstance()->parentContainerMap_[MockContainer::CurrentId()] = parentId; + AceEngine::Get().AddContainer(parentId, AceType::MakeRefPtr()); + auto parentContainer = AceType::DynamicCast(AceEngine::Get().GetContainer(parentId)); + parentContainer->pipelineContext_ = MockPipelineContext::GetCurrentContext(); + EXPECT_CALL(*parentContainer, GetGlobalScaledRect()).WillOnce(Return(subWindow)); + layoutAlgorithm->targetOffset_ = OffsetF((offset + size) * HALF, (offset + size) * HALF); + layoutAlgorithm->FitMouseOffset(AceType::RawPtr(layoutWrapper)); + EXPECT_EQ(layoutAlgorithm->targetOffset_, targetPosition); + AceType::DynamicCast(AceEngine::Get().GetContainer(containerId))->isSubContainer_ = false; +} + +/** + * @tc.name: FitMouseOffset002 + * @tc.desc: Test FitMouseOffset. + * @tc.type: FUNC + */ +HWTEST_F(BubbleTipsTestNg, FitMouseOffset002, TestSize.Level1) +{ + auto targetNode = CreateTargetNode(); + auto id = targetNode->GetId(); + auto targetTag = targetNode->GetTag(); + auto popupId = ElementRegister::GetInstance()->MakeUniqueId(); + auto frameNode = + FrameNode::CreateFrameNode(V2::POPUP_ETS_TAG, popupId, AceType::MakeRefPtr(id, targetTag)); + auto bubblePattern = frameNode->GetPattern(); + ASSERT_NE(bubblePattern, nullptr); + auto layoutAlgorithm = AceType::DynamicCast(bubblePattern->CreateLayoutAlgorithm()); + ASSERT_NE(layoutAlgorithm, nullptr); + RefPtr geometryNode = AceType::MakeRefPtr(); + ASSERT_NE(geometryNode, nullptr); + RefPtr layoutWrapper = + AceType::MakeRefPtr(frameNode, geometryNode, frameNode->GetLayoutProperty()); + ASSERT_NE(layoutWrapper, nullptr); + MockPipelineContext::GetCurrent()->displayWindowRectInfo_ = Rect(0.0, 0.0, WIDTH, HEIGHT); + layoutAlgorithm->followCursor_ = true; + layoutAlgorithm->expandDisplay_ = false; + const double offset = 10.0; + const double size = 20.0; + const Rect subWindow(offset, offset, size, size); + const OffsetF targetPosition((offset + size) * HALF, (offset + size) * HALF); + MockContainer::UpdateCurrent(1); + layoutAlgorithm->targetOffset_ = targetPosition; + layoutAlgorithm->FitMouseOffset(AceType::RawPtr(layoutWrapper)); + EXPECT_EQ(layoutAlgorithm->targetOffset_, targetPosition); +} + +/** + * @tc.name: FitMouseOffset003 + * @tc.desc: Test FitMouseOffset. + * @tc.type: FUNC + */ +HWTEST_F(BubbleTipsTestNg, FitMouseOffset003, TestSize.Level1) +{ + auto targetNode = CreateTargetNode(); + auto id = targetNode->GetId(); + auto targetTag = targetNode->GetTag(); + auto popupId = ElementRegister::GetInstance()->MakeUniqueId(); + auto frameNode = + FrameNode::CreateFrameNode(V2::POPUP_ETS_TAG, popupId, AceType::MakeRefPtr(id, targetTag)); + auto bubblePattern = frameNode->GetPattern(); + ASSERT_NE(bubblePattern, nullptr); + auto layoutAlgorithm = AceType::DynamicCast(bubblePattern->CreateLayoutAlgorithm()); + ASSERT_NE(layoutAlgorithm, nullptr); + RefPtr geometryNode = AceType::MakeRefPtr(); + ASSERT_NE(geometryNode, nullptr); + RefPtr layoutWrapper = + AceType::MakeRefPtr(frameNode, geometryNode, frameNode->GetLayoutProperty()); + ASSERT_NE(layoutWrapper, nullptr); + MockPipelineContext::GetCurrent()->displayWindowRectInfo_ = Rect(0.0, 0.0, WIDTH, HEIGHT); + layoutAlgorithm->followCursor_ = true; + layoutAlgorithm->expandDisplay_ = false; + const Rect subWindow(0.0, 0.0, 0.0, 0.0); + const OffsetF targetPosition(0.0, 0.0); + const int32_t parentId = 1; + RefPtr pipelineContext = targetNode->GetContextRefPtr(); + auto containerId = pipelineContext->GetInstanceId(); + AceType::DynamicCast(AceEngine::Get().GetContainer(containerId))->isSubContainer_ = true; + auto parentContainer = AceType::DynamicCast(AceEngine::Get().GetContainer(parentId)); + parentContainer->pipelineContext_ = MockPipelineContext::GetCurrentContext(); + EXPECT_CALL(*parentContainer, GetGlobalScaledRect()).WillOnce(Return(subWindow)); + layoutAlgorithm->targetOffset_ = targetPosition; + layoutAlgorithm->FitMouseOffset(AceType::RawPtr(layoutWrapper)); + EXPECT_EQ(layoutAlgorithm->targetOffset_, targetPosition); + AceType::DynamicCast(AceEngine::Get().GetContainer(containerId))->isSubContainer_ = false; +} } // namespace OHOS::Ace::NG -- Gitee