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 cc.Control = cc.LayerRGBA.extend({ 62 _isOpacityModifyRGB:false, 63 _hasVisibleParents:false, 64 65 isOpacityModifyRGB:function () { 66 return this._isOpacityModifyRGB; 67 }, 68 setOpacityModifyRGB:function (opacityModifyRGB) { 69 this._isOpacityModifyRGB = opacityModifyRGB; 70 71 var children = this.getChildren(); 72 for (var i = 0, len = children.length; i < len; i++) { 73 var selNode = children[i]; 74 if (selNode && selNode.RGBAProtocol) 75 selNode.setOpacityModifyRGB(opacityModifyRGB); 76 } 77 }, 78 79 /** The current control state constant. */ 80 _state:cc.CONTROL_STATE_NORMAL, 81 getState:function () { 82 return this._state; 83 }, 84 85 _enabled:false, 86 _selected:false, 87 _highlighted:false, 88 89 _dispatchTable:null, 90 91 /** 92 * Tells whether the control is enabled 93 * @param {Boolean} enabled 94 */ 95 setEnabled:function (enabled) { 96 this._enabled = enabled; 97 this._state = enabled ? cc.CONTROL_STATE_NORMAL:cc.CONTROL_STATE_DISABLED; 98 99 this.needsLayout(); 100 }, 101 isEnabled:function () { 102 return this._enabled; 103 }, 104 105 /** 106 * A Boolean value that determines the control selected state. 107 * @param {Boolean} selected 108 */ 109 setSelected:function (selected) { 110 this._selected = selected; 111 this.needsLayout(); 112 }, 113 isSelected:function () { 114 return this._selected; 115 }, 116 117 /** 118 * A Boolean value that determines whether the control is highlighted. 119 * @param {Boolean} highlighted 120 */ 121 setHighlighted:function (highlighted) { 122 this._highlighted = highlighted; 123 this.needsLayout(); 124 }, 125 isHighlighted:function () { 126 return this._highlighted; 127 }, 128 129 hasVisibleParents:function () { 130 var parent = this.getParent(); 131 for (var c = parent; c != null; c = c.getParent()) { 132 if (!c.isVisible()) 133 return false; 134 } 135 return true; 136 }, 137 138 ctor:function () { 139 cc.LayerRGBA.prototype.ctor.call(this); 140 this._dispatchTable = {}; 141 this._color = cc.white(); 142 }, 143 144 init:function () { 145 if (cc.LayerRGBA.prototype.init.call(this)) { 146 //this.setTouchEnabled(true); 147 //m_bIsTouchEnabled=true; 148 // Initialise instance variables 149 this._state = cc.CONTROL_STATE_NORMAL; 150 this._enabled = true; 151 this._selected = false; 152 this._highlighted = false; 153 154 // Set the touch dispatcher priority by default to 1 155 this._defaultTouchPriority = 1; 156 this.setTouchPriority(1); 157 // Initialise the tables 158 //this._dispatchTable = {}; 159 //dispatchTable.autorelease(); 160 // dispatchTable_ = [[NSMutableDictionary alloc] initWithCapacity:1]; 161 return true; 162 } else 163 return false; 164 }, 165 166 registerWithTouchDispatcher:function () { 167 cc.registerTargetedDelegate(this.getTouchPriority(), true, this); 168 }, 169 170 /** 171 * Sends action messages for the given control events. 172 * which action messages are sent. See "CCControlEvent" for bitmask constants. 173 * @param {Number} controlEvents A bitmask whose set flags specify the control events for 174 */ 175 sendActionsForControlEvents:function (controlEvents) { 176 // For each control events 177 for (var i = 0, len = cc.CONTROL_EVENT_TOTAL_NUMBER; i < len; i++) { 178 // If the given controlEvents bitmask contains the curent event 179 if ((controlEvents & (1 << i))) { 180 // Call invocations 181 // <CCInvocation*> 182 var invocationList = this._dispatchListforControlEvent(1 << i); 183 for (var j = 0, inLen = invocationList.length; j < inLen; j++) { 184 invocationList[j].invoke(this); 185 } 186 } 187 } 188 }, 189 190 /** 191 * <p> 192 * Adds a target and action for a particular event (or events) to an internal <br/> 193 * dispatch table. <br/> 194 * The action message may optionally include the sender and the event as <br/> 195 * parameters, in that order. <br/> 196 * When you call this method, target is not retained. 197 * </p> 198 * @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. 199 * @param {function} action A selector identifying an action message. It cannot be NULL. 200 * @param {Number} controlEvents A bitmask specifying the control events for which the action message is sent. See "CCControlEvent" for bitmask constants. 201 */ 202 addTargetWithActionForControlEvents:function (target, action, controlEvents) { 203 // For each control events 204 for (var i = 0, len = cc.CONTROL_EVENT_TOTAL_NUMBER; i < len; i++) { 205 // If the given controlEvents bit mask contains the current event 206 if ((controlEvents & (1 << i))) 207 this._addTargetWithActionForControlEvent(target, action, 1 << i); 208 } 209 }, 210 211 /** 212 * Removes a target and action for a particular event (or events) from an internal dispatch table. 213 * 214 * @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. 215 * @param {function} action A selector identifying an action message. Pass NULL to remove all action messages paired with target. 216 * @param {Number} controlEvents A bitmask specifying the control events associated with target and action. See "CCControlEvent" for bitmask constants. 217 */ 218 removeTargetWithActionForControlEvents:function (target, action, controlEvents) { 219 // For each control events 220 for (var i = 0, len = cc.CONTROL_EVENT_TOTAL_NUMBER; i < len; i++) { 221 // If the given controlEvents bitmask contains the current event 222 if ((controlEvents & (1 << i))) 223 this._removeTargetWithActionForControlEvent(target, action, 1 << i); 224 } 225 }, 226 227 /** 228 * Returns a point corresponding to the touh location converted into the 229 * control space coordinates. 230 * @param {cc.Touch} touch A CCTouch object that represents a touch. 231 */ 232 getTouchLocation:function (touch) { 233 var touchLocation = touch.getLocation(); // Get the touch position 234 return this.convertToNodeSpace(touchLocation); // Convert to the node space of this class 235 }, 236 237 /** 238 * 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. 239 * 240 * @param {cc.Touch} touch A cc.Touch object that represents a touch. 241 * @return {Boolean} YES whether a touch is inside the receiver's rect. 242 */ 243 isTouchInside:function (touch) { 244 var touchLocation = touch.getLocation(); // Get the touch position 245 touchLocation = this.getParent().convertToNodeSpace(touchLocation); 246 return cc.rectContainsPoint(this.getBoundingBox(), touchLocation); 247 }, 248 249 /** 250 * <p> 251 * Returns an cc.Invocation object able to construct messages using a given <br/> 252 * target-action pair. (The invocation may optionally include the sender and <br/> 253 * the event as parameters, in that order) 254 * </p> 255 * @param {Object} target The target object. 256 * @param {function} action A selector identifying an action message. 257 * @param {Number} controlEvent A control events for which the action message is sent. See "CCControlEvent" for constants. 258 * 259 * @return {cc.Invocation} an CCInvocation object able to construct messages using a given target-action pair. 260 */ 261 _invocationWithTargetAndActionForControlEvent:function (target, action, controlEvent) { 262 return null; 263 }, 264 265 /** 266 * 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. 267 * 268 * @param {Number} controlEvent A control events for which the action message is sent. See "CCControlEvent" for constants. 269 * @return {cc.Invocation} the cc.Invocation list for the given control event. 270 */ 271 _dispatchListforControlEvent:function (controlEvent) { 272 controlEvent = controlEvent.toString(); 273 // If the invocation list does not exist for the dispatch table, we create it 274 if (!this._dispatchTable.hasOwnProperty(controlEvent)) 275 this._dispatchTable[controlEvent] = []; 276 return this._dispatchTable[controlEvent]; 277 }, 278 279 /** 280 * Adds a target and action for a particular event to an internal dispatch 281 * table. 282 * The action message may optionally include the sender and the event as 283 * parameters, in that order. 284 * When you call this method, target is not retained. 285 * 286 * @param target The target object that is, the object to which the action 287 * message is sent. It cannot be nil. The target is not retained. 288 * @param action A selector identifying an action message. It cannot be NULL. 289 * @param controlEvent A control event for which the action message is sent. 290 * See "CCControlEvent" for constants. 291 */ 292 _addTargetWithActionForControlEvent:function (target, action, controlEvent) { 293 // Create the invocation object 294 var invocation = new cc.Invocation(target, action, controlEvent); 295 296 // Add the invocation into the dispatch list for the given control event 297 var eventInvocationList = this._dispatchListforControlEvent(controlEvent); 298 eventInvocationList.push(invocation); 299 }, 300 301 /** 302 * Removes a target and action for a particular event from an internal dispatch table. 303 * 304 * @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. 305 * @param {function} action A selector identifying an action message. Pass NULL to remove all action messages paired with target. 306 * @param {Number} controlEvent A control event for which the action message is sent. See "CCControlEvent" for constants. 307 */ 308 _removeTargetWithActionForControlEvent:function (target, action, controlEvent) { 309 // Retrieve all invocations for the given control event 310 //<CCInvocation*> 311 var eventInvocationList = this._dispatchListforControlEvent(controlEvent); 312 313 //remove all invocations if the target and action are null 314 //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? 315 var bDeleteObjects = true; 316 if (!target && !action) { 317 //remove objects 318 eventInvocationList.length = 0; 319 } else { 320 //normally we would use a predicate, but this won't work here. Have to do it manually 321 for (var i = 0; i < eventInvocationList.length; ) { 322 var invocation = eventInvocationList[i]; 323 var shouldBeRemoved = true; 324 if (target) 325 shouldBeRemoved = (target == invocation.getTarget()); 326 if (action) 327 shouldBeRemoved = (shouldBeRemoved && (action == invocation.getAction())); 328 // Remove the corresponding invocation object 329 if (shouldBeRemoved) 330 cc.ArrayRemoveObject(eventInvocationList, invocation); 331 else 332 i ++; 333 } 334 } 335 }, 336 337 /** 338 * Updates the control layout using its current internal state. 339 */ 340 needsLayout:function () { 341 } 342 }); 343 344 cc.Control.create = function () { 345 var retControl = new cc.Control(); 346 if (retControl && retControl.init()) 347 return retControl; 348 return null; 349 }; 350 351