From f7284cff948b01a3f77c361e1ff76540d517e794 Mon Sep 17 00:00:00 2001 From: zhanghaibo Date: Mon, 30 Oct 2023 22:12:15 +0800 Subject: [PATCH] fire correct event when using navigation and modal together Signed-off-by: zhanghaibo Change-Id: I741d3eb255aea4be4eef90a0164b2712a819a8f1 --- .../pattern/navigation/navigation_pattern.cpp | 71 ++++++++++++++ .../pattern/navigation/navigation_pattern.h | 3 + .../pattern/overlay/overlay_manager.cpp | 97 +++++++++++++++++-- .../pattern/overlay/overlay_manager.h | 1 + .../unittest/core/base/view_abstract/BUILD.gn | 2 + 5 files changed, 167 insertions(+), 7 deletions(-) diff --git a/frameworks/core/components_ng/pattern/navigation/navigation_pattern.cpp b/frameworks/core/components_ng/pattern/navigation/navigation_pattern.cpp index 8a5b796a9e5..d711efe5ff3 100644 --- a/frameworks/core/components_ng/pattern/navigation/navigation_pattern.cpp +++ b/frameworks/core/components_ng/pattern/navigation/navigation_pattern.cpp @@ -216,6 +216,8 @@ void NavigationPattern::CheckTopNavPathChange( NotifyPageHide(preTopNavPath->first); eventHub->FireOnHiddenEvent(); navDestinationPattern->SetIsOnShow(false); + // The navigations in NavDestination should be fired the hidden event + NavigationPattern::FireNavigationStateChange(preTopNavDestination, false); } auto focusHub = preTopNavDestination->GetOrCreateFocusHub(); focusHub->SetParentFocusable(false); @@ -262,6 +264,8 @@ void NavigationPattern::CheckTopNavPathChange( NotifyPageShow(newTopNavPath->first); eventHub->FireOnShownEvent(); navDestinationPattern->SetIsOnShow(true); + // The navigations in NavDestination should be fired the shown event + NavigationPattern::FireNavigationStateChange(newTopNavDestination, true); } auto focusHub = newTopNavDestination->GetOrCreateFocusHub(); context->AddAfterLayoutTask([focusHub]() { @@ -297,6 +301,73 @@ void NavigationPattern::CheckTopNavPathChange( hostNode->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE); } +RefPtr NavigationPattern::FireNavDestinationStateChange(bool show) +{ + // Only need to check top NavDestination every time. + auto topNavPath = navigationStack_->GetTopNavPath(); + if (!topNavPath.has_value()) { + return nullptr; + } + + auto navDestinationGroupNode = AceType::DynamicCast( + NavigationGroupNode::GetNavDestinationNode(topNavPath->second)); + CHECK_NULL_RETURN(navDestinationGroupNode, nullptr); + + auto navDestinationPattern = AceType::DynamicCast(navDestinationGroupNode->GetPattern()); + CHECK_NULL_RETURN(navDestinationPattern, nullptr); + + // Same state, no need to fire event + if (navDestinationPattern->GetIsOnShow() == show) { + return navDestinationGroupNode; + } + + auto eventHub = navDestinationGroupNode->GetEventHub(); + CHECK_NULL_RETURN(eventHub, nullptr); + + auto id = GetHost()->GetId(); + auto pipeline = AceType::DynamicCast(PipelineBase::GetCurrentContext()); + CHECK_NULL_RETURN(pipeline, nullptr); + + if (show) { + NotifyPageShow(topNavPath->first); + eventHub->FireOnShownEvent(); + navDestinationPattern->SetIsOnShow(true); + // The change from hiding to showing of top page means the navigation return to screen, + // so add window state callback again. + pipeline->AddWindowStateChangedCallback(id); + } else { + NotifyPageHide(topNavPath->first); + eventHub->FireOnHiddenEvent(); + navDestinationPattern->SetIsOnShow(false); + // The change from showing to hiding of top page means the navigation leaves from screen, + // so remove window state callback. + pipeline->RemoveWindowStateChangedCallback(id); + } + + return navDestinationGroupNode; +} + +void NavigationPattern::FireNavigationStateChange(const RefPtr& node, bool show) +{ + const auto& children = node->GetChildren(); + for (auto iter = children.rbegin(); iter != children.rend(); ++iter) { + auto& child = *iter; + + auto navigation = AceType::DynamicCast(child); + if (navigation) { + auto navigationPattern = AceType::DynamicCast(navigation->GetPattern()); + CHECK_NULL_VOID(navigationPattern); + auto changedNode = navigationPattern->FireNavDestinationStateChange(show); + if (changedNode) { + // Ignore node from navigation to navdestination in node tree, start from navdestination node directly. + NavigationPattern::FireNavigationStateChange(changedNode, show); + continue; + } + } + NavigationPattern::FireNavigationStateChange(child, show); + } +} + void NavigationPattern::NotifyPageHide(const std::string& pageName) { auto container = Container::Current(); diff --git a/frameworks/core/components_ng/pattern/navigation/navigation_pattern.h b/frameworks/core/components_ng/pattern/navigation/navigation_pattern.h index ef9f47661ea..01834024747 100644 --- a/frameworks/core/components_ng/pattern/navigation/navigation_pattern.h +++ b/frameworks/core/components_ng/pattern/navigation/navigation_pattern.h @@ -268,6 +268,8 @@ public: void OnNavigationModeChange(bool modeChange); + static void FireNavigationStateChange(const RefPtr& node, bool show); + private: void CheckTopNavPathChange(const std::optional>>& preTopNavPath, const std::optional>>& newTopNavPath, bool isPopPage); @@ -309,6 +311,7 @@ private: std::map> onStateChangeMap_; void NotifyPageHide(const std::string& pageName); void NotifyPageShow(const std::string& pageName); + RefPtr FireNavDestinationStateChange(bool show); }; } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/overlay/overlay_manager.cpp b/frameworks/core/components_ng/pattern/overlay/overlay_manager.cpp index 892d15df217..b3bb95872c4 100644 --- a/frameworks/core/components_ng/pattern/overlay/overlay_manager.cpp +++ b/frameworks/core/components_ng/pattern/overlay/overlay_manager.cpp @@ -47,7 +47,7 @@ #include "core/components_ng/pattern/menu/preview/menu_preview_pattern.h" #include "core/components_ng/pattern/menu/wrapper/menu_wrapper_pattern.h" #include "core/components_ng/pattern/navigation/navigation_group_node.h" -#include "core/components_ng/pattern/navrouter/navdestination_group_node.h" +#include "core/components_ng/pattern/navigation/navigation_pattern.h" #include "core/components_ng/pattern/overlay/keyboard_base_pattern.h" #include "core/components_ng/pattern/overlay/keyboard_view.h" #include "core/components_ng/pattern/overlay/modal_presentation_pattern.h" @@ -1342,6 +1342,8 @@ bool OverlayManager::RemoveModalInOverlay() if (isProhibitBack_ && pattern->GetTargetId() < 0) { return true; } + auto builder = AceType::DynamicCast(topModalNode->GetFirstChild()); + CHECK_NULL_RETURN(builder, false); if (!ModalExitProcess(topModalNode)) { return false; } @@ -1351,6 +1353,17 @@ bool OverlayManager::RemoveModalInOverlay() if (!modalList_.empty()) { modalList_.pop_back(); } + + if (topModalNode->GetTag() == V2::MODAL_PAGE_TAG) { + auto modalPattern = AceType::DynamicCast(pattern); + CHECK_NULL_RETURN(modalPattern, false); + auto modalTransition = modalPattern->GetType(); + if (modalTransition == ModalTransition::NONE || builder->GetRenderContext()->HasTransition()) { + // Fire shown event of navdestination under the disappeared modal + FireNavigationStateChange(true); + } + } + FireModalPageHide(); SaveLastModalNode(); return true; @@ -1366,6 +1379,8 @@ bool OverlayManager::RemoveAllModalInOverlay() modalStack_.pop(); continue; } + auto builder = AceType::DynamicCast(topModalNode->GetFirstChild()); + CHECK_NULL_RETURN(builder, false); ModalPageLostFocus(topModalNode); if (!ModalExitProcess(topModalNode)) { continue; @@ -1376,6 +1391,17 @@ bool OverlayManager::RemoveAllModalInOverlay() if (!modalList_.empty()) { modalList_.pop_back(); } + + if (topModalNode->GetTag() == V2::MODAL_PAGE_TAG) { + auto modalPattern = topModalNode->GetPattern(); + CHECK_NULL_RETURN(modalPattern, false); + auto modalTransition = modalPattern->GetType(); + if (modalTransition == ModalTransition::NONE || builder->GetRenderContext()->HasTransition()) { + // Fire shown event of navdestination under the disappeared modal + FireNavigationStateChange(true); + } + } + FireModalPageHide(); SaveLastModalNode(); } @@ -1393,6 +1419,8 @@ bool OverlayManager::ModalExitProcess(const RefPtr& topModalNode) if (builder->GetRenderContext()->HasTransition()) { if (!topModalNode->GetPattern()->IsExecuteOnDisappear()) { topModalNode->GetPattern()->OnDisappear(); + // Fire hidden event of navdestination on the disappeared modal + FireNavigationStateChange(false, topModalNode); } topModalNode->Clean(false, true); topModalNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); @@ -1403,6 +1431,8 @@ bool OverlayManager::ModalExitProcess(const RefPtr& topModalNode) PlayAlphaModalTransition(topModalNode, false); } else if (!builder->GetRenderContext()->HasTransition()) { topModalNode->GetPattern()->OnDisappear(); + // Fire hidden event of navdestination on the disappeared modal + FireNavigationStateChange(false, topModalNode); rootNode->RemoveChild(topModalNode); rootNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); } @@ -1604,6 +1634,35 @@ void OverlayManager::SaveLastModalNode() } } +void OverlayManager::FireNavigationStateChange(bool show, const RefPtr& node) +{ + if (!show && node) { + // Only check node When it is appointed + NavigationPattern::FireNavigationStateChange(node, show); + return; + } + + // Fire show event with non-empty stack. Only Check top modal node. + auto topModalNode = modalStack_.empty() ? nullptr : modalStack_.top().Upgrade(); + if (show && topModalNode) { + // Modal always displays on top of stage. If it existed, only need to check the top of modal stack. + NavigationPattern::FireNavigationStateChange(topModalNode, show); + return; + } + + auto rootNode = rootNodeWeak_.Upgrade(); + CHECK_NULL_VOID(rootNode); + const auto& children = rootNode->GetChildren(); + for (auto iter = children.begin(); iter != children.end(); ++iter) { + auto& child = *iter; + if (!show && child == topModalNode) { + // Do not check top modal if firing hidden event + continue; + } + NavigationPattern::FireNavigationStateChange(child, show); + } +} + void OverlayManager::BindContentCover(bool isShow, std::function&& callback, std::function()>&& buildNodeFunc, NG::ModalStyle& modalStyle, std::function&& onAppear, std::function&& onDisappear, int32_t targetId) @@ -1653,6 +1712,8 @@ void OverlayManager::BindContentCover(bool isShow, std::function