1 /** 2 * 3 * Copyright (c) 2010-2012 cocos2d-x.org 4 * Copyright 2011 Yannick Loriot. 5 * http://yannickloriot.com 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in 15 * all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 * THE SOFTWARE. 24 * 25 * 26 * converted to Javascript / cocos2d-x by Angus C 27 */ 28 29 /** Number of kinds of control event. */ 30 cc.CONTROL_EVENT_TOTAL_NUMBER = 9; 31 32 /** Kinds of possible events for the control objects. */ 33 cc.CONTROL_EVENT_TOUCH_DOWN = 1 << 0; // A touch-down event in the control. 34 cc.CONTROL_EVENT_TOUCH_DRAG_INSIDE = 1 << 1; // An event where a finger is dragged inside the bounds of the control. 35 cc.CONTROL_EVENT_TOUCH_DRAG_OUTSIDE = 1 << 2; // An event where a finger is dragged just outside the bounds of the control. 36 cc.CONTROL_EVENT_TOUCH_DRAG_ENTER = 1 << 3; // An event where a finger is dragged into the bounds of the control. 37 cc.CONTROL_EVENT_TOUCH_DRAG_EXIT = 1 << 4; // An event where a finger is dragged from within a control to outside its bounds. 38 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. 39 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. 40 cc.CONTROL_EVENT_TOUCH_CANCEL = 1 << 7; // A system event canceling the current touches for the control. 41 cc.CONTROL_EVENT_VALUECHANGED = 1 << 8; // A touch dragging or otherwise manipulating a control; causing it to emit a series of different values. 42 43 /** The possible state for a control. */ 44 cc.CONTROL_STATE_NORMAL = 1 << 0; // The normal; or default state of a control梩hat is; enabled but neither selected nor highlighted. 45 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. 46 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. 47 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. 48 cc.CONTROL_STATE_INITIAL = 1 << 3; 49 50 /** 51 * CCControl is inspired by the UIControl API class from the UIKit library of 52 * CocoaTouch. It provides a base class for control CCSprites such as CCButton 53 * or CCSlider that convey user intent to the application. 54 * The goal of CCControl is to define an interface and base implementation for 55 * preparing action messages and initially dispatching them to their targets when 56 * certain events occur. 57 * To use the CCControl you have to subclass it. 58 * @class 59 * @extends cc.LayerRGBA 60 * 61 * @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 62 * @property {Boolean} enabled - Indicate whether the control node is enbaled 63 * @property {Boolean} selected - Indicate whether the control node is selected 64 * @property {Boolean} highlighted - Indicate whether the control node is highlighted 65 */ 66 cc.Control = cc.LayerRGBA.extend(/** @lends cc.Control# */{ 67 _isOpacityModifyRGB:false, 68 _hasVisibleParents:false, 69 _touchListener: null, 70 _className:"Control", 71 72 isOpacityModifyRGB:function () { 73 return this._isOpacityModifyRGB; 74 }, 75 setOpacityModifyRGB:function (opacityModifyRGB) { 76 this._isOpacityModifyRGB = opacityModifyRGB; 77 78 var children = this.getChildren(); 79 for (var i = 0, len = children.length; i < len; i++) { 80 var selNode = children[i]; 81 if (selNode && selNode.RGBAProtocol) 82 selNode.setOpacityModifyRGB(opacityModifyRGB); 83 } 84 }, 85 86 /** The current control state constant. */ 87 _state:cc.CONTROL_STATE_NORMAL, 88 getState:function () { 89 return this._state; 90 }, 91 92 _enabled:false, 93 _selected:false, 94 _highlighted:false, 95 96 _dispatchTable:null, 97 98 /** 99 * Tells whether the control is enabled 100 * @param {Boolean} enabled 101 */ 102 setEnabled:function (enabled) { 103 this._enabled = enabled; 104 this._state = enabled ? cc.CONTROL_STATE_NORMAL:cc.CONTROL_STATE_DISABLED; 105 106 this.needsLayout(); 107 }, 108 isEnabled:function () { 109 return this._enabled; 110 }, 111 112 /** 113 * A Boolean value that determines the control selected state. 114 * @param {Boolean} selected 115 */ 116 setSelected:function (selected) { 117 this._selected = selected; 118 this.needsLayout(); 119 }, 120 isSelected:function () { 121 return this._selected; 122 }, 123 124 /** 125 * A Boolean value that determines whether the control is highlighted. 126 * @param {Boolean} highlighted 127 */ 128 setHighlighted:function (highlighted) { 129 this._highlighted = highlighted; 130 this.needsLayout(); 131 }, 132 isHighlighted:function () { 133 return this._highlighted; 134 }, 135 136 hasVisibleParents:function () { 137 var parent = this.getParent(); 138 for (var c = parent; c != null; c = c.getParent()) { 139 if (!c.isVisible()) 140 return false; 141 } 142 return true; 143 }, 144 145 ctor:function () { 146 cc.LayerRGBA.prototype.ctor.call(this); 147 this._dispatchTable = {}; 148 this._color = cc.color.WHITE; 149 }, 150 151 init:function () { 152 if (cc.LayerRGBA.prototype.init.call(this)) { 153 // Initialise instance variables 154 this._state = cc.CONTROL_STATE_NORMAL; 155 this._enabled = true; 156 this._selected = false; 157 this._highlighted = false; 158 159 var listener = cc.EventListener.create({ 160 event: cc.EventListener.TOUCH_ONE_BY_ONE 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 window._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 delete window._p; 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