/** * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011-2012 cocos2d-x.org * Copyright (c) 2013-2014 Chukong Technologies Inc. * 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. * */ /** Number of kinds of control event. */ cc.CONTROL_EVENT_TOTAL_NUMBER = 9; /** Kinds of possible events for the control objects. */ cc.CONTROL_EVENT_TOUCH_DOWN = 1 << 0; // A touch-down event in the control. cc.CONTROL_EVENT_TOUCH_DRAG_INSIDE = 1 << 1; // An event where a finger is dragged inside the bounds of the control. cc.CONTROL_EVENT_TOUCH_DRAG_OUTSIDE = 1 << 2; // An event where a finger is dragged just outside the bounds of the control. cc.CONTROL_EVENT_TOUCH_DRAG_ENTER = 1 << 3; // An event where a finger is dragged into the bounds of the control. cc.CONTROL_EVENT_TOUCH_DRAG_EXIT = 1 << 4; // An event where a finger is dragged from within a control to outside its bounds. cc.CONTROL_EVENT_TOUCH_UP_INSIDE = 1 << 5; // A touch-up event in the control where the finger is inside the bounds of the control. cc.CONTROL_EVENT_TOUCH_UP_OUTSIDE = 1 << 6; // A touch-up event in the control where the finger is outside the bounds of the control. cc.CONTROL_EVENT_TOUCH_CANCEL = 1 << 7; // A system event canceling the current touches for the control. cc.CONTROL_EVENT_VALUECHANGED = 1 << 8; // A touch dragging or otherwise manipulating a control; causing it to emit a series of different values. /** The possible state for a control. */ cc.CONTROL_STATE_NORMAL = 1 << 0; // The normal; or default state of a control梩hat is; enabled but neither selected nor highlighted. cc.CONTROL_STATE_HIGHLIGHTED = 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. cc.CONTROL_STATE_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. cc.CONTROL_STATE_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. cc.CONTROL_STATE_INITIAL = 1 << 3; /** * CCControl is inspired by the UIControl API class from the UIKit library of * CocoaTouch. It provides a base class for control CCSprites such as CCButton * or CCSlider that convey user intent to the application. * The goal of CCControl is to define an interface and base implementation for * preparing action messages and initially dispatching them to their targets when * certain events occur. * To use the CCControl you have to subclass it. * @class * @extends cc.Layer * * @property {Number} state - <@readonly> The current control state: cc.CONTROL_STATE_NORMAL | cc.CONTROL_STATE_HIGHLIGHTED | cc.CONTROL_STATE_DISABLED | cc.CONTROL_STATE_SELECTED | cc.CONTROL_STATE_INITIAL * @property {Boolean} enabled - Indicate whether the control node is enbaled * @property {Boolean} selected - Indicate whether the control node is selected * @property {Boolean} highlighted - Indicate whether the control node is highlighted */ cc.Control = cc.Layer.extend(/** @lends cc.Control# */{ _isOpacityModifyRGB: false, _hasVisibleParents: false, _touchListener: null, _className: "Control", isOpacityModifyRGB: function () { return this._isOpacityModifyRGB; }, setOpacityModifyRGB: function (opacityModifyRGB) { this._isOpacityModifyRGB = opacityModifyRGB; var children = this.getChildren(); for (var i = 0, len = children.length; i < len; i++) { var selNode = children[i]; if (selNode) selNode.setOpacityModifyRGB(opacityModifyRGB); } }, /** The current control state constant. */ _state: cc.CONTROL_STATE_NORMAL, getState: function () { return this._state; }, _enabled: false, _selected: false, _highlighted: false, _dispatchTable: null, /** * Tells whether the control is enabled * @param {Boolean} enabled */ setEnabled: function (enabled) { this._enabled = enabled; this._state = enabled ? cc.CONTROL_STATE_NORMAL : cc.CONTROL_STATE_DISABLED; this.needsLayout(); }, isEnabled: function () { return this._enabled; }, /** * A Boolean value that determines the control selected state. * @param {Boolean} selected */ setSelected: function (selected) { this._selected = selected; this.needsLayout(); }, isSelected: function () { return this._selected; }, /** * A Boolean value that determines whether the control is highlighted. * @param {Boolean} highlighted */ setHighlighted: function (highlighted) { this._highlighted = highlighted; this.needsLayout(); }, isHighlighted: function () { return this._highlighted; }, hasVisibleParents: function () { var parent = this.getParent(); for (var c = parent; c != null; c = c.getParent()) { if (!c.isVisible()) return false; } return true; }, ctor: function () { cc.Layer.prototype.ctor.call(this); this._dispatchTable = {}; this._color = cc.color.WHITE; }, init: function () { // Initialise instance variables this._state = cc.CONTROL_STATE_NORMAL; this._enabled = true; this._selected = false; this._highlighted = false; var listener = cc.EventListener.create({ event: cc.EventListener.TOUCH_ONE_BY_ONE, swallowTouches: true }); if (this.onTouchBegan) listener.onTouchBegan = this.onTouchBegan.bind(this); if (this.onTouchMoved) listener.onTouchMoved = this.onTouchMoved.bind(this); if (this.onTouchEnded) listener.onTouchEnded = this.onTouchEnded.bind(this); if (this.onTouchCancelled) listener.onTouchCancelled = this.onTouchCancelled.bind(this); this._touchListener = listener; return true; }, onEnter: function () { var locListener = this._touchListener; if (!locListener._isRegistered()) cc.eventManager.addListener(locListener, this); cc.Node.prototype.onEnter.call(this); }, /** * Sends action messages for the given control events. * which action messages are sent. See "CCControlEvent" for bitmask constants. * @param {Number} controlEvents A bitmask whose set flags specify the control events for */ sendActionsForControlEvents: function (controlEvents) { // For each control events for (var i = 0, len = cc.CONTROL_EVENT_TOTAL_NUMBER; i < len; i++) { // If the given controlEvents bitmask contains the curent event if ((controlEvents & (1 << i))) { // Call invocations // var invocationList = this._dispatchListforControlEvent(1 << i); for (var j = 0, inLen = invocationList.length; j < inLen; j++) { invocationList[j].invoke(this); } } } }, /** *

* Adds a target and action for a particular event (or events) to an internal
* dispatch table.
* The action message may optionally include the sender and the event as
* parameters, in that order.
* When you call this method, target is not retained. *

* @param {Object} target The target object that is, the object to which the action message is sent. It cannot be nil. The target is not retained. * @param {function} action A selector identifying an action message. It cannot be NULL. * @param {Number} controlEvents A bitmask specifying the control events for which the action message is sent. See "CCControlEvent" for bitmask constants. */ addTargetWithActionForControlEvents: function (target, action, controlEvents) { // For each control events for (var i = 0, len = cc.CONTROL_EVENT_TOTAL_NUMBER; i < len; i++) { // If the given controlEvents bit mask contains the current event if ((controlEvents & (1 << i))) this._addTargetWithActionForControlEvent(target, action, 1 << i); } }, /** * Removes a target and action for a particular event (or events) from an internal dispatch table. * * @param {Object} target The target object that is, the object to which the action message is sent. Pass nil to remove all targets paired with action and the specified control events. * @param {function} action A selector identifying an action message. Pass NULL to remove all action messages paired with target. * @param {Number} controlEvents A bitmask specifying the control events associated with target and action. See "CCControlEvent" for bitmask constants. */ removeTargetWithActionForControlEvents: function (target, action, controlEvents) { // For each control events for (var i = 0, len = cc.CONTROL_EVENT_TOTAL_NUMBER; i < len; i++) { // If the given controlEvents bitmask contains the current event if ((controlEvents & (1 << i))) this._removeTargetWithActionForControlEvent(target, action, 1 << i); } }, /** * Returns a point corresponding to the touh location converted into the * control space coordinates. * @param {cc.Touch} touch A CCTouch object that represents a touch. */ getTouchLocation: function (touch) { var touchLocation = touch.getLocation(); // Get the touch position return this.convertToNodeSpace(touchLocation); // Convert to the node space of this class }, /** * Returns a boolean value that indicates whether a touch is inside the bounds of the receiver. The given touch must be relative to the world. * * @param {cc.Touch} touch A cc.Touch object that represents a touch. * @return {Boolean} YES whether a touch is inside the receiver's rect. */ isTouchInside: function (touch) { var touchLocation = touch.getLocation(); // Get the touch position touchLocation = this.getParent().convertToNodeSpace(touchLocation); return cc.rectContainsPoint(this.getBoundingBox(), touchLocation); }, /** *

* Returns an cc.Invocation object able to construct messages using a given
* target-action pair. (The invocation may optionally include the sender and
* the event as parameters, in that order) *

* @param {Object} target The target object. * @param {function} action A selector identifying an action message. * @param {Number} controlEvent A control events for which the action message is sent. See "CCControlEvent" for constants. * * @return {cc.Invocation} an CCInvocation object able to construct messages using a given target-action pair. */ _invocationWithTargetAndActionForControlEvent: function (target, action, controlEvent) { return null; }, /** * Returns the cc.Invocation list for the given control event. If the list does not exist, it'll create an empty array before returning it. * * @param {Number} controlEvent A control events for which the action message is sent. See "CCControlEvent" for constants. * @return {cc.Invocation} the cc.Invocation list for the given control event. */ _dispatchListforControlEvent: function (controlEvent) { controlEvent = controlEvent.toString(); // If the invocation list does not exist for the dispatch table, we create it if (!this._dispatchTable[controlEvent]) this._dispatchTable[controlEvent] = []; return this._dispatchTable[controlEvent]; }, /** * Adds a target and action for a particular event to an internal dispatch * table. * The action message may optionally include the sender and the event as * parameters, in that order. * When you call this method, target is not retained. * * @param target The target object that is, the object to which the action * message is sent. It cannot be nil. The target is not retained. * @param action A selector identifying an action message. It cannot be NULL. * @param controlEvent A control event for which the action message is sent. * See "CCControlEvent" for constants. */ _addTargetWithActionForControlEvent: function (target, action, controlEvent) { // Create the invocation object var invocation = new cc.Invocation(target, action, controlEvent); // Add the invocation into the dispatch list for the given control event var eventInvocationList = this._dispatchListforControlEvent(controlEvent); eventInvocationList.push(invocation); }, /** * Removes a target and action for a particular event from an internal dispatch table. * * @param {Object} target The target object that is, the object to which the action message is sent. Pass nil to remove all targets paired with action and the specified control events. * @param {function} action A selector identifying an action message. Pass NULL to remove all action messages paired with target. * @param {Number} controlEvent A control event for which the action message is sent. See "CCControlEvent" for constants. */ _removeTargetWithActionForControlEvent: function (target, action, controlEvent) { // Retrieve all invocations for the given control event // var 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? var bDeleteObjects = true; if (!target && !action) { //remove objects eventInvocationList.length = 0; } else { //normally we would use a predicate, but this won't work here. Have to do it manually for (var i = 0; i < eventInvocationList.length;) { var invocation = eventInvocationList[i]; var shouldBeRemoved = true; if (target) shouldBeRemoved = (target === invocation.getTarget()); if (action) shouldBeRemoved = (shouldBeRemoved && (action === invocation.getAction())); // Remove the corresponding invocation object if (shouldBeRemoved) cc.arrayRemoveObject(eventInvocationList, invocation); else i++; } } }, /** * Updates the control layout using its current internal state. */ needsLayout: function () { } }); var _p = cc.Control.prototype; // Extended properties /** @expose */ _p.state; cc.defineGetterSetter(_p, "state", _p.getState); /** @expose */ _p.enabled; cc.defineGetterSetter(_p, "enabled", _p.isEnabled, _p.setEnabled); /** @expose */ _p.selected; cc.defineGetterSetter(_p, "selected", _p.isSelected, _p.setSelected); /** @expose */ _p.highlighted; cc.defineGetterSetter(_p, "highlighted", _p.isHighlighted, _p.setHighlighted); _p = null; cc.Control.create = function () { var retControl = new cc.Control(); if (retControl && retControl.init()) return retControl; return null; };