/* * Copyright (c) 2012 cocos2d-x.org * http://www.cocos2d-x.org * * Copyright 2011 Yannick Loriot. * http://yannickloriot.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "CCControlButton.h" #include "2d/CCLabel.h" #include "2d/CCAction.h" #include "2d/CCActionInterval.h" using namespace std; NS_CC_EXT_BEGIN enum { kZoomActionTag = 0xCCCB0001, }; ControlButton::ControlButton() : _isPushed(false) , _parentInited(false) , _doesAdjustBackgroundImage(false) , _currentTitleColor(Color3B::WHITE) , _titleLabel(nullptr) , _backgroundSprite(nullptr) , _zoomOnTouchDown(false) , _marginV(ControlButtonMarginTB) , _marginH(ControlButtonMarginLR) { } ControlButton::~ControlButton() { CC_SAFE_RELEASE(_titleLabel); CC_SAFE_RELEASE(_backgroundSprite); } //initialisers bool ControlButton::init() { return this->initWithLabelAndBackgroundSprite(Label::createWithSystemFont("", "Helvetica", 12), cocos2d::ui::Scale9Sprite::create(),true); } bool ControlButton::initWithLabelAndBackgroundSprite(Node* node, ui::Scale9Sprite* backgroundSprite, bool adjustBackGroundSize) { if (Control::init()) { CCASSERT(node != nullptr, "node must not be nil."); LabelProtocol* label = dynamic_cast(node); CCASSERT(backgroundSprite != nullptr, "Background sprite must not be nil."); CCASSERT(label != nullptr, "label must not be nil."); _parentInited = true; _isPushed = false; // Adjust the background image by adjustBackGroundSize setPreferredSize(Size::ZERO); setAdjustBackgroundImage(adjustBackGroundSize); // Zooming button by default _zoomOnTouchDown = true; _scaleRatio = 1.1f; // Set the default anchor point ignoreAnchorPointForPosition(false); setAnchorPoint(Vec2::ANCHOR_MIDDLE); // Set the nodes setTitleLabel(node); setBackgroundSprite(backgroundSprite); // Set the default color and opacity setColor(Color3B::WHITE); setOpacity(255.0f); setOpacityModifyRGB(true); // Initialize the dispatch table setTitleForState(label->getString(), Control::State::NORMAL); setTitleColorForState(node->getColor(), Control::State::NORMAL); setTitleLabelForState(node, Control::State::NORMAL); setBackgroundSpriteForState(backgroundSprite, Control::State::NORMAL); setLabelAnchorPoint(Vec2::ANCHOR_MIDDLE); // Layout update needsLayout(); return true; } //couldn't init the Control else { return false; } } ControlButton* ControlButton::create(Node* label, cocos2d::ui::Scale9Sprite* backgroundSprite) { ControlButton *pRet = new (std::nothrow) ControlButton(); pRet->initWithLabelAndBackgroundSprite(label, backgroundSprite, true); pRet->autorelease(); return pRet; } ControlButton* ControlButton::create(Node* label, cocos2d::ui::Scale9Sprite* backgroundSprite, bool adjustBackGroundSize) { ControlButton *pRet = new (std::nothrow) ControlButton(); pRet->initWithLabelAndBackgroundSprite(label, backgroundSprite, adjustBackGroundSize); pRet->autorelease(); return pRet; } bool ControlButton::initWithTitleAndFontNameAndFontSize(const std::string& title, const std::string& fontName, float fontSize) { return initWithLabelAndBackgroundSprite(Label::createWithSystemFont(title, fontName, fontSize), cocos2d::ui::Scale9Sprite::create(),true); } ControlButton* ControlButton::create(const std::string& title, const std::string& fontName, float fontSize) { ControlButton *pRet = new (std::nothrow) ControlButton(); pRet->initWithTitleAndFontNameAndFontSize(title, fontName, fontSize); pRet->autorelease(); return pRet; } bool ControlButton::initWithBackgroundSprite(cocos2d::ui::Scale9Sprite* sprite) { Label *label = Label::createWithSystemFont("", "Arial", 30);// return initWithLabelAndBackgroundSprite(label, sprite,false); } ControlButton* ControlButton::create(cocos2d::ui::Scale9Sprite* sprite) { ControlButton *pRet = new (std::nothrow) ControlButton(); pRet->initWithBackgroundSprite(sprite); pRet->autorelease(); return pRet; } void ControlButton::setMargins(int marginH, int marginV) { _marginV = marginV; _marginH = marginH; needsLayout(); } void ControlButton::setEnabled(bool enabled) { Control::setEnabled(enabled); needsLayout(); } void ControlButton::setSelected(bool enabled) { Control::setSelected(enabled); needsLayout(); } void ControlButton::setHighlighted(bool enabled) { if (enabled == true) { _state = Control::State::HIGH_LIGHTED; } else { _state = Control::State::NORMAL; } Control::setHighlighted(enabled); Action *action = getActionByTag(kZoomActionTag); if (action) { stopAction(action); } needsLayout(); if( _zoomOnTouchDown ) { float scaleValue = (isHighlighted() && isEnabled() && !isSelected()) ? _scaleRatio : 1.0f; Action *zoomAction = ScaleTo::create(0.05f, scaleValue); zoomAction->setTag(kZoomActionTag); runAction(zoomAction); } } void ControlButton::setZoomOnTouchDown(bool zoomOnTouchDown) { _zoomOnTouchDown = zoomOnTouchDown; } bool ControlButton::getZoomOnTouchDown() const { return _zoomOnTouchDown; } void ControlButton::setPreferredSize(const Size& size) { if(size.width == 0 && size.height == 0) { _doesAdjustBackgroundImage = true; } else { _doesAdjustBackgroundImage = false; for (auto iter = _backgroundSpriteDispatchTable.begin(); iter != _backgroundSpriteDispatchTable.end(); ++iter) { iter->second->setPreferredSize(size); } } _preferredSize = size; needsLayout(); } const Size& ControlButton::getPreferredSize() const { return _preferredSize; } void ControlButton::setAdjustBackgroundImage(bool adjustBackgroundImage) { _doesAdjustBackgroundImage=adjustBackgroundImage; needsLayout(); } bool ControlButton::doesAdjustBackgroundImage() { return _doesAdjustBackgroundImage; } const Vec2& ControlButton::getLabelAnchorPoint() const { return this->_labelAnchorPoint; } void ControlButton::setLabelAnchorPoint(const Vec2& labelAnchorPoint) { this->_labelAnchorPoint = labelAnchorPoint; if (_titleLabel != nullptr) { this->_titleLabel->setAnchorPoint(labelAnchorPoint); } } std::string ControlButton::getTitleForState(State state) { auto iter = _titleDispatchTable.find((int)state); if (iter != _titleDispatchTable.end()) { return iter->second; } iter = _titleDispatchTable.find((int)Control::State::NORMAL); return iter != _titleDispatchTable.end() ? iter->second : ""; } void ControlButton::setTitleForState(const std::string& title, State state) { _titleDispatchTable.erase((int)state); if (!title.empty()) { _titleDispatchTable[(int)state] = title; } // If the current state if equal to the given state we update the layout if (getState() == state) { needsLayout(); } } Color3B ControlButton::getTitleColorForState(State state) const { Color3B returnColor = Color3B::WHITE; auto iter = _titleColorDispatchTable.find((int)state); if (iter != _titleColorDispatchTable.end()) { returnColor = iter->second; } else { iter = _titleColorDispatchTable.find((int)Control::State::NORMAL); if (iter != _titleColorDispatchTable.end()) { returnColor = iter->second; } } return returnColor; } void ControlButton::setTitleColorForState(const Color3B& color, State state) { _titleColorDispatchTable.erase((int)state); _titleColorDispatchTable[(int)state] = color; // If the current state if equal to the given state we update the layout if (getState() == state) { needsLayout(); } } Node* ControlButton::getTitleLabelForState(State state) { Node* titleLabel = _titleLabelDispatchTable.at((int)state); if (titleLabel) { return titleLabel; } return _titleLabelDispatchTable.at((int)Control::State::NORMAL); } void ControlButton::setTitleLabelForState(Node* titleLabel, State state) { Node* previousLabel = _titleLabelDispatchTable.at((int)state); if (previousLabel) { removeChild(previousLabel, true); _titleLabelDispatchTable.erase((int)state); } _titleLabelDispatchTable.insert((int)state, titleLabel); titleLabel->setVisible(false); titleLabel->setAnchorPoint(Vec2(0.5f, 0.5f)); addChild(titleLabel, 1); // If the current state if equal to the given state we update the layout if (getState() == state) { needsLayout(); } } void ControlButton::setTitleTTFForState(const std::string& fontName, State state) { this->setTitleLabelForState(Label::createWithSystemFont(getTitleForState(state), fontName, 12), state); } const std::string& ControlButton::getTitleTTFForState(State state) { LabelProtocol* label = dynamic_cast(this->getTitleLabelForState(state)); Label* labelTTF = dynamic_cast(label); if(labelTTF != 0) { return labelTTF->getSystemFontName(); } static std::string ret(""); return ret; } void ControlButton::setTitleTTFSizeForState(float size, State state) { LabelProtocol* label = dynamic_cast(this->getTitleLabelForState(state)); if(label) { Label* labelTTF = dynamic_cast(label); if(labelTTF != 0) { return labelTTF->setSystemFontSize(size); } } } float ControlButton::getTitleTTFSizeForState(State state) { LabelProtocol* label = dynamic_cast(this->getTitleLabelForState(state)); Label* labelTTF = dynamic_cast(label); if(labelTTF != 0) { return labelTTF->getSystemFontSize(); } else { return 0; } } void ControlButton::setTitleBMFontForState(const std::string& fntFile, State state) { std::string title = this->getTitleForState(state); this->setTitleLabelForState(Label::createWithBMFont(fntFile, title), state); } const std::string& ControlButton::getTitleBMFontForState(State state) { LabelProtocol* label = dynamic_cast(this->getTitleLabelForState(state)); auto labelBMFont = dynamic_cast(label); if(labelBMFont != 0) { return labelBMFont->getBMFontFilePath(); } static std::string ret(""); return ret; } ui::Scale9Sprite* ControlButton::getBackgroundSpriteForState(State state) { auto backgroundSprite = _backgroundSpriteDispatchTable.at((int)state); if (backgroundSprite) { return backgroundSprite; } return _backgroundSpriteDispatchTable.at((int)Control::State::NORMAL); } void ControlButton::setBackgroundSpriteForState(ui::Scale9Sprite* sprite, State state) { Size oldPreferredSize = _preferredSize; auto previousBackgroundSprite = _backgroundSpriteDispatchTable.at((int)state); if (previousBackgroundSprite) { removeChild(previousBackgroundSprite, true); _backgroundSpriteDispatchTable.erase((int)state); } _backgroundSpriteDispatchTable.insert((int)state, sprite); sprite->setVisible(false); sprite->setAnchorPoint(Vec2(0.5f, 0.5f)); addChild(sprite); if (this->_preferredSize.width != 0 || this->_preferredSize.height != 0) { if (oldPreferredSize.equals(_preferredSize)) { // Force update of preferred size sprite->setPreferredSize(Size(oldPreferredSize.width+1, oldPreferredSize.height+1)); } sprite->setPreferredSize(this->_preferredSize); } // If the current state if equal to the given state we update the layout if (getState() == state) { needsLayout(); } } void ControlButton::setBackgroundSpriteFrameForState(SpriteFrame * spriteFrame, State state) { ui::Scale9Sprite * sprite = ui::Scale9Sprite::createWithSpriteFrame(spriteFrame); this->setBackgroundSpriteForState(sprite, state); } void ControlButton::needsLayout() { if (!_parentInited) { return; } // Hide the background and the label if (_titleLabel != nullptr) { _titleLabel->setVisible(false); } if (_backgroundSprite) { _backgroundSprite->setVisible(false); } // Update anchor of all labels this->setLabelAnchorPoint(this->_labelAnchorPoint); // Update the label to match with the current state _currentTitle = getTitleForState(_state); _currentTitleColor = getTitleColorForState(_state); this->setTitleLabel(getTitleLabelForState(_state)); LabelProtocol* label = dynamic_cast(_titleLabel); if (label && !_currentTitle.empty()) { label->setString(_currentTitle); } if (_titleLabel) { _titleLabel->setColor(_currentTitleColor); } if (_titleLabel != nullptr) { _titleLabel->setPosition(getContentSize().width / 2, getContentSize().height / 2); } // Update the background sprite this->setBackgroundSprite(this->getBackgroundSpriteForState(_state)); if (_backgroundSprite != nullptr) { _backgroundSprite->setPosition(getContentSize().width / 2, getContentSize().height / 2); } // Get the title label size Size titleLabelSize; if (_titleLabel != nullptr) { titleLabelSize = _titleLabel->getBoundingBox().size; } // Adjust the background image if necessary if (_doesAdjustBackgroundImage) { // Add the margins if (_backgroundSprite != nullptr) { _backgroundSprite->setContentSize(Size(titleLabelSize.width + _marginH * 2, titleLabelSize.height + _marginV * 2)); } } else { //TODO: should this also have margins if one of the preferred sizes is relaxed? if (_backgroundSprite != nullptr) { Size preferredSize = _backgroundSprite->getPreferredSize(); if (preferredSize.width <= 0) { preferredSize.width = titleLabelSize.width; } if (preferredSize.height <= 0) { preferredSize.height = titleLabelSize.height; } _backgroundSprite->setContentSize(preferredSize); } } // Set the content size Rect rectTitle; if (_titleLabel != nullptr) { rectTitle = _titleLabel->getBoundingBox(); } Rect rectBackground; if (_backgroundSprite != nullptr) { rectBackground = _backgroundSprite->getBoundingBox(); } Rect maxRect = ControlUtils::RectUnion(rectTitle, rectBackground); setContentSize(Size(maxRect.size.width, maxRect.size.height)); if (_titleLabel != nullptr) { _titleLabel->setPosition(getContentSize().width/2, getContentSize().height/2); // Make visible the background and the label _titleLabel->setVisible(true); } if (_backgroundSprite != nullptr) { _backgroundSprite->setPosition(getContentSize().width/2, getContentSize().height/2); _backgroundSprite->setVisible(true); } } bool ControlButton::onTouchBegan(Touch *pTouch, Event *pEvent) { if (!isTouchInside(pTouch) || !isEnabled() || !isVisible() || !hasVisibleParents() ) { return false; } for (Node *c = this->_parent; c != nullptr; c = c->getParent()) { if (c->isVisible() == false) { return false; } } _isPushed = true; this->setHighlighted(true); sendActionsForControlEvents(Control::EventType::TOUCH_DOWN); return true; } void ControlButton::onTouchMoved(Touch *pTouch, Event *pEvent) { if (!isEnabled() || !isPushed() || isSelected()) { if (isHighlighted()) { setHighlighted(false); } return; } bool isTouchMoveInside = isTouchInside(pTouch); if (isTouchMoveInside && !isHighlighted()) { setHighlighted(true); sendActionsForControlEvents(Control::EventType::DRAG_ENTER); } else if (isTouchMoveInside && isHighlighted()) { sendActionsForControlEvents(Control::EventType::DRAG_INSIDE); } else if (!isTouchMoveInside && isHighlighted()) { setHighlighted(false); sendActionsForControlEvents(Control::EventType::DRAG_EXIT); } else if (!isTouchMoveInside && !isHighlighted()) { sendActionsForControlEvents(Control::EventType::DRAG_OUTSIDE); } } void ControlButton::onTouchEnded(Touch *pTouch, Event *pEvent) { _isPushed = false; setHighlighted(false); if (isTouchInside(pTouch)) { sendActionsForControlEvents(Control::EventType::TOUCH_UP_INSIDE); } else { sendActionsForControlEvents(Control::EventType::TOUCH_UP_OUTSIDE); } } void ControlButton::setOpacity(GLubyte opacity) { Control::setOpacity(opacity); for (auto iter = _backgroundSpriteDispatchTable.begin(); iter != _backgroundSpriteDispatchTable.end(); ++iter) { iter->second->setOpacity(opacity); } for (auto iter = _titleLabelDispatchTable.begin(); iter != _titleLabelDispatchTable.end(); ++iter) { iter->second->setOpacity(opacity); } } void ControlButton::updateDisplayedOpacity(GLubyte parentOpacity) { Control::updateDisplayedOpacity(parentOpacity); for (auto iter = _backgroundSpriteDispatchTable.begin(); iter != _backgroundSpriteDispatchTable.end(); ++iter) { iter->second->updateDisplayedOpacity(parentOpacity); } for (auto iter = _titleLabelDispatchTable.begin(); iter != _titleLabelDispatchTable.end(); ++iter) { iter->second->updateDisplayedOpacity(parentOpacity); } } void ControlButton::setColor(const Color3B & color) { Control::setColor(color); for (auto iter = _backgroundSpriteDispatchTable.begin(); iter != _backgroundSpriteDispatchTable.end(); ++iter) { iter->second->setColor(color); } for (auto iter = _titleLabelDispatchTable.begin(); iter != _titleLabelDispatchTable.end(); ++iter) { iter->second->setColor(color); } } void ControlButton::updateDisplayedColor(const Color3B& parentColor) { Control::updateDisplayedColor(parentColor); for (auto iter = _backgroundSpriteDispatchTable.begin(); iter != _backgroundSpriteDispatchTable.end(); ++iter) { iter->second->updateDisplayedColor(parentColor); } for (auto iter = _titleLabelDispatchTable.begin(); iter != _titleLabelDispatchTable.end(); ++iter) { iter->second->updateDisplayedColor(parentColor); } } void ControlButton::onTouchCancelled(Touch *pTouch, Event *pEvent) { _isPushed = false; setHighlighted(false); sendActionsForControlEvents(Control::EventType::TOUCH_CANCEL); } ControlButton* ControlButton::create() { ControlButton *pControlButton = new (std::nothrow) ControlButton(); if (pControlButton && pControlButton->init()) { pControlButton->autorelease(); return pControlButton; } CC_SAFE_DELETE(pControlButton); return nullptr; } NS_CC_EXT_END