2013年7月19日金曜日

cocos2dxでModalなAlertView(Dialog)

coco2dxでAlertViewのような仕組みが見つからなかった。CCMessageBoxというのがあるがあくまで簡易的なものでボタンを2つは置けないようで。
[Cocos2d-x] モーダルレイヤを作成するを参考にして作ってみた。細かいところは未検証。とりあえず動くかなという状態。


FSAlertLayer.cpp

#include "FSAlertLayer.h"

USING_NS_CC;

// ceate with 2 buttons
FSAlertLayer* FSAlertLayer::create(const char* message, CCObject *target, SEL_CallFuncN okSelector, SEL_CallFuncN ngSelector)
{
    FSAlertLayer* layer = FSAlertLayer::create();
    layer->setStyle(message, target, okSelector, ngSelector);
    return layer;
}


// create with only 1 button
FSAlertLayer* FSAlertLayer::create(const char *message, cocos2d::CCObject *target, cocos2d::SEL_CallFuncN okSelector)
{
    return FSAlertLayer::create(message, target, okSelector, NULL);
}


bool FSAlertLayer::init()
{
    // half clear black bgcolor
    if (! CCLayerColor::initWithColor(ccc4(0, 0, 0, 100))) {
        return false;
    }
    
    CCDirector* pDirector = CCDirector::sharedDirector();
    pDirector->getTouchDispatcher()->addTargetedDelegate(this, kCCMenuHandlerPriority, true);
    
    CCSize vsize = pDirector->getVisibleSize();
    
    // bg
    CCSprite* frame = CCSprite::create("bg_alert.png");
    frame->setTag(FSAlertLayerTagFrame);
    frame->setPosition(ccp(vsize.width / 2, vsize.height / 2));
    this->addChild(frame);
    
    return true;
}


void FSAlertLayer::setStyle(const char* message, cocos2d::CCObject *target, cocos2d::SEL_CallFuncN okSelector, cocos2d::SEL_CallFuncN ngSelector)
{
    this->target        = target;
    this->okSelector    = okSelector;
    this->ngSelector    = ngSelector;
    
    CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
    
    // message
    CCLabelTTF *pMsg = CCLabelTTF::create(message, "", 16);
    pMsg->setPosition(ccp(visibleSize.width / 2, visibleSize.height / 2));
    this->addChild(pMsg);
    
    // OK Button
    CCMenuItemImage *pOKItem = CCMenuItemImage::create("btn_ok.png", "btn_ok.png", this, menu_selector(FSAlertLayer::menuAlertOK));
    
    int btnHeight = visibleSize.height / 2 - this->getChildByTag(FSAlertLayerTagFrame)->getContentSize().height / 2 + pOKItem->getContentSize().height / 2 + 10;
    
    pOKItem->setTag(FSAlertLayerTagOK);
    pOKItem->setPosition(ccp(visibleSize.width / 2, btnHeight));
    
    CCMenu* pMenu = CCMenu::create(pOKItem, NULL);
    pMenu->setPosition(CCPointZero);
    pMenu->setTag(FSAlertLayerTagMenu);
    this->addChild(pMenu);
    
    // set as 2 buttons
    if (ngSelector) {
        pOKItem->setPosition(ccp(visibleSize.width / 2 - pOKItem->getContentSize().width / 2, btnHeight));
        
        // NG btn
        CCMenuItemImage* pNGItem = CCMenuItemImage::create("btn_back.png", "btn_back.png", this, menu_selector(FSAlertLayer::menuAlertClose));
        pNGItem->setTag(FSAlertLayerTagNG);
        pNGItem->setPosition(ccp(visibleSize.width / 2 + pOKItem->getContentSize().width / 2, btnHeight));
        pMenu->addChild(pNGItem);
    }
}


// touch event disable other layers
bool FSAlertLayer::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
    return true;
}


void FSAlertLayer::menuAlertOK(CCObject* pSender)
{
    CCLOG("OK");
    
    this->menuAlertClose(pSender);
    
    if (this->target && this->okSelector) {
        (this->target->*this->okSelector)(this);
    }
}


void FSAlertLayer::menuAlertClose(cocos2d::CCObject *pSender)
{
    this->removeFromParentAndCleanup(true);
    
    CCDirector* pDirector = CCDirector::sharedDirector();
    pDirector->getTouchDispatcher()->removeDelegate(this);
    
    if (((CCNode*)pSender)->getTag() == FSAlertLayerTagNG && this->target && this->ngSelector) {
        (this->target->*this->ngSelector)(this);
    }
}

FSAlertLayer.h

#ifndef __xxx__FSAlertLayer__
#define __xxx__FSAlertLayer__

#include "cocos2d.h"

#define FSAlertLayerTagMenu     101
#define FSAlertLayerTagOK       102
#define FSAlertLayerTagNG       103
#define FSAlertLayerTagFrame    110


class FSAlertLayer : public cocos2d::CCLayerColor
{
public:
    static FSAlertLayer* create(const char* message, CCObject* target, cocos2d::SEL_CallFuncN okSelector, cocos2d::SEL_CallFuncN ngSelector);
    static FSAlertLayer* create(const char* message, CCObject* target, cocos2d::SEL_CallFuncN okSelector);
    
    virtual bool init();
    void setStyle(const char* message, CCObject* target, cocos2d::SEL_CallFuncN okSelector, cocos2d::SEL_CallFuncN ngSelector);
    virtual bool ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
    
    void menuAlertOK(CCObject* pSender);
    void menuAlertClose(CCObject* pSender);
    
    CREATE_FUNC(FSAlertLayer);
    
private:
    cocos2d::CCObject* target;
    cocos2d::SEL_CallFuncN okSelector;
    cocos2d::SEL_CallFuncN ngSelector;
};

#endif /* defined(__xxx__FSAlertLayer__) */


利用側

// call from Scene, Layer, etc
FSAlertLayer *alertLayer = FSAlertLayer::create("XXXを使用します。よろしいですか", this, callfuncN_selector(ThisScene::menuOKCallback), callfuncN_selector(ThisScene::menuNGCallback));
// 適当に高い数値を入れて最上部に表示させる
this->addChild(alertLayer, 100001);