2017-04-17 15:10:04 -04:00
|
|
|
const MathUtil = require('../util/math-util');
|
2016-08-15 21:37:36 -04:00
|
|
|
|
2017-04-17 19:42:48 -04:00
|
|
|
class Mouse {
|
|
|
|
constructor (runtime) {
|
|
|
|
this._x = 0;
|
|
|
|
this._y = 0;
|
|
|
|
this._isDown = false;
|
|
|
|
/**
|
|
|
|
* Reference to the owning Runtime.
|
|
|
|
* Can be used, for example, to activate hats.
|
|
|
|
* @type{!Runtime}
|
|
|
|
*/
|
|
|
|
this.runtime = runtime;
|
|
|
|
}
|
|
|
|
|
2016-09-12 17:16:10 -04:00
|
|
|
/**
|
2018-10-25 15:08:37 -04:00
|
|
|
* Activate "event_whenthisspriteclicked" hats.
|
|
|
|
* @param {Target} target to trigger hats on.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_activateClickHats (target) {
|
|
|
|
// Activate both "this sprite clicked" and "stage clicked"
|
|
|
|
// They were separated into two opcodes for labeling,
|
|
|
|
// but should act the same way.
|
|
|
|
// Intentionally not checking isStage to make it work when sharing blocks.
|
|
|
|
// @todo the blocks should be converted from one to another when shared
|
|
|
|
this.runtime.startHats('event_whenthisspriteclicked',
|
|
|
|
null, target);
|
|
|
|
this.runtime.startHats('event_whenstageclicked',
|
|
|
|
null, target);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find a target by XY location
|
2017-04-17 19:42:48 -04:00
|
|
|
* @param {number} x X position to be sent to the renderer.
|
|
|
|
* @param {number} y Y position to be sent to the renderer.
|
2018-10-25 15:08:37 -04:00
|
|
|
* @return {Target} the target at that location
|
2017-04-17 19:42:48 -04:00
|
|
|
* @private
|
2016-09-12 17:16:10 -04:00
|
|
|
*/
|
2018-10-25 15:08:37 -04:00
|
|
|
_pickTarget (x, y) {
|
2017-04-17 19:42:48 -04:00
|
|
|
if (this.runtime.renderer) {
|
|
|
|
const drawableID = this.runtime.renderer.pick(x, y);
|
|
|
|
for (let i = 0; i < this.runtime.targets.length; i++) {
|
|
|
|
const target = this.runtime.targets[i];
|
|
|
|
if (target.hasOwnProperty('drawableID') &&
|
|
|
|
target.drawableID === drawableID) {
|
2018-10-25 15:08:37 -04:00
|
|
|
return target;
|
2017-04-17 19:42:48 -04:00
|
|
|
}
|
2016-09-12 17:16:10 -04:00
|
|
|
}
|
2016-09-15 19:02:03 -04:00
|
|
|
}
|
2018-10-25 15:08:37 -04:00
|
|
|
// Return the stage if no target was found
|
|
|
|
return this.runtime.getTargetForStage();
|
2016-08-15 21:37:36 -04:00
|
|
|
}
|
|
|
|
|
2017-04-17 19:42:48 -04:00
|
|
|
/**
|
|
|
|
* Mouse DOM event handler.
|
|
|
|
* @param {object} data Data from DOM event.
|
|
|
|
*/
|
|
|
|
postData (data) {
|
|
|
|
if (data.x) {
|
2018-01-09 10:36:03 -05:00
|
|
|
this._clientX = data.x;
|
2018-12-13 11:06:31 -05:00
|
|
|
this._scratchX = Math.round(MathUtil.clamp(
|
2018-01-09 10:36:03 -05:00
|
|
|
480 * ((data.x / data.canvasWidth) - 0.5),
|
|
|
|
-240,
|
|
|
|
240
|
2018-12-13 11:06:31 -05:00
|
|
|
));
|
2017-04-17 19:42:48 -04:00
|
|
|
}
|
|
|
|
if (data.y) {
|
2018-01-09 10:36:03 -05:00
|
|
|
this._clientY = data.y;
|
2018-12-13 11:06:31 -05:00
|
|
|
this._scratchY = Math.round(MathUtil.clamp(
|
2018-01-09 10:36:03 -05:00
|
|
|
-360 * ((data.y / data.canvasHeight) - 0.5),
|
|
|
|
-180,
|
|
|
|
180
|
2018-12-13 11:06:31 -05:00
|
|
|
));
|
2017-04-17 19:42:48 -04:00
|
|
|
}
|
|
|
|
if (typeof data.isDown !== 'undefined') {
|
2018-08-02 14:29:56 -04:00
|
|
|
const previousDownState = this._isDown;
|
2017-04-17 19:42:48 -04:00
|
|
|
this._isDown = data.isDown;
|
2018-10-25 15:08:37 -04:00
|
|
|
|
|
|
|
// Do not trigger if down state has not changed
|
|
|
|
if (previousDownState === this._isDown) return;
|
|
|
|
|
|
|
|
// Never trigger click hats at the end of a drag
|
|
|
|
if (data.wasDragged) return;
|
|
|
|
|
|
|
|
// Do not activate click hats for clicks outside canvas bounds
|
|
|
|
if (!(data.x > 0 && data.x < data.canvasWidth &&
|
|
|
|
data.y > 0 && data.y < data.canvasHeight)) return;
|
|
|
|
|
|
|
|
const target = this._pickTarget(data.x, data.y);
|
2018-08-02 14:29:56 -04:00
|
|
|
const isNewMouseDown = !previousDownState && this._isDown;
|
2018-10-25 15:08:37 -04:00
|
|
|
const isNewMouseUp = previousDownState && !this._isDown;
|
|
|
|
|
|
|
|
// Draggable targets start click hats on mouse up.
|
|
|
|
// Non-draggable targets start click hats on mouse down.
|
|
|
|
if (target.draggable && isNewMouseUp) {
|
|
|
|
this._activateClickHats(target);
|
|
|
|
} else if (!target.draggable && isNewMouseDown) {
|
|
|
|
this._activateClickHats(target);
|
2017-04-17 19:42:48 -04:00
|
|
|
}
|
2016-10-24 11:56:52 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-17 19:42:48 -04:00
|
|
|
/**
|
2018-01-09 10:36:03 -05:00
|
|
|
* Get the X position of the mouse in client coordinates.
|
2018-01-09 09:39:52 -05:00
|
|
|
* @return {number} Non-clamped X position of the mouse cursor.
|
2017-04-17 19:42:48 -04:00
|
|
|
*/
|
2018-01-09 10:36:03 -05:00
|
|
|
getClientX () {
|
|
|
|
return this._clientX;
|
2017-04-17 19:42:48 -04:00
|
|
|
}
|
2016-08-15 21:37:36 -04:00
|
|
|
|
2017-04-17 19:42:48 -04:00
|
|
|
/**
|
2018-01-09 10:36:03 -05:00
|
|
|
* Get the Y position of the mouse in client coordinates.
|
2018-01-09 09:39:52 -05:00
|
|
|
* @return {number} Non-clamped Y position of the mouse cursor.
|
2017-04-17 19:42:48 -04:00
|
|
|
*/
|
2018-01-09 10:36:03 -05:00
|
|
|
getClientY () {
|
|
|
|
return this._clientY;
|
2018-01-09 09:39:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-01-09 10:36:03 -05:00
|
|
|
* Get the X position of the mouse in scratch coordinates.
|
2018-12-13 11:06:31 -05:00
|
|
|
* @return {number} Clamped and integer rounded X position of the mouse cursor.
|
2018-01-09 09:39:52 -05:00
|
|
|
*/
|
2018-01-09 10:36:03 -05:00
|
|
|
getScratchX () {
|
|
|
|
return this._scratchX;
|
2018-01-09 09:39:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-01-09 10:36:03 -05:00
|
|
|
* Get the Y position of the mouse in scratch coordinates.
|
2018-12-13 11:06:31 -05:00
|
|
|
* @return {number} Clamped and integer rounded Y position of the mouse cursor.
|
2018-01-09 09:39:52 -05:00
|
|
|
*/
|
2018-01-09 10:36:03 -05:00
|
|
|
getScratchY () {
|
|
|
|
return this._scratchY;
|
2017-04-17 19:42:48 -04:00
|
|
|
}
|
2016-08-15 21:37:36 -04:00
|
|
|
|
2017-04-17 19:42:48 -04:00
|
|
|
/**
|
|
|
|
* Get the down state of the mouse.
|
|
|
|
* @return {boolean} Is the mouse down?
|
|
|
|
*/
|
|
|
|
getIsDown () {
|
|
|
|
return this._isDown;
|
|
|
|
}
|
|
|
|
}
|
2016-08-15 21:37:36 -04:00
|
|
|
|
|
|
|
module.exports = Mouse;
|