From 04ed0e05ff1808efdcd31f4565d17cf6b39fd061 Mon Sep 17 00:00:00 2001 From: zhanghang Date: Wed, 3 Sep 2025 10:16:47 +0800 Subject: [PATCH] observer add tab content change. Signed-off-by: zhanghang --- .../components_ng/base/observer_handler.cpp | 17 ++ .../components_ng/base/observer_handler.h | 5 + .../pattern/swiper/swiper_pattern.h | 5 + .../pattern/tabs/tabs_pattern.cpp | 84 +++++++- .../components_ng/pattern/tabs/tabs_pattern.h | 14 +- .../napi/kits/observer/js_ui_observer.cpp | 62 ++++++ .../napi/kits/observer/js_ui_observer.h | 3 + interfaces/napi/kits/observer/ui_observer.cpp | 119 +++++++++++ interfaces/napi/kits/observer/ui_observer.h | 23 +++ .../kits/observer/ui_observer_listener.cpp | 27 +-- .../napi/kits/observer/ui_observer_listener.h | 1 + .../core/pattern/tabs/tabs_event_test_ng.cpp | 194 ++++++++++++++++++ 12 files changed, 539 insertions(+), 15 deletions(-) diff --git a/frameworks/core/components_ng/base/observer_handler.cpp b/frameworks/core/components_ng/base/observer_handler.cpp index 0078c856c1c..b06779378ca 100644 --- a/frameworks/core/components_ng/base/observer_handler.cpp +++ b/frameworks/core/components_ng/base/observer_handler.cpp @@ -204,6 +204,18 @@ void UIObserverHandler::NotifyTabContentStateUpdate(const TabContentInfo& info) tabContentStateHandleFunc_(info); } +void UIObserverHandler::NotifyTabContentStateChange(const TabContentInfo& info) +{ + NotifyTabContentStateChange(tabContentStateChangeHandleFunc_, info); +} + +void UIObserverHandler::NotifyTabContentStateChange(const TabContentStateHandleFunc& func, + const TabContentInfo& info) +{ + CHECK_NULL_VOID(func); + func(info); +} + UIObserverHandler::NavDestinationSwitchHandleFunc UIObserverHandler::GetHandleNavDestinationSwitchFunc() { return navDestinationSwitchHandleFunc_; @@ -439,6 +451,11 @@ void UIObserverHandler::SetHandleTabContentStateUpdateFunc(TabContentStateHandle tabContentStateHandleFunc_ = func; } +void UIObserverHandler::SetHandleTabContentStateChangeFunc(TabContentStateHandleFunc func) +{ + tabContentStateChangeHandleFunc_ = func; +} + napi_value UIObserverHandler::GetUIContextValue() { auto container = Container::Current(); diff --git a/frameworks/core/components_ng/base/observer_handler.h b/frameworks/core/components_ng/base/observer_handler.h index 3477f7d3d0c..2a334dca689 100644 --- a/frameworks/core/components_ng/base/observer_handler.h +++ b/frameworks/core/components_ng/base/observer_handler.h @@ -150,6 +150,7 @@ struct TabContentInfo { int32_t index = 0; std::string id; int32_t uniqueId = 0; + std::optional lastIndex; TabContentInfo(std::string tabContentId, int32_t tabContentUniqueId, TabContentState state, int32_t index, std::string id, int32_t uniqueId) @@ -189,6 +190,7 @@ public: const RefPtr& current, const RefPtr& frameNode, const PanGestureInfo& panGestureInfo); void NotifyTabContentStateUpdate(const TabContentInfo& info); + void NotifyTabContentStateChange(const TabContentInfo& info); void NotifyGestureStateChange(NG::GestureListenerType gestureListenerType, const GestureEvent& gestureEventInfo, const RefPtr& current, const RefPtr& frameNode, NG::GestureActionPhase phase); std::shared_ptr GetNavigationState(const RefPtr& node); @@ -231,6 +233,7 @@ public: void SetWillClickFunc(WillClickHandleFunc func); void SetDidClickFunc(DidClickHandleFunc func); void SetPanGestureHandleFunc(PanGestureHandleFunc func); + void SetHandleTabContentStateChangeFunc(TabContentStateHandleFunc func); void SetHandleTabContentStateUpdateFunc(TabContentStateHandleFunc func); void SetHandleGestureHandleFunc(GestureHandleFunc func); private: @@ -246,9 +249,11 @@ private: DidClickHandleFunc didClickHandleFunc_ = nullptr; PanGestureHandleFunc panGestureHandleFunc_ = nullptr; TabContentStateHandleFunc tabContentStateHandleFunc_ = nullptr; + TabContentStateHandleFunc tabContentStateChangeHandleFunc_ = nullptr; GestureHandleFunc gestureHandleFunc_ = nullptr; napi_value GetUIContextValue(); + static void NotifyTabContentStateChange(const TabContentStateHandleFunc& func, const TabContentInfo& info); }; } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/swiper/swiper_pattern.h b/frameworks/core/components_ng/pattern/swiper/swiper_pattern.h index 0dab285e3f6..d6b82da67bc 100644 --- a/frameworks/core/components_ng/pattern/swiper/swiper_pattern.h +++ b/frameworks/core/components_ng/pattern/swiper/swiper_pattern.h @@ -162,6 +162,11 @@ public: int32_t GetCurrentIndex(bool original = false); + int32_t GetOldIndex() const + { + return oldIndex_; + } + float GetTurnPageRate() const { return turnPageRate_; diff --git a/frameworks/core/components_ng/pattern/tabs/tabs_pattern.cpp b/frameworks/core/components_ng/pattern/tabs/tabs_pattern.cpp index b6ea16fc8cd..7232738e045 100644 --- a/frameworks/core/components_ng/pattern/tabs/tabs_pattern.cpp +++ b/frameworks/core/components_ng/pattern/tabs/tabs_pattern.cpp @@ -105,7 +105,7 @@ void TabsPattern::SetOnChangeEvent(std::function&& e } } -void TabsPattern::FireTabContentStateCallback(int32_t oldIndex, int32_t nextIndex) const +void TabsPattern::FireTabContentStateCallback(int32_t oldIndex, int32_t nextIndex) { auto tabsNode = AceType::DynamicCast(GetHost()); CHECK_NULL_VOID(tabsNode); @@ -121,6 +121,15 @@ void TabsPattern::FireTabContentStateCallback(int32_t oldIndex, int32_t nextInde TabContentInfo oldTabContentInfo(oldTabContentId, oldTabContentUniqueId, TabContentState::ON_HIDE, oldIndex, id, uniqueId); UIObserverHandler::GetInstance().NotifyTabContentStateUpdate(oldTabContentInfo); + + if (lastNotifyTabContentInfo_.has_value() && + CanFireTabContentStateChange(lastNotifyTabContentInfo_, oldIndex, false)) { + oldTabContentInfo.lastIndex = lastNotifyTabContentInfo_.value().lastFocusIndex; + UIObserverHandler::GetInstance().NotifyTabContentStateChange(oldTabContentInfo); + auto& lastNotifyTabContentInfo = lastNotifyTabContentInfo_.value(); + lastNotifyTabContentInfo.index = oldIndex; + lastNotifyTabContentInfo.isShow = false; + } } auto nextTabContent = AceType::DynamicCast(swiperNode->GetChildByIndex(nextIndex)); @@ -130,9 +139,80 @@ void TabsPattern::FireTabContentStateCallback(int32_t oldIndex, int32_t nextInde TabContentInfo nextTabContentInfo(nextTabContentId, nextTabContentUniqueId, TabContentState::ON_SHOW, nextIndex, id, uniqueId); UIObserverHandler::GetInstance().NotifyTabContentStateUpdate(nextTabContentInfo); + + if (CanFireTabContentStateChange(lastNotifyTabContentInfo_, nextIndex, true)) { + if (!lastNotifyTabContentInfo_.has_value()) { + lastNotifyTabContentInfo_ = NotifyTabContentInfo(); + } + nextTabContentInfo.lastIndex = lastNotifyTabContentInfo_.value().lastFocusIndex; + UIObserverHandler::GetInstance().NotifyTabContentStateChange(nextTabContentInfo); + auto& lastNotifyTabContentInfo = lastNotifyTabContentInfo_.value(); + lastNotifyTabContentInfo.index = nextIndex; + lastNotifyTabContentInfo.isShow = true; + lastNotifyTabContentInfo.lastFocusIndex = nextIndex; + } } } +int32_t TabsPattern::ToValidTabContentIndex(int32_t tabContentNum, int32_t index) +{ + return ((index >= 0) && (index < tabContentNum)) ? index : 0; +} + +bool TabsPattern::CanFireTabContentStateChange(const std::optional& lastNotifyTabContentInfo, + int32_t index, bool isShow) +{ + CHECK_EQUAL_RETURN(lastNotifyTabContentInfo.has_value(), false, true); + CHECK_NE_RETURN(lastNotifyTabContentInfo.value().index, index, true); + CHECK_NE_RETURN(lastNotifyTabContentInfo.value().isShow, isShow, true); + return false; +} + +bool TabsPattern::ShouldFireTabContentStateChangeFirstShow(bool isInit, int32_t tabContentNum, + int32_t showIndex, int32_t currentIndex, int32_t oldIndex) +{ + CHECK_EQUAL_RETURN((tabContentNum <= 0), true, false); + CHECK_EQUAL_RETURN(isInit, true, true); + CHECK_NE_RETURN(showIndex, currentIndex, false); + CHECK_NE_RETURN(currentIndex, oldIndex, false); + return true; +} + +void TabsPattern::FireTabContentStateChangeFirstShow(bool isInit, int32_t tabContentNum, int32_t showIndex) +{ + if (tabContentNum <= 0) { + lastNotifyTabContentInfo_.reset(); + return; + } + CHECK_EQUAL_VOID(lastNotifyTabContentInfo_.has_value(), true); + auto tabsNode = AceType::DynamicCast(GetHost()); + CHECK_NULL_VOID(tabsNode); + auto swiperNode = AceType::DynamicCast(tabsNode->GetTabs()); + CHECK_NULL_VOID(swiperNode); + auto swiperPattern = swiperNode->GetPattern(); + CHECK_NULL_VOID(swiperPattern); + int32_t currentIndex = swiperPattern->GetCurrentIndex(); + int32_t oldIndex = swiperPattern->GetOldIndex(); + auto isNotify = ShouldFireTabContentStateChangeFirstShow(isInit, tabContentNum, showIndex, currentIndex, oldIndex); + CHECK_EQUAL_VOID(isNotify, false); + showIndex = (isInit) ? swiperPattern->GetLoopIndex(showIndex) : currentIndex; + showIndex = ToValidTabContentIndex(tabContentNum, showIndex); + auto tabContentNode = AceType::DynamicCast(swiperNode->GetChildByIndex(showIndex)); + CHECK_NULL_VOID(tabContentNode); + std::string id = tabsNode->GetInspectorId().value_or(""); + int32_t uniqueId = tabsNode->GetId(); + std::string tabContentNodeId = tabContentNode->GetInspectorId().value_or(""); + int32_t tabContentNodeUniqueId = tabContentNode->GetId(); + TabContentInfo tabContentInfo(tabContentNodeId, tabContentNodeUniqueId, TabContentState::ON_SHOW, showIndex, + id, uniqueId); + UIObserverHandler::GetInstance().NotifyTabContentStateChange(tabContentInfo); + lastNotifyTabContentInfo_ = NotifyTabContentInfo(); + auto& lastNotifyTabContentInfo = lastNotifyTabContentInfo_.value(); + lastNotifyTabContentInfo.index = showIndex; + lastNotifyTabContentInfo.isShow = true; + lastNotifyTabContentInfo.lastFocusIndex = showIndex; +} + void TabsPattern::RecordChangeEvent(int32_t index) { auto tabsNode = AceType::DynamicCast(GetHost()); @@ -621,6 +701,7 @@ void TabsPattern::BeforeCreateLayoutWrapper() auto willShowIndex = tabsLayoutProperty->GetIndex().value_or(0); swiperPattern->FireSelectedEvent(-1, willShowIndex); swiperPattern->FireWillShowEvent(willShowIndex); + FireTabContentStateChangeFirstShow(true, swiperNode->TotalChildCount(), willShowIndex); } isInit_ = false; } @@ -637,6 +718,7 @@ void TabsPattern::BeforeCreateLayoutWrapper() index = 0; } UpdateSelectedState(swiperNode, tabBarPattern, tabsLayoutProperty, index); + FireTabContentStateChangeFirstShow(false, tabContentNum, index); } } diff --git a/frameworks/core/components_ng/pattern/tabs/tabs_pattern.h b/frameworks/core/components_ng/pattern/tabs/tabs_pattern.h index 98b40377562..079bab3900d 100644 --- a/frameworks/core/components_ng/pattern/tabs/tabs_pattern.h +++ b/frameworks/core/components_ng/pattern/tabs/tabs_pattern.h @@ -176,7 +176,18 @@ private: void UpdateSwiperDisableSwipe(bool disableSwipe); void SetSwiperPaddingAndBorder(); void RecordChangeEvent(int32_t index); - void FireTabContentStateCallback(int32_t oldIndex, int32_t nextIndex) const; + void FireTabContentStateCallback(int32_t oldIndex, int32_t nextIndex); + static int32_t ToValidTabContentIndex(int32_t tabContentNum, int32_t index); + struct NotifyTabContentInfo { + int32_t index = 0; // last notify index + bool isShow = false; // show state + std::optional lastFocusIndex; + }; + static bool CanFireTabContentStateChange(const std::optional& lastNotifyTabContentInfo, + int32_t index, bool isShow); + static bool ShouldFireTabContentStateChangeFirstShow(bool isInit, int32_t tabContentNum, + int32_t showIndex, int32_t currentIndex, int32_t oldIndex); + void FireTabContentStateChangeFirstShow(bool isInit, int32_t tabContentNum, int32_t showIndex); void UpdateIndex(const RefPtr& tabsNode, const RefPtr& tabBarNode, const RefPtr& swiperNode, const RefPtr& tabsLayoutProperty); void InitFocusEvent(); @@ -198,6 +209,7 @@ private: std::function callback_; bool interceptStatus_ = false; BarPosition barPosition_ = BarPosition::END; // default accessibilityZIndex is consistent with BarPosition::END + std::optional lastNotifyTabContentInfo_; }; } // namespace OHOS::Ace::NG diff --git a/interfaces/napi/kits/observer/js_ui_observer.cpp b/interfaces/napi/kits/observer/js_ui_observer.cpp index 1820239e1d7..51858a972a9 100644 --- a/interfaces/napi/kits/observer/js_ui_observer.cpp +++ b/interfaces/napi/kits/observer/js_ui_observer.cpp @@ -95,6 +95,7 @@ constexpr char DRAW_COMMAND_SEND[] = "willDraw"; constexpr char NAVDESTINATION_SWITCH[] = "navDestinationSwitch"; constexpr char WILLCLICK_UPDATE[] = "willClick"; constexpr char DIDCLICK_UPDATE[] = "didClick"; +constexpr char TAB_CONTENT_STATE_CHANGE[] = "tabChange"; constexpr char TAB_CONTENT_STATE[] = "tabContentUpdate"; constexpr char BEFORE_PAN_START[] = "beforePanStart"; constexpr char BEFORE_PAN_END[] = "beforePanEnd"; @@ -381,6 +382,7 @@ ObserverProcess::ObserverProcess() { NAVDESTINATION_SWITCH, &ObserverProcess::ProcessNavDestinationSwitchRegister }, { WILLCLICK_UPDATE, &ObserverProcess::ProcessWillClickRegister }, { DIDCLICK_UPDATE, &ObserverProcess::ProcessDidClickRegister }, + { TAB_CONTENT_STATE_CHANGE, &ObserverProcess::ProcessTabContentChangeRegister }, { TAB_CONTENT_STATE, &ObserverProcess::ProcessTabContentStateRegister }, { BEFORE_PAN_START, &ObserverProcess::ProcessBeforePanStartRegister }, { AFTER_PAN_START, &ObserverProcess::ProcessAfterPanStartRegister }, @@ -399,6 +401,7 @@ ObserverProcess::ObserverProcess() { NAVDESTINATION_SWITCH, &ObserverProcess::ProcessNavDestinationSwitchUnRegister }, { WILLCLICK_UPDATE, &ObserverProcess::ProcessWillClickUnRegister }, { DIDCLICK_UPDATE, &ObserverProcess::ProcessDidClickUnRegister }, + { TAB_CONTENT_STATE_CHANGE, &ObserverProcess::ProcessTabContentChangeUnRegister }, { TAB_CONTENT_STATE, &ObserverProcess::ProcessTabContentStateUnRegister }, { BEFORE_PAN_START, &ObserverProcess::ProcessBeforePanStartUnRegister }, { AFTER_PAN_START, &ObserverProcess::ProcessAfterPanStartUnRegister }, @@ -1107,6 +1110,65 @@ napi_value ObserverProcess::ProcessTabContentStateUnRegister(napi_env env, napi_ return result; } +napi_value ObserverProcess::ProcessTabContentChangeRegister(napi_env env, napi_callback_info info) +{ + GET_PARAMS(env, info, PARAM_SIZE_THREE); + + if (!isTabContentStateChangeFuncSetted_) { + NG::UIObserverHandler::GetInstance().SetHandleTabContentStateChangeFunc( + &UIObserver::HandleTabContentChange); + isTabContentStateChangeFuncSetted_ = true; + } + + if (argc == PARAM_SIZE_TWO && MatchValueType(env, argv[PARAM_INDEX_ONE], napi_function)) { + auto listener = std::make_shared(env, argv[PARAM_INDEX_ONE]); + UIObserver::RegisterTabContentChangeCallback(listener); + } + + if (argc == PARAM_SIZE_THREE && MatchValueType(env, argv[PARAM_INDEX_ONE], napi_object) + && MatchValueType(env, argv[PARAM_INDEX_TWO], napi_function)) { + std::string id; + if (ParseScrollId(env, argv[PARAM_INDEX_ONE], id)) { + auto listener = std::make_shared(env, argv[PARAM_INDEX_TWO]); + UIObserver::RegisterTabContentChangeCallback(id, listener); + } + } + + napi_value result = nullptr; + return result; +} + +napi_value ObserverProcess::ProcessTabContentChangeUnRegister(napi_env env, napi_callback_info info) +{ + GET_PARAMS(env, info, PARAM_SIZE_THREE); + + if (argc == PARAM_SIZE_ONE) { + UIObserver::UnRegisterTabContentChangeCallback(nullptr); + } + + if (argc == PARAM_SIZE_TWO && MatchValueType(env, argv[PARAM_INDEX_ONE], napi_function)) { + UIObserver::UnRegisterTabContentChangeCallback(argv[PARAM_INDEX_ONE]); + } + + if (argc == PARAM_SIZE_TWO && MatchValueType(env, argv[PARAM_INDEX_ONE], napi_object)) { + std::string id; + if (ParseScrollId(env, argv[PARAM_INDEX_ONE], id)) { + UIObserver::UnRegisterTabContentChangeCallback(id, nullptr); + } + } + + if (argc == PARAM_SIZE_THREE && MatchValueType(env, argv[PARAM_INDEX_ONE], napi_object) + && MatchValueType(env, argv[PARAM_INDEX_TWO], napi_function)) { + std::string id; + if (ParseScrollId(env, argv[PARAM_INDEX_ONE], id)) { + UIObserver::UnRegisterTabContentChangeCallback(id, argv[PARAM_INDEX_TWO]); + } + } + + napi_value result = nullptr; + return result; +} + napi_value ObserverProcess::ProcessBeforePanStartRegister(napi_env env, napi_callback_info info) { GET_PARAMS(env, info, PARAM_SIZE_THREE); diff --git a/interfaces/napi/kits/observer/js_ui_observer.h b/interfaces/napi/kits/observer/js_ui_observer.h index 631937bd09a..86a8b826212 100644 --- a/interfaces/napi/kits/observer/js_ui_observer.h +++ b/interfaces/napi/kits/observer/js_ui_observer.h @@ -55,6 +55,8 @@ private: napi_value ProcessDidClickUnRegister(napi_env env, napi_callback_info info); napi_value ProcessTabContentStateRegister(napi_env env, napi_callback_info info); napi_value ProcessTabContentStateUnRegister(napi_env env, napi_callback_info info); + napi_value ProcessTabContentChangeRegister(napi_env env, napi_callback_info info); + napi_value ProcessTabContentChangeUnRegister(napi_env env, napi_callback_info info); napi_value ProcessBeforePanStartRegister(napi_env env, napi_callback_info info); napi_value ProcessBeforePanStartUnRegister(napi_env env, napi_callback_info info); napi_value ProcessBeforePanEndRegister(napi_env env, napi_callback_info info); @@ -77,6 +79,7 @@ private: bool isDidClickFuncSetted_ = false; bool isPanGestureHandleFuncSetted_ = false; bool isTabContentStateUpdateFuncSetted_ = false; + bool isTabContentStateChangeFuncSetted_ = false; bool isRouterPageHandleFuncSetted_ = false; bool isDestinationSwitchHandleFuncSetted_ = false; std::map registerProcessMap_; diff --git a/interfaces/napi/kits/observer/ui_observer.cpp b/interfaces/napi/kits/observer/ui_observer.cpp index f80a12fa4f4..35b6292c054 100644 --- a/interfaces/napi/kits/observer/ui_observer.cpp +++ b/interfaces/napi/kits/observer/ui_observer.cpp @@ -60,6 +60,10 @@ std::list> UIObserver::tabContentStateListen std::unordered_map>> UIObserver::specifiedTabContentStateListeners_; +std::list> UIObserver::tabContentChangeListeners_; +std::unordered_map>> + UIObserver::specifiedTabContentChangeListeners_; + std::unordered_map>> UIObserver::abilityContextBeforePanStartListeners_; std::unordered_map>> @@ -1747,6 +1751,16 @@ void UIObserver::RegisterTabContentStateCallback(const std::shared_ptr>& tabListeners, + const std::shared_ptr& listener) +{ + if (std::find(tabListeners.begin(), tabListeners.end(), listener) != tabListeners.end()) { + return; + } + tabListeners.emplace_back(listener); +} + // UIObserver.on(type: "tabContentState", options, callback) // register a listener on a specified tabContentState void UIObserver::RegisterTabContentStateCallback( @@ -1764,6 +1778,22 @@ void UIObserver::RegisterTabContentStateCallback( holder.emplace_back(listener); } +void UIObserver::RegisterTabContentStateCallback( + std::unordered_map>>& specifiedTabListeners, + const std::string& id, const std::shared_ptr& listener) +{ + auto iter = specifiedTabListeners.find(id); + if (iter == specifiedTabListeners.end()) { + specifiedTabListeners.emplace(id, std::list>({ listener })); + return; + } + auto& holder = iter->second; + if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { + return; + } + holder.emplace_back(listener); +} + // UIObserver.off(type: "tabContentState", callback) void UIObserver::UnRegisterTabContentStateCallback(napi_value cb) { @@ -1783,6 +1813,26 @@ void UIObserver::UnRegisterTabContentStateCallback(napi_value cb) ); } +void UIObserver::UnRegisterTabContentStateCallback( + std::list>& tabListeners, + napi_value cb) +{ + if (cb == nullptr) { + tabListeners.clear(); + return; + } + + tabListeners.erase( + std::remove_if( + tabListeners.begin(), + tabListeners.end(), + [cb](const std::shared_ptr& registeredListener) { + return registeredListener->NapiEqual(cb); + }), + tabListeners.end() + ); +} + // UIObserver.off(type: "tabContentState", options, callback) void UIObserver::UnRegisterTabContentStateCallback(const std::string& id, napi_value cb) { @@ -1806,6 +1856,30 @@ void UIObserver::UnRegisterTabContentStateCallback(const std::string& id, napi_v ); } +void UIObserver::UnRegisterTabContentStateCallback( + std::unordered_map>>& specifiedTabListeners, + const std::string& id, napi_value cb) +{ + auto iter = specifiedTabListeners.find(id); + if (iter == specifiedTabListeners.end()) { + return; + } + auto& holder = iter->second; + if (cb == nullptr) { + holder.clear(); + return; + } + holder.erase( + std::remove_if( + holder.begin(), + holder.end(), + [cb](const std::shared_ptr& registeredListener) { + return registeredListener->NapiEqual(cb); + }), + holder.end() + ); +} + void UIObserver::HandleTabContentStateChange(const NG::TabContentInfo& tabContentInfo) { for (const auto& listener : tabContentStateListeners_) { @@ -1824,6 +1898,51 @@ void UIObserver::HandleTabContentStateChange(const NG::TabContentInfo& tabConten } } +// UIObserver.on(type: "tabChange", callback) +// register a global listener without options +void UIObserver::RegisterTabContentChangeCallback(const std::shared_ptr& listener) +{ + RegisterTabContentStateCallback(tabContentChangeListeners_, listener); +} + +// UIObserver.on(type: "tabChange", options, callback) +// register a listener on a specified tabContentState +void UIObserver::RegisterTabContentChangeCallback( + const std::string& id, const std::shared_ptr& listener) +{ + RegisterTabContentStateCallback(specifiedTabContentChangeListeners_, id, listener); +} + +// UIObserver.off(type: "tabChange", callback) +void UIObserver::UnRegisterTabContentChangeCallback(napi_value cb) +{ + UnRegisterTabContentStateCallback(tabContentChangeListeners_, cb); +} + +// UIObserver.off(type: "tabChange", options, callback) +void UIObserver::UnRegisterTabContentChangeCallback(const std::string& id, napi_value cb) +{ + UnRegisterTabContentStateCallback(specifiedTabContentChangeListeners_, id, cb); +} + +void UIObserver::HandleTabContentChange(const NG::TabContentInfo& tabContentInfo) +{ + for (const auto& listener : tabContentChangeListeners_) { + listener->OnTabContentChange(tabContentInfo); + } + + auto iter = specifiedTabContentChangeListeners_.find(tabContentInfo.id); + if (iter == specifiedTabContentChangeListeners_.end()) { + return; + } + + auto& holder = iter->second; + + for (const auto& listener : holder) { + listener->OnTabContentChange(tabContentInfo); + } +} + void UIObserver::GetAbilityInfos(napi_env env, napi_value abilityContext, NG::AbilityContextInfo& info) { if (!env || !abilityContext) { diff --git a/interfaces/napi/kits/observer/ui_observer.h b/interfaces/napi/kits/observer/ui_observer.h index 54fb0855977..1b055fcdb68 100644 --- a/interfaces/napi/kits/observer/ui_observer.h +++ b/interfaces/napi/kits/observer/ui_observer.h @@ -119,6 +119,13 @@ public: static void UnRegisterTabContentStateCallback(const std::string& id, napi_value cb); static void HandleTabContentStateChange(const NG::TabContentInfo& tabContentInfo); + static void RegisterTabContentChangeCallback(const std::shared_ptr& listener); + static void RegisterTabContentChangeCallback( + const std::string& id, const std::shared_ptr& listener); + static void UnRegisterTabContentChangeCallback(napi_value cb); + static void UnRegisterTabContentChangeCallback(const std::string& id, napi_value cb); + static void HandleTabContentChange(const NG::TabContentInfo& tabContentInfo); + static void RegisterBeforePanStartCallback( napi_env env, napi_value uiAbilityContext, const std::shared_ptr& listener); static void RegisterBeforePanStartCallback( @@ -171,6 +178,19 @@ private: static void GetAbilityInfos(napi_env env, napi_value abilityContext, NG::AbilityContextInfo& info); static napi_env GetCurrentNapiEnv(); + static void RegisterTabContentStateCallback( + std::list>& tabListeners, + const std::shared_ptr& listener); + static void RegisterTabContentStateCallback( + std::unordered_map>>& specifiedTabListeners, + const std::string& id, const std::shared_ptr& listener); + static void UnRegisterTabContentStateCallback( + std::list>& tabListeners, + napi_value cb); + static void UnRegisterTabContentStateCallback( + std::unordered_map>>& specifiedTabListeners, + const std::string& id, napi_value cb); + static std::list> unspecifiedNavigationListeners_; static std::unordered_map>> specifiedCNavigationListeners_; @@ -198,6 +218,9 @@ private: static std::list> tabContentStateListeners_; static std::unordered_map>> specifiedTabContentStateListeners_; + static std::list> tabContentChangeListeners_; + static std::unordered_map>> + specifiedTabContentChangeListeners_; static std::unordered_map>> abilityContextBeforePanStartListeners_; static std::unordered_map>> diff --git a/interfaces/napi/kits/observer/ui_observer_listener.cpp b/interfaces/napi/kits/observer/ui_observer_listener.cpp index 4bb0e1b2a54..88db911df37 100644 --- a/interfaces/napi/kits/observer/ui_observer_listener.cpp +++ b/interfaces/napi/kits/observer/ui_observer_listener.cpp @@ -26,6 +26,7 @@ constexpr int32_t PARAM_SIZE_ONE = 1; constexpr int32_t PARAM_SIZE_TWO = 2; constexpr int32_t PARAM_SIZE_THREE = 3; constexpr int32_t PARAM_SIZE_SIX = 6; +constexpr int32_t PARAM_SIZE_SEVEN = 7; bool CheckKeysPressed(const std::vector& pressedKeyCodes, std::vector& checkKeyCodes) { @@ -565,34 +566,34 @@ void UIObserverListener::OnTabContentStateChange(const NG::TabContentInfo& tabCo napi_value param4 = nullptr; napi_value param5 = nullptr; napi_value param6 = nullptr; + napi_value param7 = nullptr; napi_create_string_utf8(env_, tabContentInfo.tabContentId.c_str(), tabContentInfo.tabContentId.length(), ¶m1); napi_create_int32(env_, tabContentInfo.tabContentUniqueId, ¶m2); napi_create_int32(env_, static_cast(tabContentInfo.state), ¶m3); napi_create_int32(env_, tabContentInfo.index, ¶m4); napi_create_string_utf8(env_, tabContentInfo.id.c_str(), tabContentInfo.id.length(), ¶m5); napi_create_int32(env_, tabContentInfo.uniqueId, ¶m6); + if (tabContentInfo.lastIndex.has_value()) { + napi_create_int32(env_, tabContentInfo.lastIndex.value(), ¶m7); + } const char *keys[] = { - "tabContentId", - "tabContentUniqueId", - "state", - "index", - "id", - "uniqueId", + "tabContentId", "tabContentUniqueId", "state", "index", "id", "uniqueId", "lastIndex", }; const napi_value values[] = { - param1, - param2, - param3, - param4, - param5, - param6, + param1, param2, param3, param4, param5, param6, param7, }; - napi_create_object_with_named_properties(env_, &objValue, PARAM_SIZE_SIX, keys, values); + const int32_t paramSize = tabContentInfo.lastIndex.has_value() ? PARAM_SIZE_SEVEN : PARAM_SIZE_SIX; + napi_create_object_with_named_properties(env_, &objValue, paramSize, keys, values); napi_value argv[] = { objValue }; napi_call_function(env_, nullptr, callback, 1, argv, nullptr); napi_close_handle_scope(env_, scope); } +void UIObserverListener::OnTabContentChange(const NG::TabContentInfo& tabContentInfo) +{ + OnTabContentStateChange(tabContentInfo); +} + napi_valuetype UIObserverListener::GetValueType(napi_env env, napi_value value) { if (value == nullptr) { diff --git a/interfaces/napi/kits/observer/ui_observer_listener.h b/interfaces/napi/kits/observer/ui_observer_listener.h index 461238302a7..cf483439c36 100644 --- a/interfaces/napi/kits/observer/ui_observer_listener.h +++ b/interfaces/napi/kits/observer/ui_observer_listener.h @@ -54,6 +54,7 @@ public: const RefPtr& current, const RefPtr frameNode, NG::GestureActionPhase phase); void OnTabContentStateChange(const NG::TabContentInfo& tabContentInfo); + void OnTabContentChange(const NG::TabContentInfo& tabContentInfo); void OnNodeRenderStateChange(NG::FrameNode* frameNode, NG::NodeRenderState nodeRenderState); void OnNavDestinationSwitch(const NG::NavDestinationSwitchInfo& switchInfo); bool NapiEqual(napi_value cb); diff --git a/test/unittest/core/pattern/tabs/tabs_event_test_ng.cpp b/test/unittest/core/pattern/tabs/tabs_event_test_ng.cpp index b4ab988d110..a7e1188a1a0 100644 --- a/test/unittest/core/pattern/tabs/tabs_event_test_ng.cpp +++ b/test/unittest/core/pattern/tabs/tabs_event_test_ng.cpp @@ -1319,6 +1319,200 @@ HWTEST_F(TabsEventTestNg, ObserverTestNg001, TestSize.Level1) UIObserverHandler::GetInstance().SetHandleTabContentStateUpdateFunc(nullptr); } +/** + * @tc.name: ObserverTestNg002 + * @tc.desc: Test the operation of Observer + * @tc.type: FUNC + */ +HWTEST_F(TabsEventTestNg, ObserverTestNg002, TestSize.Level1) +{ + /** + * @tc.steps: steps1. Init tabContent state update callback the swipe to 1 + * @tc.expected: steps1. Check state value. + */ + auto func = [](const TabContentInfo& info) { + EXPECT_EQ(info.index, 0); + EXPECT_EQ(info.state, TabContentState::ON_SHOW); + }; + UIObserverHandler::GetInstance().SetHandleTabContentStateChangeFunc(func); + + TabsModelNG model = CreateTabs(); + CreateTabContents(TABCONTENT_NUMBER); + CreateTabsDone(model); + + auto func2 = [](const TabContentInfo& info) { + if (info.index == 0) { + EXPECT_EQ(info.state, TabContentState::ON_HIDE); + } else if (info.index == 1) { + EXPECT_EQ(info.state, TabContentState::ON_SHOW); + } + }; + + UIObserverHandler::GetInstance().SetHandleTabContentStateChangeFunc(func2); + ChangeIndex(1); + UIObserverHandler::GetInstance().SetHandleTabContentStateChangeFunc(nullptr); +} + +/** + * @tc.name: ObserverTestNg003 + * @tc.desc: Test the operation of Observer + * @tc.type: FUNC + */ +HWTEST_F(TabsEventTestNg, ObserverTestNg003, TestSize.Level1) +{ + /** + * @tc.steps: steps1. Init tabContent index to 1 + * @tc.expected: steps1. Check state value. + */ + auto func = [](const TabContentInfo& info) { + EXPECT_EQ(info.index, 1); + EXPECT_EQ(info.state, TabContentState::ON_SHOW); + }; + UIObserverHandler::GetInstance().SetHandleTabContentStateChangeFunc(func); + + TabsModelNG model = CreateTabs(BarPosition::START, 1); + + CreateTabContents(TABCONTENT_NUMBER); + CreateTabsDone(model); + + UIObserverHandler::GetInstance().SetHandleTabContentStateChangeFunc(nullptr); +} + +/** + * @tc.name: ToValidTabContentIndexTest001 + * @tc.desc: Test the ToValidTabContentIndex of TabsPattern + * @tc.type: FUNC + */ +HWTEST_F(TabsEventTestNg, ToValidTabContentIndexTest001, TestSize.Level1) +{ + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(-1, -1), 0); + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(-1, 0), 0); + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(-1, 1), 0); + + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(0, -1), 0); + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(0, 0), 0); + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(0, 1), 0); + + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(1, -1), 0); + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(1, 0), 0); + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(1, 1), 0); + + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(2, -1), 0); + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(2, 0), 0); + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(2, 1), 1); + EXPECT_EQ(TabsPattern::ToValidTabContentIndex(2, 2), 0); +} + +/** + * @tc.name: CanFireTabContentStateChangeTest001 + * @tc.desc: Test the CanFireTabContentStateChange of TabsPattern + * @tc.type: FUNC + */ +HWTEST_F(TabsEventTestNg, CanFireTabContentStateChangeTest001, TestSize.Level1) +{ + std::optional lastNotifyTabContentInfo; + EXPECT_TRUE(TabsPattern::CanFireTabContentStateChange(lastNotifyTabContentInfo, -1, false)); + EXPECT_TRUE(TabsPattern::CanFireTabContentStateChange(lastNotifyTabContentInfo, 0, false)); + EXPECT_TRUE(TabsPattern::CanFireTabContentStateChange(lastNotifyTabContentInfo, 0, true)); + + lastNotifyTabContentInfo = TabsPattern::NotifyTabContentInfo(); + lastNotifyTabContentInfo.value().index = 0; + lastNotifyTabContentInfo.value().isShow = false; + EXPECT_TRUE(TabsPattern::CanFireTabContentStateChange(lastNotifyTabContentInfo, -1, false)); + EXPECT_FALSE(TabsPattern::CanFireTabContentStateChange(lastNotifyTabContentInfo, 0, false)); + EXPECT_TRUE(TabsPattern::CanFireTabContentStateChange(lastNotifyTabContentInfo, 0, true)); +} + +/** + * @tc.name: ShouldFireTabContentStateChangeFirstShowTest001 + * @tc.desc: Test the ShouldFireTabContentStateChangeFirstShow of TabsPattern + * @tc.type: FUNC + */ +HWTEST_F(TabsEventTestNg, ShouldFireTabContentStateChangeFirstShowTest001, TestSize.Level1) +{ + EXPECT_FALSE(TabsPattern::ShouldFireTabContentStateChangeFirstShow(true, 0, 0, 0, 0)); + EXPECT_TRUE(TabsPattern::ShouldFireTabContentStateChangeFirstShow(true, 1, 0, 0, 0)); + EXPECT_FALSE(TabsPattern::ShouldFireTabContentStateChangeFirstShow(false, 0, 0, 0, 0)); + + EXPECT_TRUE(TabsPattern::ShouldFireTabContentStateChangeFirstShow(false, 1, 0, 0, 0)); + EXPECT_FALSE(TabsPattern::ShouldFireTabContentStateChangeFirstShow(false, 1, 1, 0, 0)); + EXPECT_FALSE(TabsPattern::ShouldFireTabContentStateChangeFirstShow(false, 1, 0, 1, 0)); + EXPECT_FALSE(TabsPattern::ShouldFireTabContentStateChangeFirstShow(false, 1, 0, 0, 1)); + EXPECT_FALSE(TabsPattern::ShouldFireTabContentStateChangeFirstShow(false, 1, 1, 0, 1)); + EXPECT_TRUE(TabsPattern::ShouldFireTabContentStateChangeFirstShow(false, 1, 1, 1, 1)); +} + +/** + * @tc.name: FireTabContentStateChangeFirstShowTest001 + * @tc.desc: Test FireTabContentStateChangeFirstShow of TabsPattern + * @tc.type: FUNC + */ +HWTEST_F(TabsEventTestNg, FireTabContentStateChangeFirstShowTest001, TestSize.Level1) +{ + /** + * @tc.steps: steps1. Init tabContent + * @tc.expected: steps1. Check state value. + */ + TabsModelNG model = CreateTabs(); + CreateTabContents(TABCONTENT_NUMBER); + CreateTabsDone(model); + ASSERT_NE(pattern_, nullptr); + + auto func = [](const TabContentInfo& info) { + EXPECT_EQ(info.index, 0); + EXPECT_EQ(info.state, TabContentState::ON_SHOW); + }; + UIObserverHandler::GetInstance().SetHandleTabContentStateChangeFunc(func); + + pattern_->FireTabContentStateChangeFirstShow(true, 0, 0); + pattern_->FireTabContentStateChangeFirstShow(true, TABCONTENT_NUMBER, 0); + + auto func2 = [](const TabContentInfo& info) { + EXPECT_EQ(info.index, 1); + EXPECT_EQ(info.state, TabContentState::ON_SHOW); + }; + UIObserverHandler::GetInstance().SetHandleTabContentStateChangeFunc(func2); + pattern_->FireTabContentStateChangeFirstShow(false, TABCONTENT_NUMBER, 1); + + TabContentInfo info("", 0, TabContentState::ON_SHOW, 1, "", 0); + UIObserverHandler::GetInstance().NotifyTabContentStateChange(info); + + UIObserverHandler::GetInstance().SetHandleTabContentStateChangeFunc(nullptr); +} + +/** + * @tc.name: FireTabContentStateCallbackTest001 + * @tc.desc: Test FireTabContentStateCallback of TabsPattern + * @tc.type: FUNC + */ +HWTEST_F(TabsEventTestNg, FireTabContentStateCallbackTest001, TestSize.Level1) +{ + /** + * @tc.steps: steps1. Init tabContent + * @tc.expected: steps1. Check state value. + */ + TabsModelNG model = CreateTabs(); + CreateTabContents(TABCONTENT_NUMBER); + CreateTabsDone(model); + ASSERT_NE(pattern_, nullptr); + + auto func = [](const TabContentInfo& info) { + if (info.index == 0) { + EXPECT_EQ(info.state, TabContentState::ON_HIDE); + } else { + EXPECT_EQ(info.state, TabContentState::ON_SHOW); + } + }; + UIObserverHandler::GetInstance().SetHandleTabContentStateChangeFunc(func); + + pattern_->lastNotifyTabContentInfo_ = TabsPattern::NotifyTabContentInfo(); + pattern_->lastNotifyTabContentInfo_.value().index = 0; + pattern_->lastNotifyTabContentInfo_.value().isShow = true; + pattern_->FireTabContentStateCallback(0, 1); + + UIObserverHandler::GetInstance().SetHandleTabContentStateChangeFunc(nullptr); +} + /** * @tc.name: OnAppearAndOnDisappearTest001 * @tc.desc: test OnAppear and OnDisappear -- Gitee