/* * Copyright (c) 2012 cocos2d-x.org * https://axmolengine.github.io/ * * Copyright 2012 Yannick Loriot. All rights reserved. * http://yannickloriot.com * * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. * * 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 "ControlSwitch.h" #include "2d/Sprite.h" #include "2d/ActionTween.h" #include "2d/Label.h" #include "2d/ClippingNode.h" #include "renderer/Shaders.h" #include "2d/RenderTexture.h" NS_AX_EXT_BEGIN // ControlSwitchSprite class ControlSwitchSprite : public Sprite, public ActionTweenDelegate { public: /** creates an autorelease instance of ControlSwitchSprite */ static ControlSwitchSprite* create(Sprite* maskSprite, Sprite* onSprite, Sprite* offSprite, Sprite* thumbSprite, Label* onLabel, Label* offLabel); /** * @js NA * @lua NA */ void needsLayout(); /** * @js NA * @lua NA */ void setSliderXPosition(float sliderXPosition); /** * @js NA * @lua NA */ float getSliderXPosition() { return _sliderXPosition; } /** * @js NA * @lua NA */ float onSideWidth(); /** * @js NA * @lua NA */ float offSideWidth(); /** * @js NA * @lua NA */ virtual void updateTweenAction(float value, std::string_view key) override; /** Contains the position (in x-axis) of the slider inside the receiver. */ float _sliderXPosition; AX_SYNTHESIZE(float, _onPosition, OnPosition) AX_SYNTHESIZE(float, _offPosition, OffPosition) AX_SYNTHESIZE_RETAIN(Texture2D*, _maskTexture, MaskTexture) AX_SYNTHESIZE(uint32_t, _textureLocation, TextureLocation) AX_SYNTHESIZE(uint32_t, _maskLocation, MaskLocation) AX_SYNTHESIZE_RETAIN(Sprite*, _onSprite, OnSprite) AX_SYNTHESIZE_RETAIN(Sprite*, _offSprite, OffSprite) AX_SYNTHESIZE_RETAIN(Sprite*, _thumbSprite, ThumbSprite) AX_SYNTHESIZE_RETAIN(Label*, _onLabel, OnLabel) AX_SYNTHESIZE_RETAIN(Label*, _offLabel, OffLabel) Sprite* _clipperStencil; protected: /** * @js NA * @lua NA */ ControlSwitchSprite(); /** * @js NA * @lua NA */ virtual ~ControlSwitchSprite(); /** * @js NA * @lua NA */ bool initWithMaskSprite(Sprite* maskSprite, Sprite* onSprite, Sprite* offSprite, Sprite* thumbSprite, Label* onLabel, Label* offLabel); private: AX_DISALLOW_COPY_AND_ASSIGN(ControlSwitchSprite); }; ControlSwitchSprite* ControlSwitchSprite::create(Sprite* maskSprite, Sprite* onSprite, Sprite* offSprite, Sprite* thumbSprite, Label* onLabel, Label* offLabel) { auto ret = new ControlSwitchSprite(); ret->initWithMaskSprite(maskSprite, onSprite, offSprite, thumbSprite, onLabel, offLabel); ret->autorelease(); return ret; } ControlSwitchSprite::ControlSwitchSprite() : _sliderXPosition(0.0f) , _onPosition(0.0f) , _offPosition(0.0f) , _maskTexture(nullptr) , _textureLocation(0) , _maskLocation(0) , _onSprite(nullptr) , _offSprite(nullptr) , _thumbSprite(nullptr) , _onLabel(nullptr) , _offLabel(nullptr) , _clipperStencil(nullptr) {} ControlSwitchSprite::~ControlSwitchSprite() { AX_SAFE_RELEASE(_onSprite); AX_SAFE_RELEASE(_offSprite); AX_SAFE_RELEASE(_thumbSprite); AX_SAFE_RELEASE(_onLabel); AX_SAFE_RELEASE(_offLabel); AX_SAFE_RELEASE(_maskTexture); AX_SAFE_RELEASE(_clipperStencil); } bool ControlSwitchSprite::initWithMaskSprite(Sprite* maskSprite, Sprite* onSprite, Sprite* offSprite, Sprite* thumbSprite, Label* onLabel, Label* offLabel) { if (Sprite::initWithTexture(maskSprite->getTexture())) { // Sets the default values _onPosition = 0; _offPosition = -onSprite->getContentSize().width + thumbSprite->getContentSize().width / 2; _sliderXPosition = _onPosition; setOnSprite(onSprite); setOffSprite(offSprite); setThumbSprite(thumbSprite); setOnLabel(onLabel); setOffLabel(offLabel); ClippingNode* clipper = ClippingNode::create(); _clipperStencil = Sprite::createWithTexture(maskSprite->getTexture()); _clipperStencil->retain(); clipper->setAlphaThreshold(0.1f); clipper->setStencil(_clipperStencil); clipper->addChild(onSprite); clipper->addChild(offSprite); if (onLabel) { clipper->addChild(onLabel); // might be null } if (offLabel) { clipper->addChild(offLabel); // might be null } clipper->addChild(thumbSprite); addChild(clipper); // Set up the mask with the Mask shader setMaskTexture(maskSprite->getTexture()); setContentSize(_maskTexture->getContentSize()); needsLayout(); return true; } return false; } void ControlSwitchSprite::updateTweenAction(float value, std::string_view key) { AXLOGINFO("key = %s, value = %f", key.c_str(), value); setSliderXPosition(value); } void ControlSwitchSprite::needsLayout() { _onSprite->setPosition(_onSprite->getContentSize().width / 2 + _sliderXPosition, _onSprite->getContentSize().height / 2); _offSprite->setPosition( _onSprite->getContentSize().width + _offSprite->getContentSize().width / 2 + _sliderXPosition, _offSprite->getContentSize().height / 2); _thumbSprite->setPosition(_onSprite->getContentSize().width + _sliderXPosition, _maskTexture->getContentSize().height / 2); _clipperStencil->setPosition(_maskTexture->getContentSize().width / 2, _maskTexture->getContentSize().height / 2); if (_onLabel) { _onLabel->setAnchorPoint(Vec2::ANCHOR_MIDDLE); _onLabel->setPosition(_onSprite->getPosition().x - _thumbSprite->getContentSize().width / 6, _onSprite->getContentSize().height / 2); } if (_offLabel) { _offLabel->setAnchorPoint(Vec2::ANCHOR_MIDDLE); _offLabel->setPosition(_offSprite->getPosition().x + _thumbSprite->getContentSize().width / 6, _offSprite->getContentSize().height / 2); } setFlippedY(true); } void ControlSwitchSprite::setSliderXPosition(float sliderXPosition) { if (sliderXPosition <= _offPosition) { // Off sliderXPosition = _offPosition; } else if (sliderXPosition >= _onPosition) { // On sliderXPosition = _onPosition; } _sliderXPosition = sliderXPosition; needsLayout(); } float ControlSwitchSprite::onSideWidth() { return _onSprite->getContentSize().width; } float ControlSwitchSprite::offSideWidth() { return _offSprite->getContentSize().height; } // ControlSwitch ControlSwitch::ControlSwitch() : _switchSprite(nullptr), _initialTouchXPosition(0.0f), _moved(false), _on(false) {} ControlSwitch::~ControlSwitch() { AX_SAFE_RELEASE(_switchSprite); } bool ControlSwitch::initWithMaskSprite(Sprite* maskSprite, Sprite* onSprite, Sprite* offSprite, Sprite* thumbSprite) { return initWithMaskSprite(maskSprite, onSprite, offSprite, thumbSprite, nullptr, nullptr); } ControlSwitch* ControlSwitch::create(Sprite* maskSprite, Sprite* onSprite, Sprite* offSprite, Sprite* thumbSprite) { ControlSwitch* pRet = new ControlSwitch(); if (pRet->initWithMaskSprite(maskSprite, onSprite, offSprite, thumbSprite, nullptr, nullptr)) { pRet->autorelease(); } else { AX_SAFE_DELETE(pRet); } return pRet; } bool ControlSwitch::initWithMaskSprite(Sprite* maskSprite, Sprite* onSprite, Sprite* offSprite, Sprite* thumbSprite, Label* onLabel, Label* offLabel) { if (Control::init()) { AXASSERT(maskSprite, "Mask must not be nil."); AXASSERT(onSprite, "onSprite must not be nil."); AXASSERT(offSprite, "offSprite must not be nil."); AXASSERT(thumbSprite, "thumbSprite must not be nil."); _on = true; _switchSprite = ControlSwitchSprite::create(maskSprite, onSprite, offSprite, thumbSprite, onLabel, offLabel); _switchSprite->retain(); _switchSprite->setPosition(_switchSprite->getContentSize().width / 2, _switchSprite->getContentSize().height / 2); addChild(_switchSprite); setIgnoreAnchorPointForPosition(false); setAnchorPoint(Vec2(0.5f, 0.5f)); setContentSize(_switchSprite->getContentSize()); return true; } return false; } ControlSwitch* ControlSwitch::create(Sprite* maskSprite, Sprite* onSprite, Sprite* offSprite, Sprite* thumbSprite, Label* onLabel, Label* offLabel) { ControlSwitch* pRet = new ControlSwitch(); if (pRet->initWithMaskSprite(maskSprite, onSprite, offSprite, thumbSprite, onLabel, offLabel)) { pRet->autorelease(); } else { AX_SAFE_DELETE(pRet); } return pRet; } void ControlSwitch::setOn(bool isOn) { setOn(isOn, false); } void ControlSwitch::setOn(bool isOn, bool animated) { _on = isOn; if (animated) { _switchSprite->runAction( ActionTween::create(0.2f, "sliderXPosition", _switchSprite->getSliderXPosition(), (_on) ? _switchSprite->getOnPosition() : _switchSprite->getOffPosition())); } else { _switchSprite->setSliderXPosition((_on) ? _switchSprite->getOnPosition() : _switchSprite->getOffPosition()); } sendActionsForControlEvents(Control::EventType::VALUE_CHANGED); } void ControlSwitch::setEnabled(bool enabled) { _enabled = enabled; if (_switchSprite != nullptr) { _switchSprite->setOpacity((enabled) ? 255 : 128); } } Vec2 ControlSwitch::locationFromTouch(Touch* pTouch) { Vec2 touchLocation = pTouch->getLocation(); // Get the touch position touchLocation = this->convertToNodeSpace(touchLocation); // Convert to the node space of this class return touchLocation; } bool ControlSwitch::onTouchBegan(Touch* pTouch, Event* /*pEvent*/) { if (!isTouchInside(pTouch) || !isEnabled() || !isVisible()) { return false; } _moved = false; Vec2 location = this->locationFromTouch(pTouch); _initialTouchXPosition = location.x - _switchSprite->getSliderXPosition(); _switchSprite->getThumbSprite()->setColor(Color3B::GRAY); _switchSprite->needsLayout(); return true; } void ControlSwitch::onTouchMoved(Touch* pTouch, Event* /*pEvent*/) { Vec2 location = this->locationFromTouch(pTouch); location = Vec2(location.x - _initialTouchXPosition, 0.0f); _moved = true; _switchSprite->setSliderXPosition(location.x); } void ControlSwitch::onTouchEnded(Touch* pTouch, Event* /*pEvent*/) { Vec2 location = this->locationFromTouch(pTouch); _switchSprite->getThumbSprite()->setColor(Color3B::WHITE); if (hasMoved()) { setOn(!(location.x < _switchSprite->getContentSize().width / 2), true); } else { setOn(!_on, true); } } void ControlSwitch::onTouchCancelled(Touch* pTouch, Event* /*pEvent*/) { Vec2 location = this->locationFromTouch(pTouch); _switchSprite->getThumbSprite()->setColor(Color3B::WHITE); if (hasMoved()) { setOn(!(location.x < _switchSprite->getContentSize().width / 2), true); } else { setOn(!_on, true); } } NS_AX_EXT_END