1 /** 2 * Copyright (c) 2008-2010 Ricardo Quesada 3 * Copyright (c) 2011-2012 cocos2d-x.org 4 * Copyright (c) 2013-2014 Chukong Technologies Inc. 5 * Copyright 2011 Yannick Loriot. 6 * http://yannickloriot.com 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a copy 9 * of this software and associated documentation files (the "Software"), to deal 10 * in the Software without restriction, including without limitation the rights 11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 * copies of the Software, and to permit persons to whom the Software is 13 * furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be included in 16 * all copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 * THE SOFTWARE. 25 * 26 */ 27 28 /** Number of kinds of control event. */ 29 cc.CONTROL_EVENT_TOTAL_NUMBER = 9; 30 31 /** Kinds of possible events for the control objects. */ 32 cc.CONTROL_EVENT_TOUCH_DOWN = 1 << 0; // A touch-down event in the control. 33 cc.CONTROL_EVENT_TOUCH_DRAG_INSIDE = 1 << 1; // An event where a finger is dragged inside the bounds of the control. 34 cc.CONTROL_EVENT_TOUCH_DRAG_OUTSIDE = 1 << 2; // An event where a finger is dragged just outside the bounds of the control. 35 cc.CONTROL_EVENT_TOUCH_DRAG_ENTER = 1 << 3; // An event where a finger is dragged into the bounds of the control. 36 cc.CONTROL_EVENT_TOUCH_DRAG_EXIT = 1 << 4; // An event where a finger is dragged from within a control to outside its bounds. 37 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. 38 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. 39 cc.CONTROL_EVENT_TOUCH_CANCEL = 1 << 7; // A system event canceling the current touches for the control. 40 cc.CONTROL_EVENT_VALUECHANGED = 1 << 8; // A touch dragging or otherwise manipulating a control; causing it to emit a series of different values. 41 42 /** The possible state for a control. */ 43 cc.CONTROL_STATE_NORMAL = 1 << 0; // The normal; or default state of a controlę¢©hat is; enabled but neither selected nor highlighted. 44 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. 45 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. 46 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. 47 cc.CONTROL_STATE_INITIAL = 1 << 3; 48 49 /** 50 * CCControl is inspired by the UIControl API class from the UIKit library of 51 * CocoaTouch. It provides a base class for control CCSprites such as CCButton 52 * or CCSlider that convey user intent to the application. 53 * The goal of CCControl is to define an interface and base implementation for 54 * preparing action messages and initially dispatching them to their targets when 55 * certain events occur. 56 * To use the CCControl you have to subclass it. 57 * @class 58 * @extends cc.Layer 59 * 60 * @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 61 * @property {Boolean} enabled - Indicate whether the control node is enbaled 62 * @property {Boolean} selected - Indicate whether the control node is selected 63 * @property {Boolean} highlighted - Indicate whether the control node is highlighted 64 */ 65 cc.Control = cc.Layer.extend(/** @lends cc.Control# */{ 66 _isOpacityModifyRGB: false, 67 _hasVisibleParents: false, 68 _touchListener: null, 69 _className: "Control", 70 71 isOpacityModifyRGB: function () { 72 return this._isOpacityModifyRGB; 73 }, 74 setOpacityModifyRGB: function (opacityModifyRGB) { 75 this._isOpacityModifyRGB = opacityModifyRGB; 76 77 var children = this.getChildren(); 78 for (var i = 0, len = children.length; i < len; i++) { 79 var selNode = children[i]; 80 if (selNode) 81 selNode.setOpacityModifyRGB(opacityModifyRGB); 82 } 83 }, 84 85 /** The current control state constant. */ 86 _state: cc.CONTROL_STATE_NORMAL, 87 getState: function () { 88 return this._state; 89 }, 90 91 _enabled: false, 92 _selected: false, 93 _highlighted: false, 94 95 _dispatchTable: null, 96 97 /** 98 * Tells whether the control is enabled 99 * @param {Boolean} enabled 100 */ 101 setEnabled: function (enabled) { 102 this._enabled = enabled; 103 this._state = enabled ? cc.CONTROL_STATE_NORMAL : cc.CONTROL_STATE_DISABLED; 104 105 this.needsLayout(); 106 }, 107 isEnabled: function () { 108 return this._enabled; 109 }, 110 111 /** 112 * A Boolean value that determines the control selected state. 113 * @param {Boolean} selected 114 */ 115 setSelected: function (selected) { 116 this._selected = selected; 117 this.needsLayout(); 118 }, 119 isSelected: function () { 120 return this._selected; 121 }, 122 123 /** 124 * A Boolean value that determines whether the control is highlighted. 125 * @param {Boolean} highlighted 126 */ 127 setHighlighted: function (highlighted) { 128 this._highlighted = highlighted; 129 this.needsLayout(); 130 }, 131 isHighlighted: function () { 132 return this._highlighted; 133 }, 134 135 hasVisibleParents: function () { 136 var parent = this.getParent(); 137 for (var c = parent; c != null; c = c.getParent()) { 138 if (!c.isVisible()) 139 return false; 140 } 141 return true; 142 }, 143 144 ctor: function () { 145 cc.Layer.prototype.ctor.call(this); 146 this._dispatchTable = {}; 147 this._color = cc.color.WHITE; 148 }, 149 150 init: function () { 151 if (cc.Layer.prototype.init.call(this)) { 152 // Initialise instance variables 153 this._state = cc.CONTROL_STATE_NORMAL; 154 this._enabled = true; 155 this._selected = false; 156 this._highlighted = false; 157 158 var listener = cc.EventListener.create({ 159 event: cc.EventListener.TOUCH_ONE_BY_ONE, 160 swallowTouches: true 161 }); 162 if (this.onTouchBegan) 163 listener.onTouchBegan = this.onTouchBegan.bind(this); 164 if (this.onTouchMoved) 165 listener.onTouchMoved = this.onTouchMoved.bind(this); 166 if (this.onTouchEnded) 167 listener.onTouchEnded = this.onTouchEnded.bind(this); 168 if (this.onTouchCancelled) 169 listener.onTouchCancelled = this.onTouchCancelled.bind(this); 170 this._touchListener = listener; 171 return true; 172 } else 173 return false; 174 }, 175 176 onEnter: function () { 177 var locListener = this._touchListener; 178 if (!locListener._isRegistered()) 179 cc.eventManager.addListener(locListener, this); 180 cc.Node.prototype.onEnter.call(this); 181 }, 182 183 /** 184 * Sends action messages for the given control events. 185 * which action messages are sent. See "CCControlEvent" for bitmask constants. 186 * @param {Number} controlEvents A bitmask whose set flags specify the control events for 187 */ 188 sendActionsForControlEvents: function (controlEvents) { 189 // For each control events 190 for (var i = 0, len = cc.CONTROL_EVENT_TOTAL_NUMBER; i < len; i++) { 191 // If the given controlEvents bitmask contains the curent event 192 if ((controlEvents & (1 << i))) { 193 // Call invocations 194 // <CCInvocation*> 195 var invocationList = this._dispatchListforControlEvent(1 << i); 196 for (var j = 0, inLen = invocationList.length; j < inLen; j++) { 197 invocationList[j].invoke(this); 198 } 199 } 200 } 201 }, 202 203 /** 204 * <p> 205 * Adds a target and action for a particular event (or events) to an internal <br/> 206 * dispatch table. <br/> 207 * The action message may optionally include the sender and the event as <br/> 208 * parameters, in that order. <br/> 209 * When you call this method, target is not retained. 210 * </p> 211 * @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. 212 * @param {function} action A selector identifying an action message. It cannot be NULL. 213 * @param {Number} controlEvents A bitmask specifying the control events for which the action message is sent. See "CCControlEvent" for bitmask constants. 214 */ 215 addTargetWithActionForControlEvents: function (target, action, controlEvents) { 216 // For each control events 217 for (var i = 0, len = cc.CONTROL_EVENT_TOTAL_NUMBER; i < len; i++) { 218 // If the given controlEvents bit mask contains the current event 219 if ((controlEvents & (1 << i))) 220 this._addTargetWithActionForControlEvent(target, action, 1 << i); 221 } 222 }, 223 224 /** 225 * Removes a target and action for a particular event (or events) from an internal dispatch table. 226 * 227 * @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. 228 * @param {function} action A selector identifying an action message. Pass NULL to remove all action messages paired with target. 229 * @param {Number} controlEvents A bitmask specifying the control events associated with target and action. See "CCControlEvent" for bitmask constants. 230 */ 231 removeTargetWithActionForControlEvents: function (target, action, controlEvents) { 232 // For each control events 233 for (var i = 0, len = cc.CONTROL_EVENT_TOTAL_NUMBER; i < len; i++) { 234 // If the given controlEvents bitmask contains the current event 235 if ((controlEvents & (1 << i))) 236 this._removeTargetWithActionForControlEvent(target, action, 1 << i); 237 } 238 }, 239 240 /** 241 * Returns a point corresponding to the touh location converted into the 242 * control space coordinates. 243 * @param {cc.Touch} touch A CCTouch object that represents a touch. 244 */ 245 getTouchLocation: function (touch) { 246 var touchLocation = touch.getLocation(); // Get the touch position 247 return this.convertToNodeSpace(touchLocation); // Convert to the node space of this class 248 }, 249 250 /** 251 * 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. 252 * 253 * @param {cc.Touch} touch A cc.Touch object that represents a touch. 254 * @return {Boolean} YES whether a touch is inside the receiver's rect. 255 */ 256 isTouchInside: function (touch) { 257 var touchLocation = touch.getLocation(); // Get the touch position 258 touchLocation = this.getParent().convertToNodeSpace(touchLocation); 259 return cc.rectContainsPoint(this.getBoundingBox(), touchLocation); 260 }, 261 262 /** 263 * <p> 264 * Returns an cc.Invocation object able to construct messages using a given <br/> 265 * target-action pair. (The invocation may optionally include the sender and <br/> 266 * the event as parameters, in that order) 267 * </p> 268 * @param {Object} target The target object. 269 * @param {function} action A selector identifying an action message. 270 * @param {Number} controlEvent A control events for which the action message is sent. See "CCControlEvent" for constants. 271 * 272 * @return {cc.Invocation} an CCInvocation object able to construct messages using a given target-action pair. 273 */ 274 _invocationWithTargetAndActionForControlEvent: function (target, action, controlEvent) { 275 return null; 276 }, 277 278 /** 279 * 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. 280 * 281 * @param {Number} controlEvent A control events for which the action message is sent. See "CCControlEvent" for constants. 282 * @return {cc.Invocation} the cc.Invocation list for the given control event. 283 */ 284 _dispatchListforControlEvent: function (controlEvent) { 285 controlEvent = controlEvent.toString(); 286 // If the invocation list does not exist for the dispatch table, we create it 287 if (!this._dispatchTable[controlEvent]) 288 this._dispatchTable[controlEvent] = []; 289 return this._dispatchTable[controlEvent]; 290 }, 291 292 /** 293 * Adds a target and action for a particular event to an internal dispatch 294 * table. 295 * The action message may optionally include the sender and the event as 296 * parameters, in that order. 297 * When you call this method, target is not retained. 298 * 299 * @param target The target object that is, the object to which the action 300 * message is sent. It cannot be nil. The target is not retained. 301 * @param action A selector identifying an action message. It cannot be NULL. 302 * @param controlEvent A control event for which the action message is sent. 303 * See "CCControlEvent" for constants. 304 */ 305 _addTargetWithActionForControlEvent: function (target, action, controlEvent) { 306 // Create the invocation object 307 var invocation = new cc.Invocation(target, action, controlEvent); 308 309 // Add the invocation into the dispatch list for the given control event 310 var eventInvocationList = this._dispatchListforControlEvent(controlEvent); 311 eventInvocationList.push(invocation); 312 }, 313 314 /** 315 * Removes a target and action for a particular event from an internal dispatch table. 316 * 317 * @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. 318 * @param {function} action A selector identifying an action message. Pass NULL to remove all action messages paired with target. 319 * @param {Number} controlEvent A control event for which the action message is sent. See "CCControlEvent" for constants. 320 */ 321 _removeTargetWithActionForControlEvent: function (target, action, controlEvent) { 322 // Retrieve all invocations for the given control event 323 //<CCInvocation*> 324 var eventInvocationList = this._dispatchListforControlEvent(controlEvent); 325 326 //remove all invocations if the target and action are null 327 //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? 328 var bDeleteObjects = true; 329 if (!target && !action) { 330 //remove objects 331 eventInvocationList.length = 0; 332 } else { 333 //normally we would use a predicate, but this won't work here. Have to do it manually 334 for (var i = 0; i < eventInvocationList.length;) { 335 var invocation = eventInvocationList[i]; 336 var shouldBeRemoved = true; 337 if (target) 338 shouldBeRemoved = (target === invocation.getTarget()); 339 if (action) 340 shouldBeRemoved = (shouldBeRemoved && (action === invocation.getAction())); 341 // Remove the corresponding invocation object 342 if (shouldBeRemoved) 343 cc.arrayRemoveObject(eventInvocationList, invocation); 344 else 345 i++; 346 } 347 } 348 }, 349 350 /** 351 * Updates the control layout using its current internal state. 352 */ 353 needsLayout: function () { 354 } 355 }); 356 357 var _p = cc.Control.prototype; 358 359 // Extended properties 360 /** @expose */ 361 _p.state; 362 cc.defineGetterSetter(_p, "state", _p.getState); 363 /** @expose */ 364 _p.enabled; 365 cc.defineGetterSetter(_p, "enabled", _p.isEnabled, _p.setEnabled); 366 /** @expose */ 367 _p.selected; 368 cc.defineGetterSetter(_p, "selected", _p.isSelected, _p.setSelected); 369 /** @expose */ 370 _p.highlighted; 371 cc.defineGetterSetter(_p, "highlighted", _p.isHighlighted, _p.setHighlighted); 372 373 _p = null; 374 375 cc.Control.create = function () { 376 var retControl = new cc.Control(); 377 if (retControl && retControl.init()) 378 return retControl; 379 return null; 380 }; 381 382