源码版本来自3.1rc
转载请注明
cocos2d-x源码分析总目录
http://blog.csdn.net/u011225840/article/details/31743129
enum class EventType { TOUCH_DOWN = 1 << 0, // A touch-down event in the control. DRAG_INSIDE = 1 << 1, // An event where a finger is dragged inside the bounds of the control. DRAG_OUTSIDE = 1 << 2, // An event where a finger is dragged just outside the bounds of the control. DRAG_ENTER = 1 << 3, // An event where a finger is dragged into the bounds of the control. DRAG_EXIT = 1 << 4, // An event where a finger is dragged from within a control to outside its bounds. TOUCH_UP_INSIDE = 1 << 5, // A touch-up event in the control where the finger is inside the bounds of the control. TOUCH_UP_OUTSIDE = 1 << 6, // A touch-up event in the control where the finger is outside the bounds of the control. TOUCH_CANCEL = 1 << 7, // A system event canceling the current touches for the control. VALUE_CHANGED = 1 << 8 // A touch dragging or otherwise manipulating a control, causing it to emit a series of different values. };
enum class State { NORMAL = 1 << 0, // The normal, or default state of a control—that is, enabled but neither selected nor highlighted. HIGH_LIGHTED = 1 << 1, // Highlighted state of a control. A control enters this state when a touch down, drag inside or drag enter is performed. You can retrieve and set this value through the highlighted property. DISABLED = 1 << 2, // Disabled state of a control. This state indicates that the control is currently disabled. You can retrieve and set this value through the enabled property. SELECTED = 1 << 3 // Selected state of a control. This state indicates that the control is currently selected. You can retrieve and set this value through the selected property. };
void Control::sendActionsForControlEvents(EventType controlEvents) { //retain和release的作用是保证执行该actions的过程中,control不会被delete。 //可能会有actions会release 事件来源Ref--control,所以需要先retain,保证其执行完所有events后再release。 retain(); // For each control events for (int i = 0; i < kControlEventTotalNumber; i++) { // If the given controlEvents bitmask contains the curent event //bit位适配 if (((int)controlEvents & (1 << i))) { // Call invocations const auto& invocationList = this->dispatchListforControlEvent((Control::EventType)(1<<i)); for(const auto &invocation : invocationList) { invocation->invoke(this); } } } release(); }
Vector<Invocation*>& Control::dispatchListforControlEvent(EventType controlEvent) { //这个函数的作用是获得该类事件类型的InvocationVector Vector<Invocation*>* invocationList = nullptr; auto iter = _dispatchTable.find((int)controlEvent); // If the invocation list does not exist for the dispatch table, we create it if (iter == _dispatchTable.end()) { invocationList = new Vector<Invocation*>(); _dispatchTable[(int)controlEvent] = invocationList; } else { invocationList = iter->second; } return *invocationList; }
void Control::addTargetWithActionForControlEvents(Ref* target, Handler action, EventType controlEvents) { // For each control events for (int i = 0; i < kControlEventTotalNumber; i++) { // If the given controlEvents bitmask contains the curent event if (((int)controlEvents & (1 << i))) { this->addTargetWithActionForControlEvent(target, action, (EventType)(1<<i)); } } }
void Control::addTargetWithActionForControlEvent(Ref* target, Handler action, EventType controlEvent) { // Create the invocation object Invocation *invocation = Invocation::create(target, action, controlEvent); // Add the invocation into the dispatch list for the given control event auto& eventInvocationList = this->dispatchListforControlEvent(controlEvent); //此时pushback的同时也会retain eventInvocationList.pushBack(invocation); }
void Control::removeTargetWithActionForControlEvents(Ref* target, Handler action, EventType controlEvents) { // For each control events for (int i = 0; i < kControlEventTotalNumber; i++) { // If the given controlEvents bitmask contains the curent event if (((int)controlEvents & (1 << i))) { this->removeTargetWithActionForControlEvent(target, action, (EventType)(1 << i)); } } }
void Control::removeTargetWithActionForControlEvent(Ref* target, Handler action, EventType controlEvent) { // Retrieve all invocations for the given control event //<Invocation*> auto& eventInvocationList = this->dispatchListforControlEvent(controlEvent); //remove all invocations if the target and action are null //TODO: should the invocations be deleted, or just removed from the array? Won't that cause issues if you add a single invocation for multiple events? if (!target && !action) { //remove objects eventInvocationList.clear(); } else { std::vector<Invocation*> tobeRemovedInvocations; //normally we would use a predicate, but this won't work here. Have to do it manually for(const auto &invocation : eventInvocationList) { bool shouldBeRemoved=true; if (target) { shouldBeRemoved=(target==invocation->getTarget()); } if (action) { shouldBeRemoved=(shouldBeRemoved && (action==invocation->getAction())); } // Remove the corresponding invocation object if (shouldBeRemoved) { tobeRemovedInvocations.push_back(invocation); } } for(const auto &invocation : tobeRemovedInvocations) { eventInvocationList.eraseObject(invocation); } } }
bool ControlButton::onTouchBegan(Touch *pTouch, Event *pEvent) { //是否接收该touch if (!isTouchInside(pTouch) || !isEnabled() || !isVisible() || !hasVisibleParents() ) { return false; } //感觉这段与hasVisibleParents重复了,可以删除 for (Node *c = this->_parent; c != nullptr; c = c->getParent()) { if (c->isVisible() == false) { return false; } } _isPushed = true; this->setHighlighted(true); //touch down事件只在began中触发 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); //在inside内部move并且当前状态不是highlight,说明从外部移入到内部,触发事件drag enter if (isTouchMoveInside && !isHighlighted()) { setHighlighted(true); sendActionsForControlEvents(Control::EventType::DRAG_ENTER); } //inside内部move并且当前状态时highlight,说明在内部移动,触发事件 drag inside else if (isTouchMoveInside && isHighlighted()) { sendActionsForControlEvents(Control::EventType::DRAG_INSIDE); } //outside move 但是当前状态是highlight,证明从内移动到外,触发事件drag exit else if (!isTouchMoveInside && isHighlighted()) { setHighlighted(false); sendActionsForControlEvents(Control::EventType::DRAG_EXIT); } //outside move 并且 不是highlight 在外部移动,触发事件 drag outside else if (!isTouchMoveInside && !isHighlighted()) { sendActionsForControlEvents(Control::EventType::DRAG_OUTSIDE); } }
void ControlButton::onTouchEnded(Touch *pTouch, Event *pEvent) { _isPushed = false; setHighlighted(false); //在这里其实应该增加判断的,对于controlButton放在scrollView或者tableView或者可以移动的layer上的时候 //应该给用户一个开关选择,根据移动了距离的多少判断用户是否要触发touch up inside和 outside 事件。 if (isTouchInside(pTouch)) { sendActionsForControlEvents(Control::EventType::TOUCH_UP_INSIDE); } else { sendActionsForControlEvents(Control::EventType::TOUCH_UP_OUTSIDE); } }
void ControlButton::onTouchCancelled(Touch *pTouch, Event *pEvent) { _isPushed = false; setHighlighted(false); sendActionsForControlEvents(Control::EventType::TOUCH_CANCEL); }
bool ControlButton::initWithLabelAndBackgroundSprite(Node* node, Scale9Sprite* backgroundSprite) { if (Control::init()) { CCASSERT(node != nullptr, "Label must not be nil."); LabelProtocol* label = dynamic_cast<LabelProtocol*>(node); CCASSERT(backgroundSprite != nullptr, "Background sprite must not be nil."); CCASSERT(label != nullptr || backgroundSprite != nullptr, ""); _parentInited = true; _isPushed = false; // Adjust the background image by default setAdjustBackgroundImage(true); setPreferredSize(Size::ZERO); // Zooming button by default _zoomOnTouchDown = true; _scaleRatio = 1.1f; // Set the default anchor point ignoreAnchorPointForPosition(false); setAnchorPoint(Vec2::ANCHOR_MIDDLE); // Set the nodes,label setTitleLabel(node); setBackgroundSprite(backgroundSprite); // Set the default color and opacity setColor(Color3B::WHITE); setOpacity(255.0f); setOpacityModifyRGB(true); // Initialize the dispatch table,开始时候的状态皆为normal 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; } }
std::unordered_map<int, std::string> _titleDispatchTable; std::unordered_map<int, Color3B> _titleColorDispatchTable; Map<int, Node*> _titleLabelDispatchTable; Map<int, Scale9Sprite*> _backgroundSpriteDispatchTable;
void ControlButton::needsLayout() { //整体步骤:获取特定状态下的label和sprite,然后将button的size设置为两者的最大值,然后显示两者 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<LabelProtocol*>(_titleLabel); if (label && !_currentTitle.empty()) { label->setString(_currentTitle); } if (_titleLabel) { _titleLabel->setColor(_currentTitleColor); } if (_titleLabel != nullptr) { _titleLabel->setPosition(Vec2 (getContentSize().width / 2, getContentSize().height / 2)); } // Update the background sprite this->setBackgroundSprite(this->getBackgroundSpriteForState(_state)); if (_backgroundSprite != nullptr) { _backgroundSprite->setPosition(Vec2 (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 //总体来说,需要注意的就是这里,将两者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(Vec2(getContentSize().width/2, getContentSize().height/2)); // Make visible the background and the label _titleLabel->setVisible(true); } if (_backgroundSprite != nullptr) { _backgroundSprite->setPosition(Vec2(getContentSize().width/2, getContentSize().height/2)); _backgroundSprite->setVisible(true); } }
cocos2d-x 源码分析 : control 源码分析 ( 控制类组件 controlButton),布布扣,bubuko.com
cocos2d-x 源码分析 : control 源码分析 ( 控制类组件 controlButton)
原文:http://blog.csdn.net/u011225840/article/details/35988389