CCButton - not use CCMenu

I refer to http://www.cocos2d-x.org/boards/6/topics/1090?r=7731
and then make CCButton to be more similar to UIButton

here is some example

1:

CCButton * tmpBtn = CCButton::buttonWithFiles(this,menu_selector(HelloWorld::menuCloseCallback), "shop_btn.png", "shop_btn_click.png", NULL);
tmpBtn->setTitleForState("Show", CCControlStateNormal);
this->addChild(tmpBtn);
tmpBtn->setPosition(ccp(50,50));

2:

    CCSprite * tmpBgSprite = CCSprite::spriteWithFile("shop_btn.png");
    CCSprite * tmpBgSprite2 = CCSprite::spriteWithFile("shop_btn_click.png");

    CCButton * tmpBtn = CCButton::buttonWithFiles(this,menu_selector(HelloWorld::menuCloseCallback), NULL,NULL, NULL);
    tmpBtn->setBackgroundImageForState(tmpBgSprite, CCControlStateNormal);
    tmpBtn->setBackgroundImageForState(tmpBgSprite2, CCControlStateSelected);

    tmpBtn->setTitleForState("Show", CCControlStateNormal);

    this->addChild(tmpBtn);
    tmpBtn->setPosition(ccp(50,50));

3:

    CCButton * tmpBtn = CCButton::buttonWithFiles(this,menu_selector(HelloWorld::menuCloseCallback), NULL,NULL, NULL);
    tmpBtn->setTitleForState("Show", CCControlStateNormal);
    this->addChild(tmpBtn);
    tmpBtn->setPosition(ccp(50,50));

4:

CCButton * tmpBtn = CCButton::buttonWithFiles(this,menu_selector(HelloWorld::menuCloseCallback), "shop_btn.png", "shop_btn_click.png", NULL);
this->addChild(tmpBtn);
tmpBtn->setPosition(ccp(50,50)); 

here is the header file

#pragma once
#include "cocos2d.h"
#define TAP_MAX_DRAG 10

using namespace cocos2d;


enum {
    CCControlStateNormal       = 0,
    CCControlStateSelected     = 1 << 0,                  // flag usable by app (see below)
    CCControlStateDisabled     = 1 << 1,
    CCControlStateHighlighted  = 1 << 2                 // todo
};
typedef unsigned int CCControlState;


class CCButton : public CCNode, public CCTargetedTouchDelegate
{
private:
    bool initWithFile(const char* filename, SelectorProtocol* target, SEL_MenuHandler selector);
    SelectorProtocol* target;
    SEL_MenuHandler selector;
    bool touched;
    CCPoint touchPoint;

    bool isDisabled;

    CCControlState curState;

    CCMutableDictionary * backgroundNodes; //zOrder is 0
    CCMutableDictionary * touchNodes;      //zOrder is 1
    CCMutableDictionary * titleCCStrings;  //zOrder is 2

    void setState(CCControlState mState);

public:
    CCButton();
    ~CCButton();

    void initWithFile(SelectorProtocol* target, SEL_MenuHandler selector,const char * mNormal,const char * mSelected,const char * mDisabled=NULL);
    static CCButton * buttonWithFiles(SelectorProtocol* target, SEL_MenuHandler selector,const char * mNormal,const char * mSelected,const char * mDisabled=NULL);


    void setTitleForState(const char * mTitle,CCControlState mState);
    const char* titleForState(CCControlState mState);

    void setDisable(bool mIsDisabled);

    //todo
    virtual void setTitleColorForState(ccColor3B mColor,CCControlState mState){CC_UNUSED_PARAM(mColor);CC_UNUSED_PARAM(mState);};
    virtual ccColor3B titleColorForState(CCControlState mState){CC_UNUSED_PARAM(mState);return ccYELLOW;};

    void setImageForState(CCNode* mNode,CCControlState mState);
    CCNode * imageForState(CCControlState mState);
    void setBackgroundImageForState(CCNode *mNode,CCControlState mState);// default is NULL
    CCNode * backgroundImageForState(CCControlState mState);

    // touch events
    CCRect rect();
    bool containsTouchLocation(CCTouch* touch);
    virtual void onEnter();
    virtual void onExit();
    virtual bool ccTouchBegan(CCTouch* pTouch, CCEvent* pEvent);
    virtual void ccTouchMoved(CCTouch* pTouch, CCEvent* pEvent);
    virtual void ccTouchEnded(CCTouch* pTouch, CCEvent* pEvent);
    virtual void ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent);
    virtual void touchDelegateRetain();
    virtual void touchDelegateRelease();
};


CCButton.cpp.zip (2.0 KB)

Great extension. I’m considering to create a new sub-forum for extension discussion….

For the latest stable version, should we change SelectorProtocol to CCObject?

yes, you can change SelectorProtocol to CCObject.
i had test it for the latest stable version

Hey man,
awesome piece of work. Thanks a lot!

Only pity is that you use “using namespace cocos2d” in a header file. This is something you shouldn’t do, as each file which will include CCButton will include using namespace cocos2d and all following files as well. Second, it would be cool if you could wrap extensions in your own namespace like cocos2d_ext or something. Than its easier to distinguish!

Besides that… Great job!

Hello.

We have updated CCButton to cocos2d-x 0.13

CCButton.h

#pragma once
#include "cocos2d.h" 
#define TAP_MAX_DRAG 10

enum {
    CCControlStateNormal       = 0,
    CCControlStateSelected     = 1 << 0,                  // flag usable by app (see below)
    CCControlStateDisabled     = 1 << 1,
    CCControlStateHighlighted  = 1 << 2                 // todo
};
typedef unsigned int CCControlState;

class CCButton : public cocos2d::CCNode, public cocos2d::CCTargetedTouchDelegate
{
private:
    bool initWithFile(const char* filename, cocos2d::CCObject* target, cocos2d::SEL_MenuHandler selector);
    cocos2d::CCObject* target;
    cocos2d::SEL_MenuHandler selector;
    bool touched;
    cocos2d::CCPoint touchPoint;

    bool isDisabled;

    CCControlState curState;

    cocos2d::CCMutableDictionary * backgroundNodes; //zOrder is 0
    cocos2d::CCMutableDictionary * touchNodes;        //zOrder is 1
    cocos2d::CCMutableDictionary * titleCCStrings;  //zOrder is 2

    void setState(CCControlState mState);

public:
    CCButton();
    ~CCButton();

    void initWithFile(cocos2d::CCObject* target, cocos2d::SEL_MenuHandler selector,const char * mNormal,const char * mSelected,const char * mDisabled=NULL);
    static CCButton * buttonWithFiles(cocos2d::CCObject* target, cocos2d::SEL_MenuHandler selector,const char * mNormal,const char * mSelected,const char * mDisabled=NULL);

    void setTitleForState(const char * mTitle,CCControlState mState);
    const char* titleForState(CCControlState mState);

    void setDisable(bool mIsDisabled);

    //todo
    virtual void setTitleColorForState(cocos2d::ccColor3B mColor,CCControlState mState){CC_UNUSED_PARAM(mColor);CC_UNUSED_PARAM(mState);};
    virtual cocos2d::ccColor3B titleColorForState(CCControlState mState){CC_UNUSED_PARAM(mState);return cocos2d::ccYELLOW;};

    void setImageForState(CCNode* mNode,CCControlState mState);
    CCNode * imageForState(CCControlState mState);
    void setBackgroundImageForState(CCNode *mNode,CCControlState mState);// default is NULL
    CCNode * backgroundImageForState(CCControlState mState);

    // touch events
    cocos2d::CCRect rect();
    bool containsTouchLocation(cocos2d::CCTouch* touch);
    virtual void onEnter();
    virtual void onExit();
    virtual bool ccTouchBegan(cocos2d::CCTouch* pTouch, cocos2d::CCEvent* pEvent);
    virtual void ccTouchMoved(cocos2d::CCTouch* pTouch, cocos2d::CCEvent* pEvent);
    virtual void ccTouchEnded(cocos2d::CCTouch* pTouch, cocos2d::CCEvent* pEvent);
    virtual void ccTouchCancelled(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
    virtual void touchDelegateRetain();
    virtual void touchDelegateRelease();
};

CCButton.cpp

#include "CCButton.h"


#define CCButtonZorder0 0
#define CCButtonZorder1 1
#define CCButtonZorder2 2

#define CCButtonBgTag  1000001
#define CCButtonTouchedTag  1000002
#define CCButtonTitleTag 1000003

using namespace cocos2d;

CCButton::CCButton():CCNode()
{
    backgroundNodes = new CCMutableDictionary();
    touchNodes = new CCMutableDictionary();
    titleCCStrings = new CCMutableDictionary();
    isDisabled = false;
}

CCButton::~CCButton()
{
    CC_SAFE_DELETE(backgroundNodes);
    CC_SAFE_DELETE(touchNodes);
    CC_SAFE_DELETE(titleCCStrings);
}

void CCButton::initWithFile(CCObject* target, SEL_MenuHandler selector,const char * mNormal,const char * mSelected,const char * mDisabled)
{
    this->target = target;
    this->selector = selector;

    if (mNormal != NULL) {
        CCSprite * mNormalSprite = CCSprite::spriteWithFile(mNormal);
        mNormalSprite->setTag(CCButtonTouchedTag);
        this->addChild(mNormalSprite, CCButtonZorder1);
        touchNodes->setObject(mNormalSprite,CCControlStateNormal);

        //set the content size.
        CCSize mContentSize = mNormalSprite->getContentSize();
        this->setContentSize(mContentSize);
    }

    if (mSelected != NULL) {
        CCSprite * mSelectedSprite = CCSprite::spriteWithFile(mSelected);
        mSelectedSprite->setTag(CCButtonTouchedTag);
        touchNodes->setObject(mSelectedSprite,CCControlStateSelected);
    }

    if (mDisabled != NULL) {
        CCSprite * mDisabledSprite = CCSprite::spriteWithFile(mDisabled);
        mDisabledSprite->setTag(CCButtonTouchedTag);
        touchNodes->setObject(mDisabledSprite,CCControlStateDisabled);
    }

    touched = false;
}

 CCButton * CCButton::buttonWithFiles(CCObject* target, SEL_MenuHandler selector,const char * mNormal,const char * mSelected,const char * mDisabled)
{
    CCButton *pRet = new CCButton();
    if (pRet)
    {
        pRet->initWithFile(target, selector,mNormal,mSelected,mDisabled);
        pRet->autorelease();
        return pRet;
    }
    CC_SAFE_DELETE(pRet)
    return NULL;
}

void CCButton::setState(CCControlState mState)
{
    ////set background node
    CCNode * bgNode = backgroundImageForState(mState);
    if (bgNode != NULL) {
        bgNode->setTag(CCButtonBgTag);
        CCNode * mTepNode = this->getChildByTag(CCButtonBgTag);
        if (mTepNode != NULL) {
            mTepNode->removeFromParentAndCleanup(true);
        }
        this->addChild(bgNode, CCButtonZorder0);
    }

    //set touch node
    CCNode * stateNode = imageForState(mState);
    if (stateNode != NULL) {
        stateNode->setTag(CCButtonTouchedTag);
        CCNode * mTepNode = this->getChildByTag(CCButtonTouchedTag);
        if (mTepNode != NULL) {
            mTepNode->removeFromParentAndCleanup(true);
        }
        this->addChild(stateNode, CCButtonZorder1);
    }

    //set title node
    const char*  titleForState = this->titleForState(mState);
    if (titleForState != NULL) {
        this->setTitleForState(titleForState,mState);
    }
}

void CCButton::setDisable(bool mIsDisabled)
{
    this->isDisabled = mIsDisabled;
    if (mIsDisabled == true) {
        setState(CCControlStateDisabled);
    }
    else {
        setState(CCControlStateNormal);
    }
}


void CCButton::setTitleForState(const char * mTitle,CCControlState mState)
{
    if (mTitle == NULL) {
        return;
    }

    CCLabelTTF * mTepNode = (CCLabelTTF *) (this->getChildByTag(CCButtonTitleTag));
    if (mTepNode != NULL) {
        mTepNode->setString(mTitle);
    }
    else {
        CCSize mContentSize = this->getContentSize();
        mTepNode = CCLabelTTF::labelWithString(mTitle, "Arial", 14);
        mTepNode->setTag(CCButtonTitleTag);
        this->setPosition(ccp(mContentSize.width/2.0,mContentSize.height/2.0));
        this->addChild(mTepNode, CCButtonZorder2);
    }

    CCSize mContentSize = mTepNode->getContentSize();
    mContentSize.width += 8;
    mContentSize.height += 6;
    this->setContentSize(mContentSize);

    CCString * mTepNodeInList = (CCString *) (this->titleCCStrings->objectForKey((int)mState));
    if (mTepNodeInList != NULL) {
        std::string mTepString = mTepNodeInList->toStdString();
        std::string mTepCompareString(mTitle);
        if (mTepString.compare(mTepCompareString) == 0) {
            return;
        }
        this->titleCCStrings->removeObjectForKey((int)mState);
    }

    //save string
    CCString * mNormalCCString = new CCString(mTitle);
    mNormalCCString->autorelease();
    this->titleCCStrings->setObject(mNormalCCString,mState);
}

const char * CCButton::titleForState(CCControlState mState)
{
    CCString * mTepNodeInList = (CCString *) (this->titleCCStrings->objectForKey((int)mState));
    if (mTepNodeInList != NULL) {
        return mTepNodeInList->toStdString().c_str();
    }
    return NULL;
}

/*
void CCButton::setTitleColorForState(ccColor3B mColor,CCControlState mState)
{
    CC_UNUSED_PARAM(mColor);
    CC_UNUSED_PARAM(mState);
}

ccColor3B CCButton::titleColorForState(CCControlState mState)
{
    CC_UNUSED_PARAM(mState);
}
 */

void CCButton::setImageForState(CCNode* mNode,CCControlState mState)
{
    if (mNode == NULL) {
        return;
    }

    CCSize mContentSize = mNode->getContentSize();
    this->setContentSize(mContentSize);

    CCNode * mTepNodeInList = (CCNode *) (this->touchNodes->objectForKey((int)mState));
    if (mTepNodeInList != NULL) {
        this->touchNodes->removeObjectForKey((int)mState);
    }

    this->touchNodes->setObject(mNode,(int)mState);

    if (mState == CCControlStateNormal) {
        this->setState(CCControlStateNormal);
    }
}

CCNode * CCButton::imageForState(CCControlState mState)
{
    if (this->touchNodes->count() == 0) {
        return NULL;
    }

    CCNode * mTepNodeInList = (CCNode *) (this->touchNodes->objectForKey((int)mState));
    if (mTepNodeInList != NULL) {
        return mTepNodeInList;
    }
    return NULL;
}

void CCButton::setBackgroundImageForState(CCNode *mNode,CCControlState mState)// default is nil
{
    if (mNode == NULL) {
        return;
    }

    CCSize mContentSize = mNode->getContentSize();
    this->setContentSize(mContentSize);

    CCNode * mTepNodeInList = (CCNode *) (this->backgroundNodes->objectForKey((int)mState));
    if (mTepNodeInList != NULL) {
        this->backgroundNodes->removeObjectForKey((int)mState);
    }

    this->backgroundNodes->setObject(mNode,(int)mState);

    if (mState == CCControlStateNormal) {
        this->setState(CCControlStateNormal);
    }
}

CCNode * CCButton::backgroundImageForState(CCControlState mState)
{
    if (this->backgroundNodes->count() == 0) {
        return NULL;
    }

    CCNode * mTepNodeInList = (CCNode *) (this->backgroundNodes->objectForKey((int)mState));
    if (mTepNodeInList != NULL) {
        return mTepNodeInList;
    }
    return NULL;
}

void CCButton::onEnter()
{
    CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, 0, false);
    CCNode::onEnter();
}

void CCButton::onExit()
{
    CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);
    CCNode::onExit();
}   

CCRect CCButton::rect()
{
    CCSize s = this->getContentSize();
    return CCRectMake(-s.width / 2, -s.height / 2, s.width, s.height);
}

bool CCButton::containsTouchLocation(CCTouch* touch)
{   
    return CCRect::CCRectContainsPoint(rect(), convertTouchToNodeSpaceAR(touch));
}

bool CCButton::ccTouchBegan(CCTouch* pTouch, CCEvent* pEvent)
{
    if (!this->isDisabled && containsTouchLocation(pTouch)) 
    {
        touched = true;
        touchPoint = pTouch->locationInView();
        this->setState(CCControlStateSelected);
        return true;
    }
    return false;
}

void CCButton::ccTouchMoved(CCTouch* pTouch, CCEvent* pEvent)
{   
    CCPoint currTouchPoint = pTouch->locationInView();
    // cancel touch if dragged too much
    if (ccpDistance(touchPoint,currTouchPoint) > TAP_MAX_DRAG)
    {
        this->setState(CCControlStateNormal);

        touched = false;
    }
}

void CCButton::ccTouchEnded(CCTouch* pTouch, CCEvent* pEvent)
{
    if (touched)
    {
        this->setState(CCControlStateNormal);

        //perform selector
        (target->*selector)(this);      
        touched = false;
    }
}

void CCButton::ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent)
{
    this->setState(CCControlStateNormal);

    touched = false;
}

void CCButton::touchDelegateRetain()
{
    this->retain();
}
void CCButton::touchDelegateRelease()
{
    this->release();
}

www.cookiebit.com

I updated the cocos2d-extensions with your CCButton component. There is a bug in win32, maybe you can solve it and submit a patch.

So, it’s better to use this CCButton instead of CCMenu?

@Jose Antonio Andujar Why your extensions aren’t merged to cocos repo? :frowning:

@Dawid Drozd

I haven’t updated the extensions a long time ago.

I’m sure they need a lot of work for cross-platform working, and I’m not sure that will be accepted for a pull request.