diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js index 2bedb34..8d0479d 100644 --- a/src/core/ticker/TickerListener.js +++ b/src/core/ticker/TickerListener.js @@ -91,13 +91,13 @@ this.fn(deltaTime); } + const redirect = this.next; + if (this.once) { - this.destroy(); + this.destroy(true); } - const redirect = this.next; - // Soft-destroying should remove // the next reference if (this._destroyed) diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js index 2bedb34..8d0479d 100644 --- a/src/core/ticker/TickerListener.js +++ b/src/core/ticker/TickerListener.js @@ -91,13 +91,13 @@ this.fn(deltaTime); } + const redirect = this.next; + if (this.once) { - this.destroy(); + this.destroy(true); } - const redirect = this.next; - // Soft-destroying should remove // the next reference if (this._destroyed) diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index e1c50d7..729469c 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -1217,6 +1217,10 @@ const eventLen = events.length; + // if the event wasn't targeting our canvas, then consider it to be pointerupoutside + // in all cases (unless it was a pointercancel) + const eventAppend = originalEvent.target !== this.interactionDOMElement ? 'outside' : ''; + for (let i = 0; i < eventLen; i++) { const event = events[i]; @@ -1227,19 +1231,20 @@ interactionEvent.data.originalEvent = originalEvent; - this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, func, true); + // perform hit testing for events targeting our canvas or cancel events + this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, func, cancelled || !eventAppend); - this.emit(cancelled ? 'pointercancel' : 'pointerup', interactionEvent); + this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent); if (event.pointerType === 'mouse') { const isRightButton = event.button === 2 || event.which === 3; - this.emit(isRightButton ? 'rightup' : 'mouseup', interactionEvent); + this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent); } else if (event.pointerType === 'touch') { - this.emit(cancelled ? 'touchcancel' : 'touchend', interactionEvent); + this.emit(cancelled ? 'touchcancel' : `touchend${eventAppend}`, interactionEvent); this.releaseInteractionDataForPointerId(event.pointerId, interactionData); } } diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js index 2bedb34..8d0479d 100644 --- a/src/core/ticker/TickerListener.js +++ b/src/core/ticker/TickerListener.js @@ -91,13 +91,13 @@ this.fn(deltaTime); } + const redirect = this.next; + if (this.once) { - this.destroy(); + this.destroy(true); } - const redirect = this.next; - // Soft-destroying should remove // the next reference if (this._destroyed) diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index e1c50d7..729469c 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -1217,6 +1217,10 @@ const eventLen = events.length; + // if the event wasn't targeting our canvas, then consider it to be pointerupoutside + // in all cases (unless it was a pointercancel) + const eventAppend = originalEvent.target !== this.interactionDOMElement ? 'outside' : ''; + for (let i = 0; i < eventLen; i++) { const event = events[i]; @@ -1227,19 +1231,20 @@ interactionEvent.data.originalEvent = originalEvent; - this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, func, true); + // perform hit testing for events targeting our canvas or cancel events + this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, func, cancelled || !eventAppend); - this.emit(cancelled ? 'pointercancel' : 'pointerup', interactionEvent); + this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent); if (event.pointerType === 'mouse') { const isRightButton = event.button === 2 || event.which === 3; - this.emit(isRightButton ? 'rightup' : 'mouseup', interactionEvent); + this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent); } else if (event.pointerType === 'touch') { - this.emit(cancelled ? 'touchcancel' : 'touchend', interactionEvent); + this.emit(cancelled ? 'touchcancel' : `touchend${eventAppend}`, interactionEvent); this.releaseInteractionDataForPointerId(event.pointerId, interactionData); } } diff --git a/test/core/Ticker.js b/test/core/Ticker.js index 188770f..cd8e33c 100644 --- a/test/core/Ticker.js +++ b/test/core/Ticker.js @@ -150,6 +150,32 @@ expect(this.length()).to.equal(length); }); + it('should remove once listener in a stack', function () + { + const length = this.length(); + const listener1 = sinon.spy(); + const listener2 = sinon.spy(); + const listener3 = sinon.spy(); + + shared.add(listener1, null, PIXI.UPDATE_PRIORITY.HIGH); + shared.addOnce(listener2, null, PIXI.UPDATE_PRIORITY.NORMAL); + shared.add(listener3, null, PIXI.UPDATE_PRIORITY.LOW); + + shared.update(); + + expect(this.length()).to.equal(length + 2); + + shared.update(); + + expect(listener1.calledTwice).to.be.true; + expect(listener2.calledOnce).to.be.true; + expect(listener3.calledTwice).to.be.true; + + shared.remove(listener1).remove(listener3); + + expect(this.length()).to.equal(length); + }); + it('should call inserted item with a lower priority', function () { const length = this.length(); diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js index 2bedb34..8d0479d 100644 --- a/src/core/ticker/TickerListener.js +++ b/src/core/ticker/TickerListener.js @@ -91,13 +91,13 @@ this.fn(deltaTime); } + const redirect = this.next; + if (this.once) { - this.destroy(); + this.destroy(true); } - const redirect = this.next; - // Soft-destroying should remove // the next reference if (this._destroyed) diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index e1c50d7..729469c 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -1217,6 +1217,10 @@ const eventLen = events.length; + // if the event wasn't targeting our canvas, then consider it to be pointerupoutside + // in all cases (unless it was a pointercancel) + const eventAppend = originalEvent.target !== this.interactionDOMElement ? 'outside' : ''; + for (let i = 0; i < eventLen; i++) { const event = events[i]; @@ -1227,19 +1231,20 @@ interactionEvent.data.originalEvent = originalEvent; - this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, func, true); + // perform hit testing for events targeting our canvas or cancel events + this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, func, cancelled || !eventAppend); - this.emit(cancelled ? 'pointercancel' : 'pointerup', interactionEvent); + this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent); if (event.pointerType === 'mouse') { const isRightButton = event.button === 2 || event.which === 3; - this.emit(isRightButton ? 'rightup' : 'mouseup', interactionEvent); + this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent); } else if (event.pointerType === 'touch') { - this.emit(cancelled ? 'touchcancel' : 'touchend', interactionEvent); + this.emit(cancelled ? 'touchcancel' : `touchend${eventAppend}`, interactionEvent); this.releaseInteractionDataForPointerId(event.pointerId, interactionData); } } diff --git a/test/core/Ticker.js b/test/core/Ticker.js index 188770f..cd8e33c 100644 --- a/test/core/Ticker.js +++ b/test/core/Ticker.js @@ -150,6 +150,32 @@ expect(this.length()).to.equal(length); }); + it('should remove once listener in a stack', function () + { + const length = this.length(); + const listener1 = sinon.spy(); + const listener2 = sinon.spy(); + const listener3 = sinon.spy(); + + shared.add(listener1, null, PIXI.UPDATE_PRIORITY.HIGH); + shared.addOnce(listener2, null, PIXI.UPDATE_PRIORITY.NORMAL); + shared.add(listener3, null, PIXI.UPDATE_PRIORITY.LOW); + + shared.update(); + + expect(this.length()).to.equal(length + 2); + + shared.update(); + + expect(listener1.calledTwice).to.be.true; + expect(listener2.calledOnce).to.be.true; + expect(listener3.calledTwice).to.be.true; + + shared.remove(listener1).remove(listener3); + + expect(this.length()).to.equal(length); + }); + it('should call inserted item with a lower priority', function () { const length = this.length(); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index 31385b9..de81597 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -71,6 +71,25 @@ expect(eventSpy).to.have.been.called; }); + it('should call mouseupoutside handler on mouseup on different elements', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const eventSpy = sinon.spy(); + const pointer = this.pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('mouseupoutside', eventSpy); + + pointer.mousedown(10, 10); + pointer.mouseup(10, 10, false); + + expect(eventSpy).to.have.been.called; + }); + it('should call mouseover handler', function () { const stage = new PIXI.Container(); diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js index 2bedb34..8d0479d 100644 --- a/src/core/ticker/TickerListener.js +++ b/src/core/ticker/TickerListener.js @@ -91,13 +91,13 @@ this.fn(deltaTime); } + const redirect = this.next; + if (this.once) { - this.destroy(); + this.destroy(true); } - const redirect = this.next; - // Soft-destroying should remove // the next reference if (this._destroyed) diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index e1c50d7..729469c 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -1217,6 +1217,10 @@ const eventLen = events.length; + // if the event wasn't targeting our canvas, then consider it to be pointerupoutside + // in all cases (unless it was a pointercancel) + const eventAppend = originalEvent.target !== this.interactionDOMElement ? 'outside' : ''; + for (let i = 0; i < eventLen; i++) { const event = events[i]; @@ -1227,19 +1231,20 @@ interactionEvent.data.originalEvent = originalEvent; - this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, func, true); + // perform hit testing for events targeting our canvas or cancel events + this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, func, cancelled || !eventAppend); - this.emit(cancelled ? 'pointercancel' : 'pointerup', interactionEvent); + this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent); if (event.pointerType === 'mouse') { const isRightButton = event.button === 2 || event.which === 3; - this.emit(isRightButton ? 'rightup' : 'mouseup', interactionEvent); + this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent); } else if (event.pointerType === 'touch') { - this.emit(cancelled ? 'touchcancel' : 'touchend', interactionEvent); + this.emit(cancelled ? 'touchcancel' : `touchend${eventAppend}`, interactionEvent); this.releaseInteractionDataForPointerId(event.pointerId, interactionData); } } diff --git a/test/core/Ticker.js b/test/core/Ticker.js index 188770f..cd8e33c 100644 --- a/test/core/Ticker.js +++ b/test/core/Ticker.js @@ -150,6 +150,32 @@ expect(this.length()).to.equal(length); }); + it('should remove once listener in a stack', function () + { + const length = this.length(); + const listener1 = sinon.spy(); + const listener2 = sinon.spy(); + const listener3 = sinon.spy(); + + shared.add(listener1, null, PIXI.UPDATE_PRIORITY.HIGH); + shared.addOnce(listener2, null, PIXI.UPDATE_PRIORITY.NORMAL); + shared.add(listener3, null, PIXI.UPDATE_PRIORITY.LOW); + + shared.update(); + + expect(this.length()).to.equal(length + 2); + + shared.update(); + + expect(listener1.calledTwice).to.be.true; + expect(listener2.calledOnce).to.be.true; + expect(listener3.calledTwice).to.be.true; + + shared.remove(listener1).remove(listener3); + + expect(this.length()).to.equal(length); + }); + it('should call inserted item with a lower priority', function () { const length = this.length(); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index 31385b9..de81597 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -71,6 +71,25 @@ expect(eventSpy).to.have.been.called; }); + it('should call mouseupoutside handler on mouseup on different elements', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const eventSpy = sinon.spy(); + const pointer = this.pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('mouseupoutside', eventSpy); + + pointer.mousedown(10, 10); + pointer.mouseup(10, 10, false); + + expect(eventSpy).to.have.been.called; + }); + it('should call mouseover handler', function () { const stage = new PIXI.Container(); diff --git a/test/interaction/MockPointer.js b/test/interaction/MockPointer.js index c9c6228..7403d90 100644 --- a/test/interaction/MockPointer.js +++ b/test/interaction/MockPointer.js @@ -99,6 +99,7 @@ }); } + Object.defineProperty(mouseEvent, 'target', { value: this.renderer.view }); this.setPosition(x, y); this.render(); // mouseOverRenderer state should be correct, so mouse position to view rect @@ -148,6 +149,8 @@ preventDefault: sinon.stub(), }); + Object.defineProperty(mouseEvent, 'target', { value: this.renderer.view }); + this.setPosition(x, y); this.render(); this.interaction.onPointerDown(mouseEvent); @@ -156,8 +159,9 @@ /** * @param {number} x - pointer x position * @param {number} y - pointer y position + * @param {boolean} [onCanvas=true] - if the event happend on the Canvas element or not */ - mouseup(x, y) + mouseup(x, y, onCanvas = true) { const mouseEvent = new MouseEvent('mouseup', { clientX: x, @@ -165,6 +169,11 @@ preventDefault: sinon.stub(), }); + if (onCanvas) + { + Object.defineProperty(mouseEvent, 'target', { value: this.renderer.view }); + } + this.setPosition(x, y); this.render(); this.interaction.onPointerUp(mouseEvent); @@ -195,6 +204,8 @@ ], }); + Object.defineProperty(touchEvent, 'target', { value: this.renderer.view }); + this.setPosition(x, y); this.render(); this.interaction.onPointerDown(touchEvent); @@ -214,6 +225,8 @@ ], }); + Object.defineProperty(touchEvent, 'target', { value: this.renderer.view }); + this.setPosition(x, y); this.render(); this.interaction.onPointerUp(touchEvent); @@ -233,6 +246,8 @@ ], }); + Object.defineProperty(touchEvent, 'target', { value: this.renderer.view }); + this.setPosition(x, y); this.render(); this.interaction.onPointerOut(touchEvent);