Cocos2d-x study notes (14.2) EventDispatcher event distribution mechanism dispatchEvent (event)

1. event distribution method EventDispatcher :: dispatchEvent (Event * event)

First, if the distribution is enabled by _isEnabled judgment flag event.

Execution updateDirtyFlagForSceneGraph (). Some node ID corresponding dirty flag is set.

For _inDispatch ++, the number of +1 event currently being distributed.

    DispatchGuard guard(_inDispatch);

Next is a judge, if a touch event, calls the touch of a dedicated distribution method, instead of this method.

    if (event->getType() == Event::Type::TOUCH)
    {
        dispatchTouchEvent(static_cast<EventTouch*>(event));
        return;
    }

Gets the parameters of the event ID as a listener ID.

    auto listenerID = __getListenerID(event);

The next event on the same sort of ID for all listeners.

    sortEventListeners (listenerID);

Is a type of judgment, if a mouse event, mouse-specific events defined distribution function pointer, otherwise, the definition of a common event distribution function pointer.

    auto pfnDispatchEventToListeners = &EventDispatcher::dispatchEventToListeners;
    if (event->getType() == Event::Type::MOUSE) {
        pfnDispatchEventToListeners = &EventDispatcher::dispatchTouchEventToListeners;
    }

Then the listener finds the corresponding ID from Vector _listenerMap the event parameter, which contains two storage containers listeners.

    auto iter = _listenerMap.find(listenerID);
    if (iter != _listenerMap.end())
    {
        auto listeners = iter->second;
        //...
    }

Define an anonymous function.

        auto onEvent = [&event](EventListener* listener) -> bool{
            event->setCurrentTarget(listener->getAssociatedNode());
            listener->_onEvent(event);
            return event->isStopped();
        };

For event distribution.

(this->*pfnDispatchEventToListeners)(listeners, onEvent);

Finally, the processing of all be added and deleted to be a listener.

updateListeners(event);

In short, the event distribution logic is that the parameter event, locate the event ID corresponding to the listener, but also to determine whether the ID before distributing the corresponding container listener need to reorder, the event was distributed to all the listeners of the same ID The callback function for processing.

Next, focus on some of the methods of learning.

2. updateDirtyFlagForSceneGraph()

When you call resumeEventListenersForTarget method, the node is associated with all listeners to recover from the suspended state, you need to join node _dirtyNodes.

This function is to _dirtyNodes is associated with the node has been suspended listener ID in _priorityDirtyFlagMap set the dirty flag SCENE_GRAPH_PRIORITY, after listening to these container ID reordering.

3. DispatchGuard guard(_inDispatch)

 It creates an object DispatchGuard class, _inDispatch as a constructor.

DispatchGuard(int& count):
            _count(count)
    {
        ++_count;
    }

    ~DispatchGuard()
    {
        --_count;
    }

It can be seen when one event for distribution, _inDispatch ++. At the end of the distribution method, the local objects will destructor, _inDispatch--. Very clever implementation of automatic management of _inDispatch.

4. sortEventListeners (listenerID)

Briefly, in determining each ID _priorityDirtyFlagMap the dirty flag, depending on the dirty flag of the container ID to determine which reordering.

The method first acquires the dirty flag to be sorted ID of the listener.

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

NONE flag is not dirty, the container needs to reorder instructions, then the dirty flag is set NONE first, the next start sort. When the dirty flag is NONE, because it has been sorted, the sort function execution is completed.

 if (dirtyFlag != DirtyFlag::NONE)
    {
        dirtyIter->second = DirtyFlag::NONE;
//...

Here bitwise operation and determines whether or not the sort of two container ID. According to the results by bit and may be re-ordered two containers, one container may only need to sort.

        if ((int)dirtyFlag & (int)DirtyFlag::FIXED_PRIORITY)
        {
            sortEventListenersOfFixedPriority(listenerID);
        }
        
        if ((int)dirtyFlag & (int)DirtyFlag::SCENE_GRAPH_PRIORITY)
        {
            auto rootNode = Director::getInstance()->getRunningScene();
            if (rootNode)
            {
                sortEventListenersOfSceneGraphPriority(listenerID, rootNode);
            }
            else
            {
                dirtyIter->second = DirtyFlag::SCENE_GRAPH_PRIORITY;
            }
        }

Two containers are sorted uses two methods:

            sortEventListenersOfFixedPriority (listenerID); 
            sortEventListenersOfSceneGraphPriority (listenerID, root node);

4.1 sortEventListenersOfFixedPriority (listenerID)

The method first acquires corresponding to the ID fixedListeners container.

    auto listeners = getListeners(listenerID);

    if (listeners == nullptr)
        return;
    
    auto fixedListeners = listeners->getFixedPriorityListeners();
    if (fixedListeners == nullptr)
        return;

Sort the container, from small to large priority order.

std::stable_sort(fixedListeners->begin(), fixedListeners->end(), [](const EventListener* l1, const EventListener* l2) {
        return l1->getFixedPriority() < l2->getFixedPriority();
    });

Containers of sorted from small to large to find, to find the first priority is not less than 0 listeners, to its index records as a member of the Vector _gt0Index.

    int index = 0;
    for (auto& listener : *fixedListeners)
    {
        if (listener->getFixedPriority() >= 0)
            break;
        ++index;
    }
    
    listeners->setGt0Index(index);

4.2 sortEventListenersOfSceneGraphPriority (listenerID, root node)

Parameters rootNode scene is currently running.

Like the above sort, which was first acquired container. Except that sceneGraphListeners container listeners priority are 0, sort the order of node required.

Priority needs _nodePriorityIndex record node of the container.

    _nodePriorityIndex = 0;
    _nodePriorityMap.clear();

    visitTarget(rootNode, true);

Methods visitTarget the calculated priority node and node stored in _nodePriorityMap. Next, the sort sceneGraphListeners, node Relevance each listener in accordance with the priority of _nodePriorityMap size, node priority large, the top ranked listener.

std::stable_sort(sceneGraphListeners->begin(), sceneGraphListeners->end(), [this](const EventListener* l1, const EventListener* l2) {
        return _nodePriorityMap[l1->getAssociatedNode()] > _nodePriorityMap[l2->getAssociatedNode()];
    });

4.3 visit target (root node, true)

Briefly, the computed node and node priority stored in _nodePriorityMap

The method sorts the child nodes of the node. LocalZOrder sorted by child nodes in ascending order, according to the same sequence added to the node (i.e., the same order).

node->sortAllChildren();

Container acquisition sub-node number.

    auto& children = node->getChildren();
    auto childrenCount = children.size();

For children carried in preorder traversal to the node and the node globalZOrder stored in _globalZOrderNodeMap. In this case, map each node in the container are by LocalZOrder node in ascending order.

 if(childrenCount > 0)
    {
        Node* child = nullptr;
        // visit children zOrder < 0
        for( ; i < childrenCount; i++ )
        {
            child = children.at(i);
            
            if ( child && child->getLocalZOrder() < 0 )
                visitTarget(child, false);
            else
                break;
        }
        
        if (_nodeListenersMap.find(node) != _nodeListenersMap.end())
        {
            _globalZOrderNodeMap[node->getGlobalZOrder()].push_back(node);
        }
        
        for( ; i < childrenCount; i++ )
        {
            child = children.at(i);
            if (child)
                visitTarget(child, false);
        }
    }
    else
    {
        if (_nodeListenersMap.find(node) != _nodeListenersMap.end())
        {
            _globalZOrderNodeMap[node->getGlobalZOrder()].push_back(node);
        }
    }

Scene node, the first node globalZOrder get all the scenarios, and globalZOrder from small to large.

Traversal _globalZOrderNodeMap, take each node. GlobalZOrder traverse the ascending order, the same order globalZOrder press (LocalZOrder small to large) traversal. Order traversal, will in turn add node to _nodePriorityMap. Priority order node in turn +1. That is, the higher the later drawing node priority.

   IF (isRootNode) 
    { 
        STD :: Vector < a float > globalZOrders; // store scene, all the node globalZOrder 
        globalZOrders.reserve (_globalZOrderNodeMap.size ()); 
        
        for ( const Auto & E: _globalZOrderNodeMap) 
        { 
            globalZOrders.push_back (e.first ); 
        } 
        
        STD :: stable_sort (globalZOrders.begin (), globalZOrders.end (), [] ( const  a float A, const  a float B) {
             return A < B; 
        }); // globalZOrder from small to large 
        
        for (const auto& globalZ : globalZOrders)
        {
            for (const auto& n : _globalZOrderNodeMap[globalZ])
            {
                _nodePriorityMap[n] = ++_nodePriorityIndex;
            }
        }
        
        _globalZOrderNodeMap.clear();
    }

5. event distribution dispatchEventToListeners (listeners, onEvent)

The function pointer pfnDispatchEventToListeners event ID is a mouse-type point to different functions.

Non-mouse dispatchEventToListeners example.

First, get the ID of the two listener container: fixedPriorityListeners sceneGraphPriorityListeners.

According to <= 00> 0 order priority, the following code is executed for each listener. fixedPriorityListeners gets the priority by getGt0Index () is greater than 0 listeners demarcation point number, classification.

                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
                {
                    shouldStopPropagation = true;
                    break;
                }

Here the anonymous call function onEvent (listener) previously defined.

6. anonymous function onEvent (listener)

node set of events associated _currentTarget as a listener, the listener callback function _onEvent (event) to handle the event.

        auto onEvent = [&event](EventListener* listener) -> bool{
            event->setCurrentTarget(listener->getAssociatedNode());
            listener->_onEvent(event);
            return event->isStopped();
        };

7. The finishing process updateListeners (event)

Delete all be deleted container listener. Add all containers of the listener to be added. Delete the Vector isRegistered is false listener. Delete empty elements _listenerMap in Vector.

Guess you like

Origin www.cnblogs.com/deepcho/p/cocos2dx-dispatch-event.html