Skip to content

Commit

Permalink
Fix UI Element input events on multiple touches. (#4562)
Browse files Browse the repository at this point in the history
* Fix UI Element input events on multiple touches.

* Fix tests.
  • Loading branch information
jpauloruschel committed Aug 25, 2022
1 parent 90d6613 commit ff00ed5
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 55 deletions.
16 changes: 7 additions & 9 deletions src/framework/components/element/element-drag-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ class ElementDragHelper extends EventHandler {

_toggleDragListeners(onOrOff) {
const isOn = onOrOff === 'on';
const addOrRemoveEventListener = isOn ? 'addEventListener' : 'removeEventListener';

// Prevent multiple listeners
if (this._hasDragListeners && isOn) {
Expand All @@ -94,18 +93,17 @@ class ElementDragHelper extends EventHandler {
this._handleMouseUpOrTouchEnd = this._onMouseUpOrTouchEnd.bind(this);
}

// Note that we handle release events directly on the window object, rather than
// on app.mouse or app.touch. This is in order to correctly handle cases where the
// user releases the mouse/touch outside of the window.
// mouse events, if mouse is available
if (this._app.mouse) {
this._app.mouse[onOrOff]('mousemove', this._onMove, this);
window[addOrRemoveEventListener]('mouseup', this._handleMouseUpOrTouchEnd, false);
this._element[onOrOff]('mousemove', this._onMove, this);
this._element[onOrOff]('mouseup', this._handleMouseUpOrTouchEnd, false);
}

// touch events, if touch is available
if (platform.touch) {
this._app.touch[onOrOff]('touchmove', this._onMove, this);
window[addOrRemoveEventListener]('touchend', this._handleMouseUpOrTouchEnd, false);
window[addOrRemoveEventListener]('touchcancel', this._handleMouseUpOrTouchEnd, false);
this._element[onOrOff]('touchmove', this._onMove, this);
this._element[onOrOff]('touchend', this._handleMouseUpOrTouchEnd, this);
this._element[onOrOff]('touchcancel', this._handleMouseUpOrTouchEnd, this);
}

this._hasDragListeners = isOn;
Expand Down
64 changes: 23 additions & 41 deletions src/input/element-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -513,8 +513,6 @@ class ElementInput {
return;

this._calcMouseCoords(event);
if (targetX === null)
return;

this._onElementMouseEvent('mouseup', event);
}
Expand All @@ -526,8 +524,6 @@ class ElementInput {
return;

this._calcMouseCoords(event);
if (targetX === null)
return;

this._onElementMouseEvent('mousedown', event);
}
Expand All @@ -536,8 +532,6 @@ class ElementInput {
if (!this._enabled) return;

this._calcMouseCoords(event);
if (targetX === null)
return;

this._onElementMouseEvent('mousemove', event);

Expand All @@ -549,8 +543,6 @@ class ElementInput {
if (!this._enabled) return;

this._calcMouseCoords(event);
if (targetX === null)
return;

this._onElementMouseEvent('mousewheel', event);
}
Expand Down Expand Up @@ -647,19 +639,17 @@ class ElementInput {

// check if touch was released over previously touch
// element in order to fire click event
if (event.touches.length === 0) {
const coords = this._calcTouchCoords(touch);

for (let c = cameras.length - 1; c >= 0; c--) {
const hovered = this._getTargetElement(cameras[c], coords.x, coords.y);
if (hovered === element) {
const coords = this._calcTouchCoords(touch);

if (!this._clickedEntities[element.entity.getGuid()]) {
this._fireEvent('click', new ElementTouchEvent(event, element, camera, x, y, touch));
this._clickedEntities[element.entity.getGuid()] = true;
}
for (let c = cameras.length - 1; c >= 0; c--) {
const hovered = this._getTargetElement(cameras[c], coords.x, coords.y);
if (hovered === element) {

if (!this._clickedEntities[element.entity.getGuid()]) {
this._fireEvent('click', new ElementTouchEvent(event, element, camera, x, y, touch));
this._clickedEntities[element.entity.getGuid()] = true;
}

}
}
}
Expand Down Expand Up @@ -701,9 +691,9 @@ class ElementInput {
}

_onElementMouseEvent(eventType, event) {
let element;
let element = null;

const hovered = this._hoveredElement;
const lastHovered = this._hoveredElement;
this._hoveredElement = null;

const cameras = this.app.systems.camera.cameras;
Expand All @@ -720,21 +710,25 @@ class ElementInput {
break;
}

// fire mouse event
if (element) {
this._fireEvent(eventType, new ElementMouseEvent(event, element, camera, targetX, targetY, this._lastX, this._lastY));
// currently hovered element is whatever's being pointed by mouse (which may be null)
this._hoveredElement = element;

this._hoveredElement = element;
// if there was a pressed element, it takes full priority of 'move' and 'up' events
if ((eventType === 'mousemove' || eventType === 'mouseup') && this._pressedElement) {
this._fireEvent(eventType, new ElementMouseEvent(event, this._pressedElement, camera, targetX, targetY, this._lastX, this._lastY));
} else if (element) {
// otherwise, fire it to the currently hovered event
this._fireEvent(eventType, new ElementMouseEvent(event, element, camera, targetX, targetY, this._lastX, this._lastY));

if (eventType === 'mousedown') {
this._pressedElement = element;
}
}

if (hovered !== this._hoveredElement) {
if (lastHovered !== this._hoveredElement) {
// mouseleave event
if (hovered) {
this._fireEvent('mouseleave', new ElementMouseEvent(event, hovered, camera, targetX, targetY, this._lastX, this._lastY));
if (lastHovered) {
this._fireEvent('mouseleave', new ElementMouseEvent(event, lastHovered, camera, targetX, targetY, this._lastX, this._lastY));
}

// mouseenter event
Expand Down Expand Up @@ -880,20 +874,8 @@ class ElementInput {
const rect = this._target.getBoundingClientRect();
const left = Math.floor(rect.left);
const top = Math.floor(rect.top);

// mouse is outside of canvas
if (event.clientX < left ||
event.clientX >= left + this._target.clientWidth ||
event.clientY < top ||
event.clientY >= top + this._target.clientHeight) {

targetX = null;
targetY = null;
} else {
// calculate coords and scale them to the graphicsDevice size
targetX = (event.clientX - left);
targetY = (event.clientY - top);
}
targetX = (event.clientX - left);
targetY = (event.clientY - top);
}

_calcTouchCoords(touch) {
Expand Down
10 changes: 5 additions & 5 deletions tests/framework/components/element/test_elementdraghelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ describe("pc.ElementDragHelper", function() {
camera: camera
});

app.mouse.fire("mousemove", {
element.fire("mousemove", {
x: 51,
y: 52
});
Expand All @@ -132,7 +132,7 @@ describe("pc.ElementDragHelper", function() {
camera: camera
});

app.touch.fire("touchmove", {
element.fire("touchmove", {
x: 51,
y: 52
});
Expand All @@ -149,7 +149,7 @@ describe("pc.ElementDragHelper", function() {
camera: camera
});

window.dispatchEvent(new Event('mouseup'));
element.fire('mouseup');

expect(dragEndHandler.callCount).to.equal(1);
expect(dragHelper.isDragging).to.equal(false);
Expand All @@ -169,7 +169,7 @@ describe("pc.ElementDragHelper", function() {
camera: camera
});

window.dispatchEvent(new Event(touchEventName));
element.fire(touchEventName);

expect(dragEndHandler.callCount).to.equal(1);
expect(dragHelper.isDragging).to.equal(false);
Expand Down Expand Up @@ -226,7 +226,7 @@ describe("pc.ElementDragHelper", function() {
camera: camera
});

app.mouse.fire("mousemove", {
element.fire("mousemove", {
x: 60,
y: 60
});
Expand Down

0 comments on commit ff00ed5

Please sign in to comment.