NotificationNode and GraphPriority in v3

In several of my v2.x projects, I have used the Director’s notificationNode as a scene independent alert view in place of the native alert views so that I can display the alert while the scene is transitioning behind it, and send the call back to the AppDelegate.

In v2.x I was able to get touch event blocking working by using setTouchPriority() to set the alert’s background layer’s priority to -10000, and the alert buttons’ (CCControlButtons) to -10001.

In v3.2, setTouchPriority() has been removed and the CCControlButtons use addEventListenerWithSceneGraphPriority(), and it looks like the notificationNode is not part of the EventDispatcher’s sortEventListeners(), and while the alert view responds to touches, it does not swallow them, allowing the buttons in the scene below to still respond.

My code for adding the listener:

auto dispatcher = Director::getInstance()->getEventDispatcher();
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->setSwallowTouches(true);
touchListener->onTouchBegan = CC_CALLBACK_2(TWAlertView::onTouchBegan, this);
touchListener->onTouchMoved = CC_CALLBACK_2(TWAlertView::onTouchMoved, this);
touchListener->onTouchEnded = CC_CALLBACK_2(TWAlertView::onTouchEnded, this);
touchListener->onTouchCancelled = CC_CALLBACK_2(TWAlertView::onTouchCancelled, this);

// dispatcher->addEventListenerWithFixedPriority(touchListener, -10000);
dispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);

I hacked around in EventDispatcher to get it working using the following code:

void EventDispatcher::sortEventListeners(const EventListener::ListenerID& listenerID)
{
    DirtyFlag dirtyFlag = DirtyFlag::NONE;

    auto dirtyIter = _priorityDirtyFlagMap.find(listenerID);
    if (dirtyIter != _priorityDirtyFlagMap.end())
    {
        dirtyFlag = dirtyIter->second;
    }

    if (dirtyFlag != DirtyFlag::NONE)
    {
        // Clear the dirty flag first, if `rootNode` is nullptr, then set its dirty flag of scene graph priority
    dirtyIter->second = DirtyFlag::NONE;

        if ((int)dirtyFlag & (int)DirtyFlag::FIXED_PRIORITY)
        {
            sortEventListenersOfFixedPriority(listenerID);
        }
    
        if ((int)dirtyFlag & (int)DirtyFlag::SCENE_GRAPH_PRIORITY)
        {
            // ADDED HERE...
            auto rootNNode = Director::getInstance()->getNotificationNode();
            if (rootNNode && rootNNode->isVisible())
            {
                sortEventListenersOfSceneGraphPriority(listenerID, rootNNode);
            }
            else
            {
            // ... TO HERE

                auto rootNode = Director::getInstance()->getRunningScene();
                if (rootNode)
                {
                    sortEventListenersOfSceneGraphPriority(listenerID, rootNode);
                }
                else
                {
                    dirtyIter->second = DirtyFlag::SCENE_GRAPH_PRIORITY;
                }
		
            } // ADDED
        }
    }
}

It seems there should be a better way to do this so that the notificationNode is included in the sorting rather than blocking out the running scene entirely. That could have the added benefit of allowing the node to only cover part of the scene thus allow some UI in the scene to be accessible.

Thoughts?

1 Like

Trapped in the same problem here and I’m also looking for the best solutions ~

I wrote another hack to EventDispatcher:

void EventDispatcher::sortEventListenersOfSceneGraphPriority(const EventListener::ListenerID& listenerID, Node* rootNode) 
{
   ...

visitTarget(rootNode, true);

// here my code:
for (auto l : *sceneGraphListeners){
	if (l->getAssociatedNode() && _nodePriorityMap.find(l->getAssociatedNode()) == _nodePriorityMap.end()){
		_nodePriorityMap[l->getAssociatedNode()] = 1000 +  l->getAssociatedNode()->getGlobalZOrder(); // fix touch on notificationNode
	}
}

...
}

also need to call NotificationNode onEnter