l

成都手游码农一枚
随笔 - 32, 文章 - 0, 评论 - 117, 引用 - 0
数据加载中……

[cocos2d-x] 分享一个EditBox 控件代码。


实现:直接通过CCIMEDelegate实现字符的录入,删除,支持中英文混合输入,支持多个 EditBox 焦点切换,支持自动调整输入框位置等等一些列,应该满足一般游戏的需求。
主要平台:WIN32,IOS, android未测试。
优点:可以很容易的将 EditBox 控件在 cocos2dx 中融合。
缺点:当然没有原生的功能强。


#ifndef EDITBOX_H_INCLUDED
#define EDITBOX_H_INCLUDED

namespace azl
{

class EditBox;
class EditBoxListener
{
public:
    
virtual void onEditBoxChange(yy::EditBox* editBox) = 0;
    
virtual void onEditBoxReturn(yy::EditBox* editBox) = 0;
}
;

class EditBox : public cocos2d::CCNode, public cocos2d::CCIMEDelegate, public cocos2d::CCTouchDelegate
{
public:
    
enum TYPE
    
{
        TYPE_NORMAL 
= 1,
        TYPE_PASSWORD 
= 2,
    }
;

public:
    EditBox();
    
~EditBox();

public:
    
virtual bool attachWithIME();
    
virtual bool detachWithIME();

public:
    
virtual bool canAttachWithIME() { return true; }
    
virtual void didAttachWithIME();
    
virtual bool canDetachWithIME() { return true; }
    
virtual void didDetachWithIME();

public:
    
virtual void insertText(const char * text, int len);
    
virtual void deleteBackward();
    
virtual const char * getContentText() 
    
{
        
return getString().c_str();
    }


public:
    
virtual void keyboardWillShow(cocos2d::CCIMEKeyboardNotificationInfo& info);
    
virtual void keyboardDidShow(cocos2d::CCIMEKeyboardNotificationInfo& info);
    
virtual void keyboardWillHide(cocos2d::CCIMEKeyboardNotificationInfo& info);
    
virtual void keyboardDidHide(cocos2d::CCIMEKeyboardNotificationInfo& info);

public:
    
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);

public:
    
virtual void setContentSize(const cocos2d::CCSize& contentSize)
    
{
        cocos2d::CCNode::setContentSize(contentSize);
        updateString();
    }


public:
    
void updateCursor(float dt);

    
void onEnter();
    
void onExit();
    
void visit();

public:
    
void setBackground(cocos2d::CCNode* background);
    cocos2d::CCNode
* getBackground()
    
{
        
return background_;
    }


    
void setLabel(cocos2d::CCLabelTTF* label);
    cocos2d::CCLabelTTF
* getLabel()
    
{
        
return contentLabel_;
    }


    
void setPlaceholderLabel(cocos2d::CCLabelTTF* label);
    cocos2d::CCLabelTTF
* getPlaceholderLabel()
    
{
        
return placeholderLabel_;
    }


    
void setString(const std::string& text);
    
const std::string& getString()
    
{
        
return contentText_;
    }


    
void setType(TYPE type);
    TYPE getType()
    
{
        
return type_;
    }


    
void setMaxChar(int maxChar);
    
int getMaxChar()
    
{
        
return maxChar_;
    }


    
void setEnabled(bool enable)
    
{
        enable_ 
= enable;
    }

    
bool isEnabled()
    
{
        
return enable_;
    }


    
void setIsNumber(bool number)
    
{
        isNumber_ 
= number;
    }

    
bool isNumber()
    
{
        
return isNumber_;
    }


    
void setListener(EditBoxListener* listener)
    
{
        listener_ 
= listener;
    }

    EditBoxListener
* getListener()
    
{
        
return listener_;
    }


public:
    
static EditBox* create(cocos2d::CCSize size, cocos2d::CCLabelTTF* label, 
        cocos2d::CCLabelTTF
* placeholderLabel = NULL, 
        cocos2d::CCNode
* background = NULL, 
        TYPE type 
= TYPE_NORMAL);

private:
    
void updateString();

private:
    cocos2d::CCNode
* background_;
    cocos2d::CCLabelTTF
* contentLabel_;
    cocos2d::CCLabelTTF
* placeholderLabel_;
    std::
string contentText_;
    TYPE type_;
    
int cursorState_;
    
int maxChar_;
    
bool enable_;
    
float adjustVert_;
    
bool isNumber_;

    EditBoxListener
* listener_;
    std::vector
<unsigned char> lengthStack_;
}
;

}


#endif

// CPP
#include "cocos2d.h"
#include 
"EditBox.h"
#include 
"AppDelegate.h"

using namespace cocos2d;

namespace
{

static const char _charMap[] = 
{
    
/* 00 */    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 
    
/* 10 */    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 
    
/* 20 */    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 
    
/* 30 */    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 
    
/* 40 */    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 
    
/* 50 */    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 
    
/* 60 */    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 
    
/* 70 */    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 
    
/* 80 */      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1, 
    
/* 90 */      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1, 
    
/* a0 */      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1, 
    
/* b0 */      -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1, 
    
/* c0 */      -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2, 
    
/* d0 */      -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2,   -2, 
    
/* e0 */      -3,   -3,   -3,   -3,   -3,   -3,   -3,   -3,   -3,   -3,   -3,   -3,   -3,   -3,   -3,   -3, 
    
/* f0 */      -4,   -4,   -4,   -4,   -4,   -4,   -4,   -4,   -5,   -5,   -5,   -5,   -6,   -6,   -1,   -1, 
}
;

}


namespace azl
{

static EditBox* g_currentEditBox_ = NULL;

EditBox::EditBox() 
    : background_(NULL)
    , contentLabel_(NULL)
    , placeholderLabel_(NULL)
    , type_(TYPE_NORMAL)
    , cursorState_(
-1)
    , maxChar_(
64)
    , enable_(
true)
    , adjustVert_(
0)
    , isNumber_(
false)
    , listener_(NULL)
{
    
}


EditBox::
~EditBox()
{

}


bool EditBox::attachWithIME()
{
    
bool bRet = CCIMEDelegate::attachWithIME();
    
if (bRet)
    
{
        
// open keyboard
        CCEGLView * pGlView = CCDirector::sharedDirector()->getOpenGLView();
        
if (pGlView)
        
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
            
if (!isNumber())
            
{
                pGlView
->setIMEKeyboardDefault();
                
            }

            
else
            
{
                pGlView
->setIMEKeyboardNumber();
                
            }

#endif
            
            pGlView
->setIMEKeyboardState(true);
        }

    }

    
return bRet;
}


bool EditBox::detachWithIME()
{
    
bool bRet = CCIMEDelegate::detachWithIME();
    
if (bRet)
    
{
        
// close keyboard
        CCEGLView * pGlView = CCDirector::sharedDirector()->getOpenGLView();
        
if (pGlView)
        
{
            pGlView
->setIMEKeyboardState(false);
        }

    }

    
return bRet;
}


void EditBox::didAttachWithIME()
{
    cursorState_ 
= 0;
    g_currentEditBox_ 
= this;
}


void EditBox::didDetachWithIME()
{
    cursorState_ 
= -1;
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
    g_currentEditBox_ 
= NULL;
#endif
}


void EditBox::insertText( const char * text, int len )
{
    
if (!enable_ || !isVisible())
    
{
        
return;
    }


    
bool enter = false;
    
bool update = false;

    
for (int i = 0; i < len;)
    
{
        
int l = _charMap[(unsigned char)text[i]];

        
if ((int)contentText_.size() + (l < 0 ? -l : 1) > maxChar_)
        
{
            
for (; i < len; ++i)
            
{
                
if (text[i] == '\n')
                
{
                    enter 
= true;
                }

            }


            
break;
        }


        
if (l < 0)
        
{
            
if (!isNumber_)
            
{
                update 
= true;
                lengthStack_.push_back(
-l);
                
for (int j = 0; j < -l; ++j)
                
{
                    contentText_.push_back(text[i 
+ j]);
                }

            }


            i 
+= -l;
        }

        
else
        
{
            
if (text[i] == '\n')
            
{
                enter 
= true;
                
break;
            }


            
if (isNumber_)
            
{
                
if (text[i] >= '0' && text[i] <= '9')
                
{
                    update 
= true;
                    lengthStack_.push_back(
1);
                    contentText_.push_back(text[i]);
                }

            }

            
else
            
{
                update 
= true;
                lengthStack_.push_back(
1);
                contentText_.push_back(text[i]);
            }


            
++i;
        }

    }


    
if (update)
    
{
        updateString();
        
if (listener_)
        
{
            listener_
->onEditBoxChange(this);
        }

    }


    
if (enter)
    
{
        detachWithIME();
        
if (listener_)
        
{
            listener_
->onEditBoxReturn(this);
        }

    }

}


void EditBox::deleteBackward()
{
    
if (!contentText_.empty() && !lengthStack_.empty())
    
{
        contentText_ 
= contentText_.substr(0, contentText_.size() - lengthStack_.back());
        lengthStack_.pop_back();

        updateString();
        
if (listener_)
        
{
            listener_
->onEditBoxChange(this);
        }

    }

}


void EditBox::keyboardWillShow( CCIMEKeyboardNotificationInfo& info )
{
    
if (g_currentEditBox_ != this)
    
{
        
return;
    }

    
    CCNode
* node = this, *root = this;
    
while ((node = node->getParent()))
    
{
        root 
= node;
    }

    
    CCPoint position 
= root->getPosition();
    
if (adjustVert_ != 0)
    
{
        root
->setPosition(CCPoint(position.x, position.y - adjustVert_));
    }

    adjustVert_ 
= 0;
    
    CCPoint point 
= convertToWorldSpace(CCPointZero);
    
    
float mY = info.end.getMaxY() + AppDelegate::getKeyboardOffsetY();

    
if (mY > point.y)
    
{
        adjustVert_ 
= mY - point.y + 4 + CCDirector::sharedDirector()->getVisibleOrigin().y;
        position 
= root->getPosition();
        root
->setPosition(CCPoint(position.x, position.y + adjustVert_));
    }

}


void EditBox::keyboardDidShow( CCIMEKeyboardNotificationInfo& info )
{

}


void EditBox::keyboardWillHide( CCIMEKeyboardNotificationInfo& info )
{
    
if (g_currentEditBox_ != this)
    
{
        
return;
    }

    
    
if (adjustVert_ != 0)
    
{
        CCNode
* node = this, *root = this;
        
        
while ((node = node->getParent()))
        
{
            root 
= node;
        }

        
        CCPoint position 
= root->getPosition();
        root
->setPosition(CCPoint(position.x, position.y - adjustVert_));
        adjustVert_ 
= 0;
    }


}


void EditBox::keyboardDidHide( CCIMEKeyboardNotificationInfo& info )
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    g_currentEditBox_ 
= NULL;
#endif
}


bool EditBox::ccTouchBegan( CCTouch *pTouch, CCEvent *pEvent )
{
    
if (isVisible() && enable_)
    
{
        CCRect rect;
        rect.origin 
= CCPointZero;
        rect.size 
= getContentSize();
        CCPoint position 
= convertTouchToNodeSpace(pTouch);
        
        
if (rect.containsPoint(position))
        
{
            attachWithIME();
            
return true;
        }

        
else
        
{
            detachWithIME();
        }

    }


    
return false;
}


void EditBox::ccTouchMoved( CCTouch *pTouch, CCEvent *pEvent )
{
    CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);
}


void EditBox::ccTouchEnded( CCTouch *pTouch, CCEvent *pEvent )
{
    CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);
}


void EditBox::ccTouchCancelled( CCTouch *pTouch, CCEvent *pEvent )
{
    CC_UNUSED_PARAM(pTouch); CC_UNUSED_PARAM(pEvent);
}


void EditBox::updateCursor( float dt )
{
    
if (cursorState_ != -1)
    
{
        cursorState_ 
= (++cursorState_ % 2);
    }

}


void EditBox::onEnter()
{
    schedule(SEL_SCHEDULE(
&EditBox::updateCursor), 0.5);
    CCNode::onEnter();
    CCDirector::sharedDirector()
->getTouchDispatcher()->addTargetedDelegate(this, kCCMenuHandlerPriority, true);
}


void EditBox::onExit()
{
    CCDirector::sharedDirector()
->getTouchDispatcher()->removeDelegate(this);
    unschedule(SEL_SCHEDULE(
&EditBox::updateCursor));
    CCNode::onExit();
}


void EditBox::visit()
{
    
if (!isVisible())
    
{
        
return;
    }


    glEnable(GL_SCISSOR_TEST);

    CCPoint position 
= convertToWorldSpace(CCPointZero);
    CCSize size 
= getContentSize();

    CCEGLView::sharedOpenGLView()
->setScissorInPoints(position.x, position.y, size.width, size.height);

    CCNode::visit();

    
if (cursorState_ == 1 && g_currentEditBox_ == this)
    
{
        CCSize size 
= getContentSize();
        CCPoint position1;

        
if (contentLabel_ && contentLabel_->isVisible())
        
{
            CCRect rect 
= contentLabel_->boundingBox();

            
if (size.width <= rect.size.width - 4)
            
{
                position1 
= CCPoint(size.width - 4, 0);
            }

            
else
            
{
                position1 
= CCPoint(rect.size.width, 0);
            }

        }

        
else
        
{
            position1 
= CCPointZero;
        }


        position1.x 
= (position1.x < 2 ? 2 : position1.x);
        position1.y 
= -adjustVert_;

        cocos2d::ccDrawColor4B(
0, 0, 255, 128);
        
for (int i = 2; i < 6; ++i)
        
{
            cocos2d::ccDrawLine(cocos2d::CCPoint(position.x 
+ position1.x + i , position.y + position1.y),
                cocos2d::CCPoint(position.x 
+ position1.x + i , position.y + position1.y + size.height));
        }

    }


    glDisable(GL_SCISSOR_TEST);
}


void EditBox::setBackground( cocos2d::CCNode* background )
{
    
if (background_)
    
{
        removeChild(background_, 
true);
    }

    addChild(background);

    background_ 
= background;
}


void EditBox::setLabel( cocos2d::CCLabelTTF* label )
{
    contentLabel_ 
= label;
    contentLabel_
->setAnchorPoint(CCPointZero);

    updateString();
}


void EditBox::setPlaceholderLabel( cocos2d::CCLabelTTF* label )
{
    placeholderLabel_ 
= label;
    placeholderLabel_
->setAnchorPoint(CCPointZero);

    updateString();
}


void EditBox::setString( const std::string& text )
{
    lengthStack_.clear();
    contentText_ 
= "";
    insertText(text.c_str(), (
int)text.size());
//     contentText_ = text;
// 
//     if (contentLabel_)
//     {
//         contentLabel_->setString(text.c_str());
//     }
// 
//     updateString();
}


void EditBox::setType( TYPE type )
{
    type_ 
= type;

    updateString();
}


void EditBox::setMaxChar( int maxChar )
{
    maxChar_ 
= maxChar;
}


void EditBox::updateString()
{
    
if (contentText_.empty())
    
{
        
if (contentLabel_)
        
{
            contentLabel_
->setString("");
        }


        
if (placeholderLabel_)
        
{
            placeholderLabel_
->setVisible(true);
            
if (contentLabel_)
            
{
                contentLabel_
->setVisible(false);
            }


            CCRect rect 
= placeholderLabel_->boundingBox();
            CCSize size 
= getContentSize();

            placeholderLabel_
->setPosition(CCPoint(0, ( size.height - rect.size.height ) / 2.0f));
        }

    }

    
else
    
{
        
if (contentLabel_)
        
{
            contentLabel_
->setVisible(true);
            
if (placeholderLabel_)
            
{
                placeholderLabel_
->setVisible(false);
            }


            
if (type_ == TYPE_NORMAL)
            
{
                contentLabel_
->setString(contentText_.c_str());
            }

            
else if (type_ == TYPE_PASSWORD)
            
{
                contentLabel_
->setString(std::string().append(lengthStack_.size(), '*').c_str());
            }


            CCRect rect 
= contentLabel_->boundingBox();
            CCSize size 
= getContentSize();

            
float y = ( size.height  - rect.size.height) / 2.0f;

            
if (size.width - 4 <= rect.size.width)
            
{
                contentLabel_
->setPosition(CCPoint(size.width - rect.size.width - 4 , y));
            }

            
else
            
{
                contentLabel_
->setPosition(CCPoint(0, y));
            }

        }

    }

}


EditBox
* EditBox::create( cocos2d::CCSize size, 
    cocos2d::CCLabelTTF
* label, 
    cocos2d::CCLabelTTF
* placeholderLabel /*= NULL*/, 
    cocos2d::CCNode
* background /*= NULL*/, 
    TYPE type 
/*=     TYPE_NORMAL*/ )
{
    EditBox
* box = new EditBox;
    box
->autorelease();

    
if (background)
    
{
        background
->setAnchorPoint(CCPointZero);
        background
->setPosition(0, 0);
        box
->addChild(background, 0);
        box
->background_ = background;
    }


    
if (label)
    
{
        label
->setAnchorPoint(CCPointZero);
        box
->addChild(label, 1);
        box
->contentLabel_ = label;
    }


    
if (placeholderLabel)
    
{
        placeholderLabel
->setAnchorPoint(CCPointZero);
        box
->addChild(placeholderLabel, 1);
        box
->placeholderLabel_ = placeholderLabel;
    }


//     {
//         cocos2d::CCMenu* menu = cocos2d::CCMenu::create();
//         cocos2d::CCMenuItemLabel* label = cocos2d::CCMenuItemLabel::create(
//             cocos2d::CCLabelTTF::create("X", "Arial", 20));
//         menu->addChild(label);
//         menu->setPosition(cocos2d::CCPoint(size.width, size.height / 2));
//         box->addChild(menu);
//     }

    box
->setContentSize(size);
    box
->type_ = type;
    box
->updateString();

    
return box;
}


}

 

posted on 2013-07-01 19:51 l1989 阅读(5347) 评论(2)  编辑 收藏 引用 所属分类: C++ 、游戏

评论

# re: [cocos2d-x] 分享一个EditBox 控件代码。  回复  更多评论   

太好了!
2013-09-04 20:15 | 速度

# re: [cocos2d-x] 分享一个EditBox 控件代码。  回复  更多评论   

求教下,你这个类怎么用呢?
2014-03-14 10:37 | 吴