cocos2d-x CCMenu詳細源碼分析

.h

typedef enum 
{
    kCCMenuStateWaiting, // 表示沒有菜單項被選中
    kCCMenuStateTrackingTouch // 表示有菜單項被選中
} tCCMenuState;
 
enum {
    //* priority used by the menu for the event handler
    kCCMenuHandlerPriority = -128,
};
 
class CC_DLL CCMenu : public CCLayerRGBA // CCMenu的基類居然是CCLayer!
{
    bool m_bEnabled; // CCMenu是否接收觸摸事件
     
public:
    CCMenu() : m_pSelectedItem(NULL) {}
    virtual ~CCMenu(){}
 
    // 創建一個空菜單
    static CCMenu* create();
 
    // 根據多個菜單項列創建菜單
    static CCMenu* create(CCMenuItem* item, ...);
 
    // 通過子菜單項的數組創建菜單
    static CCMenu* createWithArray(CCArray* pArrayOfItems);
 
    // 通過一個菜單項創建菜單
    static CCMenu* createWithItem(CCMenuItem* item);
     
    // 通過多個菜單項列創建菜單
    static CCMenu* createWithItems(CCMenuItem *firstItem, va_list args);
 
    // 初始化空菜單
    bool init();
 
    // 通過子菜單項數組初始化菜單
    bool initWithArray(CCArray* pArrayOfItems);
 
    // 垂直方向默認間隙排列
    void alignItemsVertically();
    //垂直方向指定間隙排列
    void alignItemsVerticallyWithPadding(float padding);
 
    // 水平方向默認間隙排列
    void alignItemsHorizontally();
    // 水平方向指定間隙排列
    void alignItemsHorizontallyWithPadding(float padding);
 
    // 通過每行個數排列
    void alignItemsInColumns(unsigned int columns, ...);
    void alignItemsInColumns(unsigned int columns, va_list args);
    // ?
    void alignItemsInColumnsWithArray(CCArray* rows);
 
    // 通過每列個數排列
    void alignItemsInRows(unsigned int rows, ...);
    void alignItemsInRows(unsigned int rows, va_list args);
    // ?
    void alignItemsInRowsWithArray(CCArray* columns);
 
    // 設置事件處理優先級,默認爲kCCMenuTouchPriority
    void setHandlerPriority(int newPriority);
 
    // 基類方法
    virtual void addChild(CCNode * child);
    virtual void addChild(CCNode * child, int zOrder);
    virtual void addChild(CCNode * child, int zOrder, int tag);
    virtual void registerWithTouchDispatcher();
    virtual void removeChild(CCNode* child, bool cleanup);
 
    // 觸摸事件
    virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
    virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
    virtual void ccTouchCancelled(CCTouch *touch, CCEvent* event);
    virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
 
    // 退出調用函數
    virtual void onExit();
 
    // ?
    virtual void setOpacityModifyRGB(bool bValue) {CC_UNUSED_PARAM(bValue);}
    virtual bool isOpacityModifyRGB(void) { return false;}
    // 獲取/設置菜單是否可用
    virtual bool isEnabled() { return m_bEnabled; }
    virtual void setEnabled(bool value) { m_bEnabled = value; };
 
protected:
    // 返回被觸摸的菜單項
    CCMenuItem* itemForTouch(CCTouch * touch);
    tCCMenuState m_eState;
    CCMenuItem *m_pSelectedItem;
};



.cpp  


static std::vector<unsigned int=""> ccarray_to_std_vector(CCArray* pArray) // 將CCArray轉換成Vector
{
    std::vector<unsigned int=""> ret;
    CCObject* pObj;
    CCARRAY_FOREACH(pArray, pObj)
    {
        CCInteger* pInteger = (CCInteger*)pObj;
        ret.push_back((unsigned int)pInteger->getValue());
    }
    return ret;
}
 
enum
{
    kDefaultPadding =  5,
};
 
//
//CCMenu
//
 
CCMenu* CCMenu::create()
{ // 傳空參數,創建空菜單
    return CCMenu::create(NULL, NULL);
}
 
CCMenu * CCMenu::create(CCMenuItem* item, ...)
{
    va_list args;
    va_start(args,item); //處理...形參
     
    CCMenu *pRet = CCMenu::createWithItems(item, args);
     
    va_end(args);
     
    return pRet;
}
 
CCMenu* CCMenu::createWithArray(CCArray* pArrayOfItems)
{
    CCMenu *pRet = new CCMenu();
    if (pRet && pRet->initWithArray(pArrayOfItems))
    {
        pRet->autorelease(); // 設置自動釋放
    }
    else
    {
        CC_SAFE_DELETE(pRet);
    }
     
    return pRet;
}
 
CCMenu* CCMenu::createWithItems(CCMenuItem* item, va_list args)
{
    CCArray* pArray = NULL; // 將...形參轉換成CCArray
    if( item )
    {
        pArray = CCArray::create(item, NULL); // 創建一個CCArray
        CCMenuItem *i = va_arg(args, CCMenuItem*);
        while(i)
        {
            pArray->addObject(i); // 添加其他子菜單項
            i = va_arg(args, CCMenuItem*);
        }
    }
     
    return CCMenu::createWithArray(pArray);
}
 
CCMenu* CCMenu::createWithItem(CCMenuItem* item)
{
    return CCMenu::create(item, NULL);
}
 
bool CCMenu::init()
{
    return initWithArray(NULL);
}
 
bool CCMenu::initWithArray(CCArray* pArrayOfItems)
{
    if (CCLayer::init())
    {
        // 設置觸摸相關信息
        setTouchPriority(kCCMenuHandlerPriority);
        setTouchMode(kCCTouchesOneByOne);
        setTouchEnabled(true);
 
        // 打開可用狀態
        m_bEnabled = true;
         
        // 菜單置於屏幕中間,並填充滿整個屏幕
        CCSize s = CCDirector::sharedDirector()->getWinSize();
        this->ignoreAnchorPointForPosition(true);
        setAnchorPoint(ccp(0.5f, 0.5f));
        this->setContentSize(s);
        setPosition(ccp(s.width/2, s.height/2));
         
        // 遍歷添加菜單項
        if (pArrayOfItems != NULL)
        {
            int z=0;
            CCObject* pObj = NULL;
            CCARRAY_FOREACH(pArrayOfItems, pObj)
            {
                CCMenuItem* item = (CCMenuItem*)pObj;
                this->addChild(item, z);
                z++;
            }
        }
     
        // 將選中項置爲空
        m_pSelectedItem = NULL;
        // 將狀態置爲沒有菜單項被選中
        m_eState = kCCMenuStateWaiting;
         
        // enable cascade color and opacity on menus
        setCascadeColorEnabled(true);
        setCascadeOpacityEnabled(true);
         
        return true;
    }
    return false;
}
 
/*
* override add:
*/
void CCMenu::addChild(CCNode * child)
{
    CCLayer::addChild(child);
}
 
void CCMenu::addChild(CCNode * child, int zOrder)
{
    CCLayer::addChild(child, zOrder);
}
 
void CCMenu::addChild(CCNode * child, int zOrder, int tag)
{ // 確保child爲CCMenuItem之內
    CCAssert( dynamic_cast<ccmenuitem*>(child) != NULL, Menu only supports MenuItem objects as children);
    CCLayer::addChild(child, zOrder, tag);
}
 
void CCMenu::onExit()
{
    if (m_eState == kCCMenuStateTrackingTouch) // 如果有菜單項被選中,則取消選擇狀態
    {
        if (m_pSelectedItem)
        { // 將選中項菜單選中狀態清除,並將用於記錄的變量置爲空
            m_pSelectedItem->unselected();
            m_pSelectedItem = NULL;
        }
        // 將狀態置爲等待觸摸
        m_eState = kCCMenuStateWaiting;
    }
 
    CCLayer::onExit();
}
 
void CCMenu::removeChild(CCNode* child, bool cleanup)
{
    CCMenuItem *pMenuItem = dynamic_cast<ccmenuitem*>(child);
    CCAssert(pMenuItem != NULL, Menu only supports MenuItem objects as children);
     
    // 如果是被選中的菜單項,則先把選中置爲空
    if (m_pSelectedItem == pMenuItem)
    {
        m_pSelectedItem = NULL;
    }
     
    CCNode::removeChild(child, cleanup);
}
 
//Menu - Events
 
void CCMenu::setHandlerPriority(int newPriority)
{
    CCTouchDispatcher* pDispatcher = CCDirector::sharedDirector()->getTouchDispatcher();
    pDispatcher->setPriority(newPriority, this); // 設置觸摸優先級
}
 
void CCMenu::registerWithTouchDispatcher()
{ // 註冊觸摸
    CCDirector* pDirector = CCDirector::sharedDirector();
    pDirector->getTouchDispatcher()->addTargetedDelegate(this, this->getTouchPriority(), true);
}
 
bool CCMenu::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
    CC_UNUSED_PARAM(event);
    if (m_eState != kCCMenuStateWaiting || ! m_bVisible || !m_bEnabled)
    { // 排除不執行觸摸事件狀態
        return false;
    }
 
    for (CCNode *c = this->m_pParent; c != NULL; c = c->getParent())
    { // 如果菜單父類不可見則返回
        if (c->isVisible() == false)
        {
            return false;
        }
    }
<span style="white-space: pre;">    </span>// 選出觸摸項
    m_pSelectedItem = this->itemForTouch(touch);
    if (m_pSelectedItem)
    {
        m_eState = kCCMenuStateTrackingTouch; // 將狀態變爲有菜單項被選中
        m_pSelectedItem->selected(); // 對觸摸項執行觸摸事件
        return true;
    }
    return false;
}
 
void CCMenu::ccTouchEnded(CCTouch *touch, CCEvent* event)
{
    CC_UNUSED_PARAM(touch); // 消除不使用警告
    CC_UNUSED_PARAM(event);
    CCAssert(m_eState == kCCMenuStateTrackingTouch, [Menu ccTouchEnded] -- invalid state);
    if (m_pSelectedItem)
    { // 解除菜單項選中狀態
        m_pSelectedItem->unselected();
        m_pSelectedItem->activate();
    }
    m_eState = kCCMenuStateWaiting;
}
 
void CCMenu::ccTouchCancelled(CCTouch *touch, CCEvent* event)
{
    CC_UNUSED_PARAM(touch);
    CC_UNUSED_PARAM(event);
    CCAssert(m_eState == kCCMenuStateTrackingTouch, [Menu ccTouchCancelled] -- invalid state);
    if (m_pSelectedItem)
    { // 解除菜單項選中狀態
        m_pSelectedItem->unselected();
    }
    m_eState = kCCMenuStateWaiting;
}
 
void CCMenu::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
    CC_UNUSED_PARAM(event);
    CCAssert(m_eState == kCCMenuStateTrackingTouch, [Menu ccTouchMoved] -- invalid state);
    CCMenuItem *currentItem = this->itemForTouch(touch); // 獲取當前觸摸的子菜單項
    if (currentItem != m_pSelectedItem) 
    {
        if (m_pSelectedItem) // 如果當前有選中項,先取消選中
        {
            m_pSelectedItem->unselected();
        }
        m_pSelectedItem = currentItem; // 重新設置選中項
        if (m_pSelectedItem)
        {
            m_pSelectedItem->selected();
        }
    }
}
 
//Menu - Alignment
// 垂直方向默認間隙排列
void CCMenu::alignItemsVertically()
{
    this->alignItemsVerticallyWithPadding(kDefaultPadding);
}
 
// 垂直方向指定間隙排列
void CCMenu::alignItemsVerticallyWithPadding(float padding)
{
    float height = -padding;  // 第一個子菜單沒有間隔,用於去掉這個重複
    if (m_pChildren && m_pChildren->count() > 0)
    {
        CCObject* pObject = NULL;
        CCARRAY_FOREACH(m_pChildren, pObject)
        {
            CCNode* pChild = dynamic_cast<ccnode*>(pObject);
            if (pChild)
            {
                height += pChild->getContentSize().height * pChild->getScaleY() + padding; // 獲取子菜單項和間隔的總高度
            }
        }
    }
 
    float y = height / 2.0f; // 因爲定位點在中間,所以先除去2
    if (m_pChildren && m_pChildren->count() > 0)
    {
        CCObject* pObject = NULL;
        CCARRAY_FOREACH(m_pChildren, pObject)
        {
            CCNode* pChild = dynamic_cast<ccnode*>(pObject);
            if (pChild)
            {
                pChild->setPosition(ccp(0, y - pChild->getContentSize().height * pChild->getScaleY() / 2.0f)); // 錨點在中間,所以除2
                y -= pChild->getContentSize().height * pChild->getScaleY() + padding; // 減去一個子菜單項和間隔的高度
            }
        }
    }
}
 
// 水平方向默認間隙排列
void CCMenu::alignItemsHorizontally(void)
{
    this->alignItemsHorizontallyWithPadding(kDefaultPadding);
}
 
// 水平方向指定間隙排列
void CCMenu::alignItemsHorizontallyWithPadding(float padding) // 原理和垂直排列相似,只不過將x變成y
{
 
    float width = -padding;
    if (m_pChildren && m_pChildren->count() > 0)
    {
        CCObject* pObject = NULL;
        CCARRAY_FOREACH(m_pChildren, pObject)
        {
            CCNode* pChild = dynamic_cast<ccnode*>(pObject);
            if (pChild)
            {
                width += pChild->getContentSize().width * pChild->getScaleX() + padding;
            }
        }
    }
 
    float x = -width / 2.0f;
    if (m_pChildren && m_pChildren->count() > 0)
    {
        CCObject* pObject = NULL;
        CCARRAY_FOREACH(m_pChildren, pObject)
        {
            CCNode* pChild = dynamic_cast<ccnode*>(pObject);
            if (pChild)
            {
                pChild->setPosition(ccp(x + pChild->getContentSize().width * pChild->getScaleX() / 2.0f, 0));
                 x += pChild->getContentSize().width * pChild->getScaleX() + padding;
            }
        }
    }
}
 
void CCMenu::alignItemsInColumns(unsigned int columns, ...)
{ // 將...轉換成va_list
    va_list args;
    va_start(args, columns);
 
    this->alignItemsInColumns(columns, args);
 
    va_end(args);
}
 
void CCMenu::alignItemsInColumns(unsigned int columns, va_list args)
{ // 將va_list轉換成CCArray
    CCArray* rows = CCArray::create();
    while (columns)
    {
        rows->addObject(CCInteger::create(columns));
        columns = va_arg(args, unsigned int);
    }
    alignItemsInColumnsWithArray(rows);
}
 
void CCMenu::alignItemsInColumnsWithArray(CCArray* rowsArray)
{
    vector<unsigned int=""> rows = ccarray_to_std_vector(rowsArray); // 將CCArray轉換成vector
 
    int height = -5; // 爲-5是用於除去第一行的間隙
    unsigned int row = 0; // 索引,當前行序號
    unsigned int rowHeight = 0; // 放置每一行高度(即每行最大高度)
    unsigned int columnsOccupied = 0; // 已放置列數
    unsigned int rowColumns; // 放置該行有多少列
 
    if (m_pChildren && m_pChildren->count() > 0) // 子菜單項數組存在且個數不爲0
    {
        CCObject* pObject = NULL; // 臨時變量
        CCARRAY_FOREACH(m_pChildren, pObject) // 遍歷子菜單項數組
        {
            CCNode* pChild = dynamic_cast<ccnode*>(pObject); // 將CCObject對象強制轉換成CCNode對象
            if (pChild) // 如果強制轉換成功
            {
                CCAssert(row < rows.size(), );
 
                rowColumns = rows[row]; // 獲取該行列數
                CCAssert(rowColumns, );
 
                float tmp = pChild->getContentSize().height; // 獲取當前子菜單高度
                rowHeight = (unsigned int)((rowHeight >= tmp || isnan(tmp)) ? rowHeight : tmp); // 如果行高大於當前子菜單項高度,或者當前子菜單項高度越界,rowHeight不變,否則rowHeight等於當前子菜單項高度(用來存放最大菜單項高度)
 
                ++columnsOccupied; // 已放置列數自加
                if (columnsOccupied >= rowColumns) // 如果已放置列數大於等於該行可容納列數
                {
                    height += rowHeight + 5; // 高度加上當前高度加上間隙
                    columnsOccupied = 0; // 重置已放置列數
                    rowHeight = 0; // 重置每行行高的臨時變量
                    ++row; // 跳轉至下一行
                }
            }
        }
    }    
 
    // check if too many rows/columns for available menu items
    CCAssert(! columnsOccupied, );
 
    CCSize winSize = CCDirector::sharedDirector()->getWinSize(); // 獲取屏幕大小
 
    row = 0;
    rowHeight = 0;
    rowColumns = 0;
    float w = 0.0;
    float x = 0.0;
    float y = (float)(height / 2);
 
    if (m_pChildren && m_pChildren->count() > 0)
    {
        CCObject* pObject = NULL;
        CCARRAY_FOREACH(m_pChildren, pObject)
        {
            CCNode* pChild = dynamic_cast<ccnode>(pObject);
            if (pChild)
            {
                if (rowColumns == 0) // 如果需要放置數量爲0才重新讀取數據
                {
                    rowColumns = rows[row]; // 獲取需要放置的數量
                    w = winSize.width / (1 + rowColumns); //佔屏幕寬度大小平分
                    x = w;
                }
 
                float tmp = pChild->getContentSize().height;
                rowHeight = (unsigned int)((rowHeight >= tmp || isnan(tmp)) ? rowHeight : tmp); // 獲取當前行高度最大高度
                pChild->setPosition(ccp(x - winSize.width / 2,
                                       y - pChild->getContentSize().height / 2)); // 重新放置位置
 
                x += w;
                ++columnsOccupied;
 
                if (columnsOccupied >= rowColumns) // 如果已放置列數大於等於需要放置的列數
                {
                    y -= rowHeight + 5; // 減去一個子菜單項和間隔的高度
 
                    columnsOccupied = 0;
                    rowColumns = 0;
                    rowHeight = 0;
                    ++row;
                }
            }
        }
    }    
}
 
//alignItemsInRows系列方法參照alignItemsInColumns
void CCMenu::alignItemsInRows(unsigned int rows, ...)
{
    va_list args;
    va_start(args, rows);
 
    this->alignItemsInRows(rows, args);
 
    va_end(args);
}
 
void CCMenu::alignItemsInRows(unsigned int rows, va_list args)
{
    CCArray* pArray = CCArray::create();
    while (rows)
    {
        pArray->addObject(CCInteger::create(rows));
        rows = va_arg(args, unsigned int);
    }
    alignItemsInRowsWithArray(pArray);
}
 
void CCMenu::alignItemsInRowsWithArray(CCArray* columnArray)
{
    vector<unsigned int=""> columns = ccarray_to_std_vector(columnArray);
 
    vector<unsigned int=""> columnWidths;
    vector<unsigned int=""> columnHeights;
 
    int width = -10;
    int columnHeight = -5;
    unsigned int column = 0;
    unsigned int columnWidth = 0;
    unsigned int rowsOccupied = 0;
    unsigned int columnRows;
 
    if (m_pChildren && m_pChildren->count() > 0)
    {
        CCObject* pObject = NULL;
        CCARRAY_FOREACH(m_pChildren, pObject)
        {
            CCNode* pChild = dynamic_cast<ccnode>(pObject);
            if (pChild)
            {
                // check if too many menu items for the amount of rows/columns
                CCAssert(column < columns.size(), );
 
                columnRows = columns[column];
                // can't have zero rows on a column
                CCAssert(columnRows, );
 
                // columnWidth = fmaxf(columnWidth, [item contentSize].width);
                float tmp = pChild->getContentSize().width;
                columnWidth = (unsigned int)((columnWidth >= tmp || isnan(tmp)) ? columnWidth : tmp);
 
                columnHeight += (int)(pChild->getContentSize().height + 5);
                ++rowsOccupied;
 
                if (rowsOccupied >= columnRows)
                {
                    columnWidths.push_back(columnWidth);
                    columnHeights.push_back(columnHeight);
                    width += columnWidth + 10;
 
                    rowsOccupied = 0;
                    columnWidth = 0;
                    columnHeight = -5;
                    ++column;
                }
            }
        }
    }
 
    // check if too many rows/columns for available menu items.
    CCAssert(! rowsOccupied, );
 
    CCSize winSize = CCDirector::sharedDirector()->getWinSize();
 
    column = 0;
    columnWidth = 0;
    columnRows = 0;
    float x = (float)(-width / 2);
    float y = 0.0;
 
    if (m_pChildren && m_pChildren->count() > 0)
    {
        CCObject* pObject = NULL;
        CCARRAY_FOREACH(m_pChildren, pObject)
        {
            CCNode* pChild = dynamic_cast<ccnode>(pObject);
            if (pChild)
            {
                if (columnRows == 0)
                {
                    columnRows = columns[column];
                    y = (float) columnHeights[column];
                }
 
                // columnWidth = fmaxf(columnWidth, [item contentSize].width);
                float tmp = pChild->getContentSize().width;
                columnWidth = (unsigned int)((columnWidth >= tmp || isnan(tmp)) ? columnWidth : tmp);
 
                pChild->setPosition(ccp(x + columnWidths[column] / 2,
                                       y - winSize.height / 2));
 
                y -= pChild->getContentSize().height + 10;
                ++rowsOccupied;
 
                if (rowsOccupied >= columnRows)
                {
                    x += columnWidth + 5;
                    rowsOccupied = 0;
                    columnRows = 0;
                    columnWidth = 0;
                    ++column;
                }
            }
        }
    }
}
 
CCMenuItem* CCMenu::itemForTouch(CCTouch *touch){
    CCPoint touchLocation = touch->getLocation(); // 獲取當前觸摸點
    if (m_pChildren && m_pChildren->count() > 0) // 判斷子節點(子菜單項)是否存在,且個數不爲0
    {
        CCObject* pObject = NULL; // 臨時變量
        CCARRAY_FOREACH(m_pChildren, pObject) // 遍歷子菜單項數組
        {
            CCMenuItem* pChild = dynamic_cast<ccmenuitem>(pObject); // 強制轉換,獲取當前子菜單項
            if (pChild && pChild->isVisible() && pChild->isEnabled()) // 如果當前子菜單項不爲空、且可見、且可用
            {
                CCPoint local = pChild->convertToNodeSpace(touchLocation); // 把世界座標轉換到當前節點的本地座標系中
                CCRect r = pChild->rect(); // 獲取子菜單項範圍
                r.origin = CCPointZero;
                if (r.containsPoint(local)) // 判斷子菜單項範圍中是否包括觸摸點
                {
                    return pChild; // 返回該子菜單項
                }
            }
        }
    }
    return NULL; // 返回空
}


看懂了。就是知識



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章