diff --git a/frameworks/core/components_ng/pattern/menu/menu_item/menu_item_pattern.cpp b/frameworks/core/components_ng/pattern/menu/menu_item/menu_item_pattern.cpp index 28aabfa57b5ecc02c82f38ca91b411c406bf3732..a9d08520e35f01cfb4e89894da7a89b7172ef718 100644 --- a/frameworks/core/components_ng/pattern/menu/menu_item/menu_item_pattern.cpp +++ b/frameworks/core/components_ng/pattern/menu/menu_item/menu_item_pattern.cpp @@ -46,6 +46,13 @@ namespace OHOS::Ace::NG { namespace { const Color ITEM_FILL_COLOR = Color::TRANSPARENT; +constexpr double VELOCITY = 0.0f; +constexpr double MASS = 1.0f; +constexpr double STIFFNESS = 228.0f; +constexpr double DAMPING = 22.0f; +constexpr double SEMI_CIRCLE_ANGEL = 180.0f; +constexpr float OPACITY_EFFECT = 0.99; + void UpdateFontSize(RefPtr& textProperty, RefPtr& menuProperty, const std::optional& fontSize, const Dimension& defaultFontSize) { @@ -435,13 +442,25 @@ void MenuItemPattern::ShowSubMenuHelper(const RefPtr& subMenu) AddSelfHoverRegion(host); auto menuWrapper = GetMenuWrapper(); CHECK_NULL_VOID(menuWrapper); - subMenu->MountToParent(menuWrapper); - OffsetF offset = GetSubMenuPosition(host); - auto menuProps = subMenu->GetLayoutProperty(); - CHECK_NULL_VOID(menuProps); - menuProps->UpdateMenuOffset(offset); - menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD); - RegisterWrapperMouseEvent(); + auto parentMenu = GetMenu(); + CHECK_NULL_VOID(parentMenu); + auto layoutProps = parentMenu->GetLayoutProperty(); + CHECK_NULL_VOID(layoutProps); + auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE); + if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) && + expandingMode == SubMenuExpandingMode::STACK) { + subMenu->MountToParent(menuWrapper); + menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD); + menuPattern->SetSubMenuShow(); + } else { + subMenu->MountToParent(menuWrapper); + OffsetF offset = GetSubMenuPosition(host); + auto menuProps = subMenu->GetLayoutProperty(); + CHECK_NULL_VOID(menuProps); + menuProps->UpdateMenuOffset(offset); + menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD); + RegisterWrapperMouseEvent(); + } // select overlay menu no need focus if (!isSelectOverlayMenu) { menuPattern->FocusViewShow(); @@ -483,12 +502,95 @@ void MenuItemPattern::HideSubMenu() } } +void MenuItemPattern::ShowEmbeddedExpandMenu(const RefPtr& expandableNode) +{ + CHECK_NULL_VOID(expandableNode); + auto host = GetHost(); + CHECK_NULL_VOID(host); + auto rightRow = AceType::DynamicCast(host->GetChildAtIndex(1)); + CHECK_NULL_VOID(rightRow); + auto imageNode = AceType::DynamicCast(rightRow->GetChildAtIndex(0)); + CHECK_NULL_VOID(imageNode); + auto imageContext = imageNode->GetRenderContext(); + CHECK_NULL_VOID(imageContext); + imageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, 0.0f, 0.0f)); + + auto expandableAreaContext = expandableNode->GetRenderContext(); + CHECK_NULL_VOID(expandableAreaContext); + auto expandableAreaSize = expandableNode->GetGeometryNode()->GetFrameSize(); + expandableAreaContext->ClipWithRRect(RectF(0.0f, 0.0f, expandableAreaSize.Width(), 0.0f), + RadiusF(EdgeF(0.0f, 0.0f))); + + AnimationOption option = AnimationOption(); + auto rotateOption = AceType::MakeRefPtr(VELOCITY, MASS, STIFFNESS, DAMPING); + option.SetCurve(rotateOption); + AnimationUtils::Animate(option, [host, rightRow, expandableNode, expandableAreaContext, imageContext]() { + host->AddChildAfter(expandableNode, rightRow); + imageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, SEMI_CIRCLE_ANGEL, 0.0f)); + expandableNode->MarkModifyDone(); + expandableNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD); + + auto pipeline = PipelineContext::GetCurrentContext(); + CHECK_NULL_VOID(pipeline); + pipeline->FlushUITasks(); + auto expandableAreaFrameSize = expandableNode->GetGeometryNode()->GetFrameSize(); + expandableAreaContext->ClipWithRRect(RectF(0.0f, 0.0f, expandableAreaFrameSize.Width(), + expandableAreaFrameSize.Height()), RadiusF(EdgeF(0.0f, 0.0f))); + }); +} + +void MenuItemPattern::HideEmbeddedExpandMenu(const RefPtr& expandableNode) +{ + CHECK_NULL_VOID(expandableNode); + auto host = GetHost(); + CHECK_NULL_VOID(host); + auto expandableAreaContext = expandableNode->GetRenderContext(); + CHECK_NULL_VOID(expandableAreaContext); + + RefPtr opacity = AceType::MakeRefPtr(OPACITY_EFFECT); + expandableAreaContext->UpdateChainedTransition(opacity); + + AnimationOption option = AnimationOption(); + auto rotateOption = AceType::MakeRefPtr(VELOCITY, MASS, STIFFNESS, DAMPING); + option.SetCurve(rotateOption); + + AnimationUtils::Animate(option, [this, host, expandableNode, expandableAreaContext]() { + for (auto item : expandableItems_) { + expandableNode->RemoveChild(item, true); + } + expandableNode->MarkModifyDone(); + expandableNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD); + + auto rightRow = AceType::DynamicCast(host->GetChildAtIndex(1)); + CHECK_NULL_VOID(rightRow); + auto imageNode = AceType::DynamicCast(rightRow->GetChildAtIndex(0)); + CHECK_NULL_VOID(imageNode); + auto imageContext = imageNode->GetRenderContext(); + CHECK_NULL_VOID(imageContext); + imageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, 0.0f, 0.0f)); + auto pipeline = PipelineContext::GetCurrentContext(); + CHECK_NULL_VOID(pipeline); + pipeline->FlushUITasks(); + auto expandableAreaFrameSize = expandableNode->GetGeometryNode()->GetFrameSize(); + expandableAreaContext->ClipWithRRect(RectF(0.0f, 0.0f, expandableAreaFrameSize.Width(), + 0.0f), RadiusF(EdgeF(0.0f, 0.0f))); + }); +} + void MenuItemPattern::CloseMenu() { // no need close for selection menu if (IsSelectOverlayMenu()) { return; } + auto parentMenu = GetMenu(); + CHECK_NULL_VOID(parentMenu); + auto layoutProps = parentMenu->GetLayoutProperty(); + CHECK_NULL_VOID(layoutProps); + auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE); + if (IsSubMenu() && expandingMode == SubMenuExpandingMode::STACK) { + return; + } auto menuWrapper = GetMenuWrapper(); CHECK_NULL_VOID(menuWrapper); auto menuWrapperPattern = menuWrapper->GetPattern(); @@ -869,9 +971,7 @@ void MenuItemPattern::AddExpandIcon(RefPtr& column, RefPtrGetIconPath( expandingMode == SubMenuExpandingMode::STACK ? InternalResource::ResourceId::IC_PUBLIC_ARROW_RIGHT_SVG - : isExpanded_ - ? InternalResource::ResourceId::IC_PUBLIC_ARROW_UP_SVG - : InternalResource::ResourceId::IC_PUBLIC_ARROW_DOWN_SVG); + : InternalResource::ResourceId::IC_PUBLIC_ARROW_DOWN_SVG); auto selectTheme = pipeline->GetTheme(); CHECK_NULL_VOID(selectTheme); ImageSourceInfo imageSourceInfo(iconPath); @@ -891,18 +991,14 @@ void MenuItemPattern::UpdateExpandableArea() { auto host = GetHost(); CHECK_NULL_VOID(host); - RefPtr expandableArea = + RefPtr expandableNode = host->GetChildAtIndex(EXPANDABLE_AREA_VIEW_INDEX) ? AceType::DynamicCast(host->GetChildAtIndex(EXPANDABLE_AREA_VIEW_INDEX)) : nullptr; - CHECK_NULL_VOID(expandableArea); + CHECK_NULL_VOID(expandableNode); if (!isExpanded_) { - for (auto item : expandableItems_) { - expandableArea->RemoveChild(item); - } - expandableArea->MarkModifyDone(); - expandableArea->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); + HideEmbeddedExpandMenu(expandableNode); return; } @@ -917,12 +1013,11 @@ void MenuItemPattern::UpdateExpandableArea() BuildEmbeddedMenuItems(menu); } for (auto item : expandableItems_) { - item->MountToParent(expandableArea); + item->MountToParent(expandableNode); } } + ShowEmbeddedExpandMenu(expandableNode); } - expandableArea->MarkModifyDone(); - expandableArea->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); } void MenuItemPattern::BuildEmbeddedMenuItems(RefPtr& node, bool needNextLevel) @@ -975,7 +1070,7 @@ RefPtr MenuItemPattern::BuildStackSubMenu(RefPtr& node) CHECK_NULL_RETURN(iconTheme, node); auto selectTheme = pipeline->GetTheme(); CHECK_NULL_RETURN(selectTheme, node); - auto iconPath = iconTheme->GetIconPath(InternalResource::ResourceId::IC_PUBLIC_ARROW_DOWN_SVG); + auto iconPath = iconTheme->GetIconPath(InternalResource::ResourceId::IC_PUBLIC_ARROW_RIGHT_SVG); ImageSourceInfo imageSourceInfo; imageSourceInfo.SetSrc(iconPath); imageSourceInfo.SetFillColor(selectTheme->GetMenuIconColor()); @@ -1280,4 +1375,13 @@ void MenuItemPattern::ParseMenuRadius(MenuParam& param) } } } + +bool MenuItemPattern::IsSubMenu() +{ + auto topLevelMenuPattern = GetMenuPattern(true); + if (!topLevelMenuPattern) { + return false; + } + return topLevelMenuPattern->IsSubMenu(); +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/menu/menu_item/menu_item_pattern.h b/frameworks/core/components_ng/pattern/menu/menu_item/menu_item_pattern.h index b47e30e9a0abf1884ad20fbf8141703e0853f171..791a9bdd21c681ee070fa2da0aa72180c0773f8b 100644 --- a/frameworks/core/components_ng/pattern/menu/menu_item/menu_item_pattern.h +++ b/frameworks/core/components_ng/pattern/menu/menu_item/menu_item_pattern.h @@ -205,13 +205,15 @@ private: void ShowSubMenu(); void ShowSubMenuHelper(const RefPtr& subMenu); void HideSubMenu(); + void HideEmbeddedExpandMenu(const RefPtr& expandableNode); + void ShowEmbeddedExpandMenu(const RefPtr& expandableNode); OffsetF GetSubMenuPosition(const RefPtr& targetNode); void AddSelfHoverRegion(const RefPtr& targetNode); void SetAccessibilityAction(); bool IsSelectOverlayMenu(); - + bool IsSubMenu(); void RecordChangeEvent() const; void ParseMenuRadius(MenuParam& param); diff --git a/frameworks/core/components_ng/pattern/menu/menu_layout_algorithm.cpp b/frameworks/core/components_ng/pattern/menu/menu_layout_algorithm.cpp index 5d4e00caa1839388634c87963ada71ac91f4f425..85b9d5e3bdacfb189e79b0dae2cb47c83f5eaeeb 100644 --- a/frameworks/core/components_ng/pattern/menu/menu_layout_algorithm.cpp +++ b/frameworks/core/components_ng/pattern/menu/menu_layout_algorithm.cpp @@ -364,6 +364,10 @@ void MenuLayoutAlgorithm::Initialize(LayoutWrapper* layoutWrapper) } InitWrapperRect(props, menuPattern); placement_ = props->GetMenuPlacement().value_or(Placement::BOTTOM_LEFT); + if ((menuPattern->IsSelectOverlayExtensionMenu() || menuPattern->IsSubMenu()) && + Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) { + placement_ = props->GetMenuPlacement().value_or(Placement::BOTTOM_RIGHT); + } dumpInfo_.originPlacement = PlacementUtils::ConvertPlacementToString(placement_); ModifyPositionToWrapper(layoutWrapper, position_); if (!menuPattern->IsSelectOverlayExtensionMenu() && menuPattern->GetPreviewMode() != MenuPreviewMode::NONE) { diff --git a/frameworks/core/components_ng/pattern/menu/menu_pattern.cpp b/frameworks/core/components_ng/pattern/menu/menu_pattern.cpp index d7cacbdedd671a3b17a7aff4659b08a326c7b8bb..0957b78afdec08f76b63f4eb001280bb6e6ff27d 100644 --- a/frameworks/core/components_ng/pattern/menu/menu_pattern.cpp +++ b/frameworks/core/components_ng/pattern/menu/menu_pattern.cpp @@ -52,6 +52,25 @@ namespace { constexpr float PAN_MAX_VELOCITY = 2000.0f; constexpr Dimension MIN_SELECT_MENU_WIDTH = 64.0_vp; constexpr int32_t COLUMN_NUM = 2; +constexpr int32_t STACK_EXPAND_DISAPPEAR_DURATION = 300; +constexpr double MENU_ORIGINAL_SCALE = 0.6f; +constexpr double MOUNT_MENU_OPACITY = 0.4f; +constexpr Dimension ORIGINAL_BLUR_RADIUS = 20.0_px; +constexpr Dimension FINAL_BLUR_RADIUS = 0.1_px; + +constexpr double VELOCITY = 0.0f; +constexpr double MASS = 1.0f; +constexpr double STIFFNESS = 228.0f; +constexpr double DAMPING = 22.0f; +constexpr double STACK_MENU_DAMPING = 26.0f; +const RefPtr MENU_ANIMATION_CURVE = + AceType::MakeRefPtr(VELOCITY, MASS, STIFFNESS, DAMPING); +const RefPtr STACK_MENU_CURVE = + AceType::MakeRefPtr(VELOCITY, MASS, STIFFNESS, STACK_MENU_DAMPING); + +constexpr double MOUNT_MENU_FINAL_SCALE = 0.95f; +constexpr double SEMI_CIRCLE_ANGEL = 90.0f; + void UpdateFontStyle(RefPtr& menuProperty, RefPtr& itemProperty, RefPtr& itemPattern, bool& contentChanged, bool& labelChanged) @@ -399,7 +418,10 @@ void MenuPattern::OnTouchEvent(const TouchEventInfo& info) auto touchUpOffset = info.GetTouches().front().GetLocalLocation(); if (lastTouchOffset_.has_value() && (touchUpOffset - lastTouchOffset_.value()).GetDistance() <= DEFAULT_CLICK_DISTANCE) { - HideMenu(true); + auto touchGlobalLocation = info.GetTouches().front().GetGlobalLocation(); + auto position = OffsetF(static_cast(touchGlobalLocation.GetX()), + static_cast(touchGlobalLocation.GetY())); + HideMenu(true, position); } lastTouchOffset_.reset(); } @@ -532,7 +554,7 @@ void MenuPattern::UpdateSelectParam(const std::vector& params) host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); } -void MenuPattern::HideMenu(bool isMenuOnTouch) const +void MenuPattern::HideMenu(bool isMenuOnTouch, OffsetF position) const { auto pipeline = PipelineContext::GetCurrentContext(); CHECK_NULL_VOID(pipeline); @@ -557,12 +579,91 @@ void MenuPattern::HideMenu(bool isMenuOnTouch) const return; } + if (HideStackExpandMenu(position)) { + return; + } + auto overlayManager = pipeline->GetOverlayManager(); CHECK_NULL_VOID(overlayManager); overlayManager->HideMenu(wrapper, targetId_, isMenuOnTouch); overlayManager->EraseMenuInfo(targetId_); } +bool MenuPattern::HideStackExpandMenu(const OffsetF& position) const +{ + auto wrapper = GetMenuWrapper(); + CHECK_NULL_RETURN(wrapper, false); + auto outterMenu = wrapper->GetFirstChild(); + CHECK_NULL_RETURN(outterMenu, false); + auto scroll = outterMenu->GetFirstChild(); + CHECK_NULL_RETURN(scroll, false); + auto innerMenu = AceType::DynamicCast(scroll->GetFirstChild()); + CHECK_NULL_RETURN(innerMenu, false); + auto innerMenuPattern = AceType::DynamicCast(innerMenu->GetPattern()); + CHECK_NULL_RETURN(innerMenuPattern, false); + auto layoutProps = innerMenuPattern->GetLayoutProperty(); + CHECK_NULL_RETURN(layoutProps, false); + auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE); + if (IsSubMenu() && expandingMode == SubMenuExpandingMode::STACK) { + auto host = GetHost(); + CHECK_NULL_RETURN(host, false); + auto hostZone = host->GetPaintRectOffset(); + scroll = host->GetFirstChild(); + CHECK_NULL_RETURN(scroll, false); + auto column = scroll->GetFirstChild(); + CHECK_NULL_RETURN(column, false); + auto clickAreaNode = AceType::DynamicCast(column->GetFirstChild()); + CHECK_NULL_RETURN(clickAreaNode, false); + auto clickAreaZone = clickAreaNode->GetGeometryNode()->GetFrameRect(); + clickAreaZone.SetLeft(hostZone.GetX()); + clickAreaZone.SetTop(hostZone.GetY()); + if (clickAreaZone.IsInRegion(PointF(position.GetX(), position.GetY()))) { + HideStackMenu(); + return true; + } + } + return false; +} + +void MenuPattern::HideStackMenu() const +{ + auto host = GetHost(); + CHECK_NULL_VOID(host); + auto wrapper = GetMenuWrapper(); + CHECK_NULL_VOID(wrapper); + AnimationOption option; + option.SetOnFinishEvent( + [weak = WeakClaim(RawPtr(wrapper)), subMenuWk = WeakClaim(RawPtr(host))] { + auto pipeline = PipelineBase::GetCurrentContext(); + CHECK_NULL_VOID(pipeline); + auto taskExecutor = pipeline->GetTaskExecutor(); + CHECK_NULL_VOID(taskExecutor); + taskExecutor->PostTask( + [weak, subMenuWk]() { + auto subMenuNode = subMenuWk.Upgrade(); + CHECK_NULL_VOID(subMenuNode); + auto menuWrapper = weak.Upgrade(); + CHECK_NULL_VOID(menuWrapper); + menuWrapper->RemoveChild(subMenuNode); + menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD); + }, + TaskExecutor::TaskType::UI, "HideStackMenu"); + }); + auto menuPattern = AceType::DynamicCast(host->GetPattern()); + if (menuPattern) { + menuPattern->RemoveParentHoverStyle(); + auto frameNode = FrameNode::GetFrameNode(menuPattern->GetTargetTag(), menuPattern->GetTargetId()); + CHECK_NULL_VOID(frameNode); + auto menuItem = frameNode->GetPattern(); + if (menuItem) { + menuItem->SetIsSubMenuShowed(false); + } + } + auto menuNode = AceType::DynamicCast(wrapper->GetFirstChild()); + CHECK_NULL_VOID(menuNode); + ShowStackExpandDisappearAnimation(menuNode, host, option); +} + void MenuPattern::HideSubMenu() { if (!showedSubMenu_) { @@ -863,6 +964,7 @@ Offset MenuPattern::GetTransformCenter() const auto placement = layoutAlgorithm->GetPlacement(); switch (placement) { case Placement::BOTTOM_LEFT: + return Offset(size.Width(), 0.0f); case Placement::RIGHT_TOP: return Offset(); case Placement::BOTTOM_RIGHT: @@ -947,9 +1049,200 @@ void MenuPattern::ShowPreviewMenuAnimation() isFirstShow_ = false; } +void MenuPattern::ShowMenuAppearAnimation() +{ + auto host = GetHost(); + CHECK_NULL_VOID(host); + if (isMenuShow_ && Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) && + previewMode_ == MenuPreviewMode::NONE) { + auto renderContext = host->GetRenderContext(); + CHECK_NULL_VOID(renderContext); + auto offset = GetTransformCenter(); + renderContext->UpdateTransformCenter(DimensionOffset(offset)); + auto menuPosition = host->GetPaintRectOffset(); + if (IsSelectOverlayExtensionMenu() && !isExtensionMenuShow_) { + menuPosition = GetEndOffset(); + } + if (IsSelectOverlayExtensionMenu()) { + SetEndOffset(menuPosition); + } + + renderContext->UpdateTransformScale(VectorF(MENU_ORIGINAL_SCALE, MENU_ORIGINAL_SCALE)); + renderContext->UpdateFrontBlurRadius(ORIGINAL_BLUR_RADIUS); + renderContext->UpdateOpacity(0.0f); + + AnimationOption option = AnimationOption(); + option.SetCurve(MENU_ANIMATION_CURVE); + AnimationUtils::Animate(option, [renderContext, menuPosition]() { + if (renderContext) { + renderContext->UpdatePosition( + OffsetT(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY()))); + renderContext->UpdateOpacity(1.0f); + renderContext->UpdateTransformScale(VectorF(1.0f, 1.0f)); + renderContext->UpdateFrontBlurRadius(FINAL_BLUR_RADIUS); + } + }); + isExtensionMenuShow_ = false; + } + isMenuShow_ = false; +} + +void MenuPattern::ShowStackExpandMenu() +{ + auto host = GetHost(); + CHECK_NULL_VOID(host); + if (!isSubMenuShow_) { + return; + } + auto renderContext = host->GetRenderContext(); + CHECK_NULL_VOID(renderContext); + auto menuWarpper = GetMenuWrapper(); + CHECK_NULL_VOID(menuWarpper); + auto menuWrapperPattern = menuWarpper->GetPattern(); + CHECK_NULL_VOID(menuWrapperPattern); + auto outterMenu = menuWrapperPattern->GetMenu(); + CHECK_NULL_VOID(outterMenu); + auto outterMenuContext = outterMenu->GetRenderContext(); + CHECK_NULL_VOID(outterMenuContext); + + auto frameSize = outterMenu->GetGeometryNode()->GetFrameSize(); + auto originOffset = outterMenu->GetPaintRectOffset(); + auto menuPosition = outterMenu->GetPaintRectOffset() + OffsetF(0.0f, frameSize.Height() / 5); + + renderContext->UpdatePosition( + OffsetT(Dimension(originOffset.GetX()), Dimension(originOffset.GetY()))); + + AnimationOption opacityOption = AnimationOption(); + opacityOption.SetCurve(Curves::FRICTION); + opacityOption.SetDuration(STACK_EXPAND_DISAPPEAR_DURATION); + AnimationUtils::Animate(opacityOption, [renderContext, outterMenuContext]() { + if (renderContext) { + renderContext->UpdateOpacity(1.0f); + } + if (outterMenuContext) { + outterMenuContext->UpdateOpacity(MOUNT_MENU_OPACITY); + } + }); + + AnimationOption translateOption = AnimationOption(); + translateOption.SetCurve(STACK_MENU_CURVE); + AnimationUtils::Animate(translateOption, [renderContext, menuPosition, outterMenuContext]() { + if (renderContext) { + renderContext->UpdatePosition( + OffsetT(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY()))); + } + if (outterMenuContext) { + outterMenuContext->UpdateTransformScale(VectorF(MOUNT_MENU_FINAL_SCALE, MOUNT_MENU_FINAL_SCALE)); + } + }); + ShowArrowRotateAnimation(); + isSubMenuShow_ = false; +} + +void MenuPattern::ShowArrowRotateAnimation() const +{ + auto host = GetHost(); + CHECK_NULL_VOID(host); + auto subImageNode = GetImageNode(host); + CHECK_NULL_VOID(subImageNode); + auto subImageContext = subImageNode->GetRenderContext(); + CHECK_NULL_VOID(subImageContext); + subImageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, 0.0f, 0.0f)); + AnimationOption option = AnimationOption(); + option.SetCurve(MENU_ANIMATION_CURVE); + AnimationUtils::Animate(option, [subImageContext]() { + if (subImageContext) { + subImageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, SEMI_CIRCLE_ANGEL, 0.0f)); + } + }); +} + +RefPtr MenuPattern::GetImageNode(const RefPtr& host) const +{ + auto scroll = host->GetFirstChild(); + CHECK_NULL_RETURN(scroll, nullptr); + auto innerMenu = scroll->GetFirstChild(); + CHECK_NULL_RETURN(innerMenu, nullptr); + auto menuItem = innerMenu->GetFirstChild(); + CHECK_NULL_RETURN(menuItem, nullptr); + auto rightRow = menuItem->GetChildAtIndex(1); + CHECK_NULL_RETURN(rightRow, nullptr); + auto image = AceType::DynamicCast(rightRow->GetChildren().back()); + return image; +} + +void MenuPattern::ShowStackExpandDisappearAnimation(const RefPtr& menuNode, + const RefPtr& subMenuNode, AnimationOption& option) const +{ + CHECK_NULL_VOID(menuNode); + CHECK_NULL_VOID(subMenuNode); + auto subImageNode = GetImageNode(subMenuNode); + CHECK_NULL_VOID(subImageNode); + + auto menuNodePos = menuNode->GetPaintRectOffset(); + auto subMenuPos = subMenuNode->GetPaintRectOffset(); + auto menuPosition = OffsetF(subMenuPos.GetX(), menuNodePos.GetY()); + + option.SetCurve(STACK_MENU_CURVE); + AnimationUtils::Animate(option, [menuNode, menuPosition, subMenuNode]() { + auto menuContext = menuNode->GetRenderContext(); + auto subMenuContext = subMenuNode->GetRenderContext(); + if (subMenuContext) { + subMenuContext->UpdatePosition( + OffsetT(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY()))); + } + if (menuContext) { + menuContext->UpdateTransformScale(VectorF(1.0f, 1.0f)); + } + }); + + option.SetCurve(MENU_ANIMATION_CURVE); + auto subImageContext = subImageNode->GetRenderContext(); + AnimationUtils::Animate(option, [subImageContext]() { + if (subImageContext) { + subImageContext->UpdateTransformRotate(Vector5F(0.0f, 0.0f, 1.0f, 0.0f, 0.0f)); + } + }); + + option.SetCurve(Curves::FRICTION); + option.SetDuration(STACK_EXPAND_DISAPPEAR_DURATION); + AnimationUtils::Animate(option, [menuNode, subMenuNode]() { + auto menuContext = menuNode->GetRenderContext(); + auto subMenuContext = subMenuNode->GetRenderContext(); + if (subMenuContext) { + subMenuContext->UpdateOpacity(0.0f); + } + if (menuContext) { + menuContext->UpdateOpacity(1.0f); + } + }, option.GetOnFinishEvent()); +} + +void MenuPattern::ShowMenuDisappearAnimation() +{ + auto host = GetHost(); + CHECK_NULL_VOID(host); + auto menuContext = host->GetRenderContext(); + + auto menuPosition = GetEndOffset(); + AnimationOption option = AnimationOption(); + option.SetCurve(MENU_ANIMATION_CURVE); + AnimationUtils::Animate(option, [menuContext, menuPosition]() { + if (menuContext) { + menuContext->UpdatePosition( + OffsetT(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY()))); + menuContext->UpdateTransformScale(VectorF(MENU_ORIGINAL_SCALE, MENU_ORIGINAL_SCALE)); + menuContext->UpdateFrontBlurRadius(ORIGINAL_BLUR_RADIUS); + menuContext->UpdateOpacity(0.0f); + } + }); +} + bool MenuPattern::OnDirtyLayoutWrapperSwap(const RefPtr& dirty, const DirtySwapConfig& config) { ShowPreviewMenuAnimation(); + ShowMenuAppearAnimation(); + ShowStackExpandMenu(); if (config.skipMeasure || dirty->SkipMeasureContent()) { return false; } diff --git a/frameworks/core/components_ng/pattern/menu/menu_pattern.h b/frameworks/core/components_ng/pattern/menu/menu_pattern.h index 23ee7d89480264ca70bb5f1c309b4b7054ae1d16..ce881b64a3b1320cf1c0ebd3c9c5fb4084a23aca 100644 --- a/frameworks/core/components_ng/pattern/menu/menu_pattern.h +++ b/frameworks/core/components_ng/pattern/menu/menu_pattern.h @@ -268,7 +268,11 @@ public: void UpdateSelectParam(const std::vector& params); - void HideMenu(bool isMenuOnTouch = false) const; + void HideMenu(bool isMenuOnTouch = false, OffsetF position = OffsetF()) const; + + bool HideStackExpandMenu(const OffsetF& position) const; + + void HideStackMenu() const; void MountOption(const RefPtr& option); @@ -284,7 +288,7 @@ public: { return showedSubMenu_; } - + void SetIsWidthModifiedBySelect(bool isModified) { isWidthModifiedBySelect_ = isModified; @@ -328,6 +332,21 @@ public: return endOffset_; } + void SetSelectOverlayExtensionMenuShow() + { + isExtensionMenuShow_ = true; + } + + void SetSubMenuShow() + { + isSubMenuShow_ = true; + } + + void SetMenuShow() + { + isMenuShow_ = true; + } + void SetPreviewOriginOffset(const OffsetF& offset) { previewOriginOffset_ = offset; @@ -357,7 +376,7 @@ public: { return targetSize_; } - + void SetIsHeightModifiedBySelect(bool isModified) { isHeightModifiedBySelect_ = isModified; @@ -373,6 +392,10 @@ public: return expandDisplay_; } + void ShowMenuDisappearAnimation(); + void ShowStackExpandDisappearAnimation(const RefPtr& menuNode, + const RefPtr& subMenuNode, AnimationOption& option) const; + void SetBuilderFunc(SelectMakeCallback&& makeFunc) { makeFunc_ = std::move(makeFunc); @@ -442,6 +465,10 @@ private: Offset GetTransformCenter() const; void ShowPreviewMenuAnimation(); + void ShowMenuAppearAnimation(); + void ShowStackExpandMenu(); + void ShowArrowRotateAnimation() const; + RefPtr GetImageNode(const RefPtr& host) const; void InitPanEvent(const RefPtr& gestureHub); void HandleDragEnd(float offsetX, float offsetY, float velocity); @@ -466,9 +493,14 @@ private: MenuPreviewMode previewMode_ = MenuPreviewMode::NONE; MenuPreviewAnimationOptions previewAnimationOptions_; bool isFirstShow_ = false; + bool isExtensionMenuShow_ = false; + bool isSubMenuShow_ = false; + bool isMenuShow_ = false; + OffsetF originOffset_; OffsetF endOffset_; OffsetF previewOriginOffset_; + WeakPtr builderNode_; bool isWidthModifiedBySelect_ = false; bool isHeightModifiedBySelect_ = false; diff --git a/frameworks/core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.cpp b/frameworks/core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.cpp index 5530221579a37eae6f4fec6a2ad89da896101837..4564904468dd074eb221f5266094d9c2d0268f7c 100644 --- a/frameworks/core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.cpp +++ b/frameworks/core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.cpp @@ -107,7 +107,7 @@ void MenuWrapperPattern::HideSubMenu() menuPattern->FocusViewShow(); } } - host->RemoveChild(subMenu); + auto menuPattern = DynamicCast(subMenu)->GetPattern(); if (menuPattern) { menuPattern->RemoveParentHoverStyle(); @@ -118,7 +118,52 @@ void MenuWrapperPattern::HideSubMenu() menuItem->SetIsSubMenuShowed(false); } } - host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD); + auto scroll = focusMenu->GetFirstChild(); + CHECK_NULL_VOID(scroll); + auto innerMenu = AceType::DynamicCast(scroll->GetFirstChild()); + CHECK_NULL_VOID(innerMenu); + auto innerMenuPattern = innerMenu->GetPattern(); + CHECK_NULL_VOID(innerMenuPattern); + auto layoutProps = innerMenuPattern->GetLayoutProperty(); + CHECK_NULL_VOID(layoutProps); + auto expandingMode = layoutProps->GetExpandingMode().value_or(SubMenuExpandingMode::SIDE); + if (!(Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) && menuPattern->IsSubMenu()) || + menuPattern->IsSelectOverlaySubMenu()) { + host->RemoveChild(subMenu); + host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD); + } else if (expandingMode == SubMenuExpandingMode::STACK && menuPattern->IsSubMenu()) { + HideStackExpandMenu(subMenu); + } +} + +void MenuWrapperPattern::HideStackExpandMenu(const RefPtr& subMenu) +{ + auto host = GetHost(); + CHECK_NULL_VOID(host); + auto menuNode = host->GetFirstChild(); + CHECK_NULL_VOID(menuNode); + AnimationOption option; + option.SetOnFinishEvent( + [weak = WeakClaim(RawPtr(host)), subMenuWk = WeakClaim(RawPtr(subMenu))] { + auto pipeline = PipelineBase::GetCurrentContext(); + CHECK_NULL_VOID(pipeline); + auto taskExecutor = pipeline->GetTaskExecutor(); + CHECK_NULL_VOID(taskExecutor); + taskExecutor->PostTask( + [weak, subMenuWk]() { + auto subMenuNode = subMenuWk.Upgrade(); + CHECK_NULL_VOID(subMenuNode); + auto menuWrapper = weak.Upgrade(); + CHECK_NULL_VOID(menuWrapper); + menuWrapper->RemoveChild(subMenuNode); + menuWrapper->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD); + }, + TaskExecutor::TaskType::UI, "HideStackExpandMenu"); + }); + auto menuNodePattern = DynamicCast(menuNode)->GetPattern(); + CHECK_NULL_VOID(menuNodePattern); + menuNodePattern->ShowStackExpandDisappearAnimation(DynamicCast(menuNode), + DynamicCast(subMenu), option); } void MenuWrapperPattern::RegisterOnTouch() diff --git a/frameworks/core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h b/frameworks/core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h index 22762edff2084f3cd9adc17fbdcd5a84c22479bb..50a5897c77d1b1b7aa4747355cc3bfaf4782de73 100644 --- a/frameworks/core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h +++ b/frameworks/core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h @@ -109,6 +109,7 @@ public: } void HideSubMenu(); + void HideStackExpandMenu(const RefPtr& subMenu); RefPtr GetMenu() const { diff --git a/frameworks/core/components_ng/pattern/overlay/overlay_manager.cpp b/frameworks/core/components_ng/pattern/overlay/overlay_manager.cpp index 20dc35fd195d742e5b34d40e2c2144f513ce9676..4a66d0bd1a0a0bb09934ef8e4ac0a661319a242e 100644 --- a/frameworks/core/components_ng/pattern/overlay/overlay_manager.cpp +++ b/frameworks/core/components_ng/pattern/overlay/overlay_manager.cpp @@ -111,6 +111,11 @@ const RefPtr SHOW_CUSTOM_KEYBOARD_ANIMATION_CURVE = const RefPtr HIDE_CUSTOM_KEYBOARD_ANIMATION_CURVE = AceType::MakeRefPtr(4.0f, 1.0f, 342.0f, 37.0f); +const RefPtr MENU_ANIMATION_CURVE = + AceType::MakeRefPtr(0.0f, 1.0f, 228.0f, 22.0f); +constexpr Dimension ORIGINAL_BLUR_RADIUS = 20.0_px; +constexpr double MENU_ORIGINAL_SCALE = 0.6f; + RefPtr GetLastPage() { auto pipelineContext = PipelineContext::GetCurrentContext(); @@ -275,6 +280,28 @@ void ShowContextMenuDisappearAnimation( }, option.GetOnFinishEvent()); } + +void ShowMenuDisappearAnimation(AnimationOption& option, const RefPtr& menuWrapperPattern) +{ + CHECK_NULL_VOID(menuWrapperPattern); + auto menuNode = menuWrapperPattern->GetMenu(); + CHECK_NULL_VOID(menuNode); + auto menuRenderContext = menuNode->GetRenderContext(); + CHECK_NULL_VOID(menuRenderContext); + auto menuPattern = menuNode->GetPattern(); + CHECK_NULL_VOID(menuPattern); + auto menuPosition = menuPattern->GetEndOffset(); + option.SetCurve(MENU_ANIMATION_CURVE); + AnimationUtils::Animate(option, [menuRenderContext, menuPosition]() { + if (menuRenderContext) { + menuRenderContext->UpdatePosition( + OffsetT(Dimension(menuPosition.GetX()), Dimension(menuPosition.GetY()))); + menuRenderContext->UpdateTransformScale(VectorF(MENU_ORIGINAL_SCALE, MENU_ORIGINAL_SCALE)); + menuRenderContext->UpdateFrontBlurRadius(ORIGINAL_BLUR_RADIUS); + menuRenderContext->UpdateOpacity(0.0f); + } + }, option.GetOnFinishEvent()); +} } // namespace void OverlayManager::UpdateContextMenuDisappearPosition(const NG::OffsetF& offset) @@ -618,12 +645,20 @@ void OverlayManager::ShowMenuAnimation(const RefPtr& menu) } } wrapperPattern->SetAniamtinOption(option); + SetPatternFirstShow(menu); +} + +void OverlayManager::SetPatternFirstShow(const RefPtr& menu) +{ + auto wrapperPattern = menu->GetPattern(); + CHECK_NULL_VOID(wrapperPattern); wrapperPattern->SetFirstShow(); auto menuChild = wrapperPattern->GetMenu(); CHECK_NULL_VOID(menuChild); auto menuPattern = AceType::DynamicCast(menuChild->GetPattern()); CHECK_NULL_VOID(menuPattern); menuPattern->SetFirstShow(); + menuPattern->SetMenuShow(); } void OverlayManager::OnPopMenuAnimationFinished(const WeakPtr menuWK, const WeakPtr rootWeak, @@ -793,6 +828,8 @@ void OverlayManager::ShowMenuClearAnimation(const RefPtr& menu, Anima ShowPreviewDisappearAnimation(menuWrapperPattern); } ShowContextMenuDisappearAnimation(option, menuWrapperPattern, startDrag); + } else if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) { + ShowMenuDisappearAnimation(option, menuWrapperPattern); } else { AnimationUtils::Animate( option, diff --git a/frameworks/core/components_ng/pattern/overlay/overlay_manager.h b/frameworks/core/components_ng/pattern/overlay/overlay_manager.h index 268a8065a7b8e2a5de6612aaa22d73002ce5b18b..5a32a9665ae82271535a9eb6b64bf3493cb48a80 100644 --- a/frameworks/core/components_ng/pattern/overlay/overlay_manager.h +++ b/frameworks/core/components_ng/pattern/overlay/overlay_manager.h @@ -521,6 +521,7 @@ private: void CloseDialogInner(const RefPtr& dialogNode); void ShowMenuAnimation(const RefPtr& menu); + void SetPatternFirstShow(const RefPtr& menu); void PopMenuAnimation(const RefPtr& menu, bool showPreviewAnimation = true, bool startDrag = false); void ClearMenuAnimation(const RefPtr& menu, bool showPreviewAnimation = true, bool startDrag = false); void ShowMenuClearAnimation(const RefPtr& menu, AnimationOption& option, diff --git a/frameworks/core/components_ng/pattern/select_overlay/select_overlay_node.cpp b/frameworks/core/components_ng/pattern/select_overlay/select_overlay_node.cpp index 645c28f340730617764b0051e2900c5910b59e85..9b306f7490c19f86dd1ceaed75eef569cb486fd8 100644 --- a/frameworks/core/components_ng/pattern/select_overlay/select_overlay_node.cpp +++ b/frameworks/core/components_ng/pattern/select_overlay/select_overlay_node.cpp @@ -657,7 +657,6 @@ void SelectOverlayNode::MoreAnimation() AnimationUtils::Animate( extensionOption, [extensionContext, selectMenuInnerContext, id = Container::CurrentId(), shadowTheme]() { ContainerScope scope(id); - extensionContext->UpdateOpacity(1.0); extensionContext->UpdateTransformTranslate({ 0.0f, 0.0f, 0.0f }); auto colorMode = SystemProperties::GetColorMode(); extensionContext->UpdateBackShadow(shadowTheme->GetShadow(ShadowStyle::OuterDefaultMD, colorMode)); @@ -666,6 +665,9 @@ void SelectOverlayNode::MoreAnimation() modifier->SetOtherPointRadius(MIN_DIAMETER / 2.0f); modifier->SetHeadPointRadius(MIN_ARROWHEAD_DIAMETER / 2.0f); modifier->SetLineEndOffset(true); + auto menuPattern = extensionMenu_->GetPattern(); + CHECK_NULL_VOID(menuPattern); + menuPattern->SetMenuShow(); FinishCallback callback = [selectMenuInnerProperty, extensionProperty, backButtonProperty, id = Container::CurrentId(), weak = WeakClaim(this)]() { @@ -724,6 +726,10 @@ void SelectOverlayNode::BackAnimation() auto meanuWidth = pattern->GetMenuWidth(); selectMenuInnerProperty->UpdateVisibility(VisibleType::VISIBLE); + + auto menuPattern = extensionMenu_->GetPattern(); + CHECK_NULL_VOID(menuPattern); + menuPattern->ShowMenuDisappearAnimation(); AnimationOption extensionOption; extensionOption.SetDuration(ANIMATION_DURATION2); extensionOption.SetCurve(Curves::FAST_OUT_SLOW_IN); @@ -731,7 +737,6 @@ void SelectOverlayNode::BackAnimation() AnimationUtils::Animate( extensionOption, [extensionContext, selectMenuInnerContext, id = Container::CurrentId()]() { ContainerScope scope(id); - extensionContext->UpdateOpacity(0.0); extensionContext->UpdateTransformTranslate({ 0.0f, MORE_MENU_TRANSLATE.ConvertToPx(), 0.0f }); selectMenuInnerContext->UpdateOpacity(1.0); }); @@ -876,6 +881,7 @@ void SelectOverlayNode::AddExtensionMenuOptions(const std::vectorUpdateTransformTranslate({ 0.0f, MORE_MENU_TRANSLATE.ConvertToPx(), 0.0f }); extensionMenu_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); extensionMenu_->MarkModifyDone(); + menuPattern->SetSelectOverlayExtensionMenuShow(); } } diff --git a/test/unittest/core/pattern/menu/menu_test_ng.cpp b/test/unittest/core/pattern/menu/menu_test_ng.cpp index c7e93bbc23a094be1e66bfec60b83ad71f609fe3..4267a958a6def582489b2de1ab9f5c71135a0f71 100644 --- a/test/unittest/core/pattern/menu/menu_test_ng.cpp +++ b/test/unittest/core/pattern/menu/menu_test_ng.cpp @@ -359,7 +359,7 @@ HWTEST_F(MenuTestNg, MenuWrapperPatternTestNg003, TestSize.Level1) ASSERT_NE(wrapperPattern, nullptr); /** * @tc.steps: step2. add submenu to wrapper - * @tc.expected: wrapper child size is 1 + * @tc.expected: wrapper child size is 2 */ auto mainMenu = FrameNode::CreateFrameNode(V2::MENU_ETS_TAG, 2, AceType::MakeRefPtr(1, TEXT_TAG, MenuType::MENU)); @@ -369,7 +369,7 @@ HWTEST_F(MenuTestNg, MenuWrapperPatternTestNg003, TestSize.Level1) wrapperPattern->HideSubMenu(); subMenu->MountToParent(wrapperNode); wrapperPattern->HideSubMenu(); - EXPECT_EQ(wrapperNode->GetChildren().size(), 1); + EXPECT_EQ(wrapperNode->GetChildren().size(), 2); } /** @@ -486,7 +486,7 @@ HWTEST_F(MenuTestNg, MenuWrapperPatternTestNg005, TestSize.Level1) subMenu->MountToParent(wrapperNode); wrapperPattern->OnTouchEvent(contextMenuTouchUpEventInfo); wrapperPattern->HideMenu(); - EXPECT_EQ(wrapperNode->GetChildren().size(), 2); + EXPECT_EQ(wrapperNode->GetChildren().size(), 3); } /**