1 /** 2 * The Render Engine 3 * MouseInputComponent 4 * 5 * @fileoverview An extension of the input component which handles 6 * mouse input. 7 * 8 * @author: Brett Fattori (brettf@renderengine.com) 9 * @author: $Author: bfattori $ 10 * @version: $Revision: 1555 $ 11 * 12 * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com) 13 * 14 * Permission is hereby granted, free of charge, to any person obtaining a copy 15 * of this software and associated documentation files (the "Software"), to deal 16 * in the Software without restriction, including without limitation the rights 17 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 * copies of the Software, and to permit persons to whom the Software is 19 * furnished to do so, subject to the following conditions: 20 * 21 * The above copyright notice and this permission notice shall be included in 22 * all copies or substantial portions of the Software. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 * THE SOFTWARE. 31 * 32 */ 33 34 // The class this file defines and its required classes 35 R.Engine.define({ 36 "class":"R.components.input.Mouse", 37 "requires":[ 38 "R.components.Input", 39 "R.engine.Events", 40 "R.lang.Timeout", 41 "R.math.Point2D", 42 "R.math.Vector2D", 43 "R.math.Math2D" 44 ] 45 }); 46 47 /** 48 * @class A component which responds to mouse events and notifies 49 * the host object when one of the events occurs. The <tt>R.engine.GameObject</tt> should 50 * add event handlers for any of the following: 51 * <ul> 52 * <li><tt>mouseover</tt> - The mouse moved over the host object, or the object moved under the mouse</li> 53 * <li><tt>mouseout</tt> - The mouse moved out of the host object (after being over it)</li> 54 * <li><tt>mousedown</tt> - A mouse button was depressed, while over the object</li> 55 * <li><tt>mouseup</tt> - A mouse button was released</li> 56 * <li><tt>click</tt> - A mouse button was depressed, and released, while over the object</li> 57 * <li><tt>mousemove</tt> - The mouse was moved</li> 58 * </ul> 59 * Each event is passed the event object and a {@link R.struct.MouseInfo MouseInfo} structure which 60 * contains information about the mouse event in the context of a game. 61 * <p/> 62 * <i>Note: The rendering context that the object is contained within needs to enable mouse event 63 * capturing with the {@link R.rendercontexts.AbstractRenderContext#captureMouse} method.</i> 64 * Objects which wish to be notified via the <tt>mouseover</tt> event handler will need to define 65 * a bounding box. 66 * 67 * @param name {String} The unique name of the component. 68 * @param priority {Number} The priority of the component among other input components. 69 * @extends R.components.Input 70 * @constructor 71 * @description Create a mouse input component. 72 */ 73 R.components.input.Mouse = function () { 74 "use strict"; 75 return R.components.Input.extend(/** @scope R.components.input.Mouse.prototype */{ 76 77 /** 78 * @private 79 */ 80 constructor:function (name, priority) { 81 this.base(name, priority); 82 }, 83 84 /** 85 * Destroy the component. 86 */ 87 destroy:function () { 88 if (this.getGameObject()) { 89 delete this.getGameObject().getObjectDataModel()[R.components.input.Mouse.MOUSE_DATA_MODEL]; 90 } 91 this.base(); 92 }, 93 94 /** 95 * Deprecated in favor of {@link #setGameObject} 96 * @deprecated 97 */ 98 setHostObject:function (hostobj) { 99 this.setGameObject(hostobj); 100 }, 101 102 /** 103 * Set the game object this component exists within. Additionally, this component 104 * sets some readable flags on the game object and establishes (if not already set) 105 * a mouse listener on the render context. 106 * 107 * @param gameObject {R.engine.GameObject} The object which hosts the component 108 * @private 109 */ 110 setGameObject:function (gameObject) { 111 this.base(gameObject); 112 113 // Set some flags we can check 114 var dataModel = gameObject.setObjectDataModel(R.components.input.Mouse.MOUSE_DATA_MODEL, { 115 mouseOver:false, 116 mouseDown:false 117 }); 118 119 // Add event pass-thru for DOM objects 120 var el = gameObject.jQ(); 121 if (el) { 122 var mI = R.struct.MouseInfo.create(); 123 124 // Wire up event handlers for the DOM element to mimic what is done for 125 // canvas objects 126 el.bind("mousemove", function (evt) { 127 mI.lastPosition.set(mI.position); 128 mI.position.set(evt.pageX, evt.pageY); 129 gameObject.triggerEvent("mousemove", [mI]); 130 }); 131 132 el.bind("mouseover", function (evt) { 133 mI.lastPosition.set(mI.position); 134 mI.position.set(evt.pageX, evt.pageY); 135 gameObject.triggerEvent("mousover", [mI]); 136 }); 137 138 el.bind("mouseout", function (evt) { 139 mI.lastPosition.set(mI.position); 140 mI.position.set(evt.pageX, evt.pageY); 141 mI.lastOver = gameObject; 142 gameObject.triggerEvent("mouseout", [mI]); 143 }); 144 145 el.bind("mousedown", function (evt) { 146 mI.button = evt.which; 147 mI.downPosition.set(evt.pageX, evt.pageY); 148 gameObject.triggerEvent("mousedown", [mI]); 149 }); 150 151 el.bind("mouseup", function (evt) { 152 mI.button = R.engine.Events.MOUSE_NO_BUTTON; 153 mI.dragVec.set(0, 0); 154 gameObject.triggerEvent("mouseup", [mI]); 155 }); 156 157 el.bind("click", function (evt) { 158 mI.button = evt.which; 159 mI.downPosition.set(evt.pageX, evt.pageY); 160 gameObject.triggerEvent("click", [mI]); 161 }); 162 } 163 }, 164 165 /** 166 * Perform the checks on the mouse info object, and also perform 167 * intersection tests to be able to call mouse events. 168 * @param renderContext {R.rendercontexts.AbstractRenderContext} The render context 169 * @param time {Number} The current world time 170 * @param dt {Number} The delta between the world time and the last time the world was updated 171 * in milliseconds. 172 */ 173 execute:function (renderContext, time, dt) { 174 // Objects may be in motion. If so, we need to call the mouse 175 // methods for just such a case. 176 var gameObject = this.getGameObject(); 177 178 // Only objects without an element will use this. For object WITH an element, 179 // this component will have intervened and wired up special handlers to fake 180 // the mouseInfo object. 181 if (!gameObject.getElement()) { 182 var mouseInfo = renderContext.getMouseInfo(), 183 bBox = gameObject.getWorldBox(), 184 mouseOver = false, 185 dataModel = gameObject.getObjectDataModel(R.components.input.Mouse.MOUSE_DATA_MODEL); 186 187 if (mouseInfo && bBox) { 188 mouseOver = R.math.Math2D.boxPointCollision(bBox, mouseInfo.position); 189 } 190 191 // Mouse position changed 192 if (!mouseInfo.position.equals(mouseInfo.lastPosition) && mouseOver) { 193 gameObject.triggerEvent("mousemove", [mouseInfo]); 194 } 195 196 // Mouse is over object 197 if (mouseOver && !dataModel.mouseOver) { 198 dataModel.mouseOver = true; 199 gameObject.triggerEvent("mouseover", [mouseInfo]); 200 } 201 202 // Mouse was over object 203 if (!mouseOver && dataModel.mouseOver === true) { 204 dataModel.mouseOver = false; 205 mouseInfo.lastOver = this; 206 gameObject.triggerEvent("mouseout", [mouseInfo]); 207 } 208 209 // Whether the mouse is over the object or not, we'll still record that the 210 // mouse button was pressed. 211 if (!dataModel.mouseDown && (mouseInfo.button != R.engine.Events.MOUSE_NO_BUTTON)) { 212 213 // BAF: 06/17/2011 - https://github.com/bfattori/TheRenderEngine/issues/8 214 // Mouse down can only be triggered if the mouse went down while over the object 215 if (mouseOver) { 216 dataModel.mouseDown = true; 217 gameObject.triggerEvent("mousedown", [mouseInfo]); 218 } 219 } 220 221 // Mouse button released (and mouse was down) 222 if (dataModel.mouseDown && (mouseInfo.button == R.engine.Events.MOUSE_NO_BUTTON)) { 223 dataModel.mouseDown = false; 224 gameObject.triggerEvent("mouseup", [mouseInfo]); 225 226 // Trigger the "click" event if the mouse was pressed and released 227 // over an object 228 if (mouseOver) { 229 gameObject.triggerEvent("click", [mouseInfo]); 230 } 231 } 232 } 233 } 234 235 }, /** @scope R.components.input.Mouse.prototype */{ 236 /** 237 * Get the class name of this object 238 * 239 * @return {String} "R.components.input.Mouse" 240 */ 241 getClassName:function () { 242 return "R.components.input.Mouse"; 243 }, 244 245 /** 246 * @private 247 */ 248 MOUSE_DATA_MODEL:"MouseInputComponent" 249 }); 250 };