From b69af383a63060565bc4d08c500ffe95f9b9b52e Mon Sep 17 00:00:00 2001 From: Sam El-Husseini <16690124+samelhusseini@users.noreply.github.com> Date: Wed, 2 May 2018 13:54:06 -0700 Subject: [PATCH] Better mouse event handling (#552) * Use learnings from touch events in Blockly and apply to the Ev3 sim. Register move and up events on the document rather than the individual element allowing users greater flexibility where they move their mouse once it's down. * Add mouse leave events --- sim/visuals/controls/colorWheel.ts | 3 - sim/visuals/controls/distanceSlider.ts | 3 - sim/visuals/controls/motorSlider.ts | 3 - sim/visuals/controls/proximitySlider.ts | 3 - sim/visuals/controls/rotationSlider.ts | 2 - sim/visuals/util.ts | 84 ++++++++++++++++++++----- 6 files changed, 68 insertions(+), 30 deletions(-) diff --git a/sim/visuals/controls/colorWheel.ts b/sim/visuals/controls/colorWheel.ts index 7748e7e2..4971ac0b 100644 --- a/sim/visuals/controls/colorWheel.ts +++ b/sim/visuals/controls/colorWheel.ts @@ -93,9 +93,6 @@ namespace pxsim.visuals { }, () => { captured = false; rect.setAttribute('cursor', '-webkit-grab'); - }, () => { - captured = false; - rect.setAttribute('cursor', '-webkit-grab'); }) return this.group; diff --git a/sim/visuals/controls/distanceSlider.ts b/sim/visuals/controls/distanceSlider.ts index dc81513e..33290d54 100644 --- a/sim/visuals/controls/distanceSlider.ts +++ b/sim/visuals/controls/distanceSlider.ts @@ -67,9 +67,6 @@ namespace pxsim.visuals { }, () => { captured = false; dragSurface.setAttribute('cursor', '-webkit-grab'); - }, () => { - captured = false; - dragSurface.setAttribute('cursor', '-webkit-grab'); }) return this.group; diff --git a/sim/visuals/controls/motorSlider.ts b/sim/visuals/controls/motorSlider.ts index aae87b99..d94cabb5 100644 --- a/sim/visuals/controls/motorSlider.ts +++ b/sim/visuals/controls/motorSlider.ts @@ -66,9 +66,6 @@ namespace pxsim.visuals { }, () => { captured = false; this.handleSliderUp(); - }, () => { - captured = false; - this.handleSliderUp(); }) return this.group; diff --git a/sim/visuals/controls/proximitySlider.ts b/sim/visuals/controls/proximitySlider.ts index 19afa077..a2af16fc 100644 --- a/sim/visuals/controls/proximitySlider.ts +++ b/sim/visuals/controls/proximitySlider.ts @@ -66,9 +66,6 @@ namespace pxsim.visuals { }, () => { captured = false; dragSurface.setAttribute('cursor', '-webkit-grab'); - }, () => { - captured = false; - dragSurface.setAttribute('cursor', '-webkit-grab'); }) return this.group; diff --git a/sim/visuals/controls/rotationSlider.ts b/sim/visuals/controls/rotationSlider.ts index 760df3a8..a772d340 100644 --- a/sim/visuals/controls/rotationSlider.ts +++ b/sim/visuals/controls/rotationSlider.ts @@ -47,8 +47,6 @@ namespace pxsim.visuals { } }, () => { captured = false; - }, () => { - captured = false; }) return this.group; diff --git a/sim/visuals/util.ts b/sim/visuals/util.ts index af77e9fe..7e3368b5 100644 --- a/sim/visuals/util.ts +++ b/sim/visuals/util.ts @@ -10,33 +10,85 @@ namespace pxsim.visuals { export type TouchCallback = (event: MouseEvent | TouchEvent | PointerEvent) => void; - export function touchEvents(e: SVGElement | SVGElement[], move?: TouchCallback, down?: TouchCallback, up?: TouchCallback, leave?: TouchCallback) { + export function touchEvents(e: SVGElement | SVGElement[], move?: TouchCallback, down?: TouchCallback, up?: TouchCallback) { if (Array.isArray(e)) { - e.forEach(el => bindEvents(el, move, down, up, leave)); + e.forEach(el => bindEvents(el, move, down, up)); } else { - bindEvents(e, move, down, up, leave); + bindEvents(e, move, down, up); } } - function bindEvents(e: SVGElement, move?: TouchCallback, down?: TouchCallback, up?: TouchCallback, leave?: TouchCallback) { + function bindEvents(e: SVGElement, move?: TouchCallback, down?: TouchCallback, up?: TouchCallback) { + + const moveEvent = move ? (ev: MouseEvent) => { + move.call(this, ev); + ev.preventDefault(); + ev.stopPropagation(); + } : undefined; + + const enterEvent = move ? (ev: MouseEvent) => { + if (ev.buttons != 1) { + // cancel all events when we re-enter without a button down + upEvent(ev); + } + } : undefined; + + const upEvent = up ? (ev: MouseEvent) => { + up.call(this, ev); + ev.preventDefault(); + ev.stopPropagation(); + + // Unregister document up and move events + if ((window as any).PointerEvent) { + if (moveEvent) document.removeEventListener("pointermove", moveEvent); + if (upEvent) document.removeEventListener("pointerup", upEvent); + if (upEvent) document.removeEventListener("pointercancel", upEvent); + if (moveEvent) document.removeEventListener("pointerenter", enterEvent); + } else { + if (moveEvent) document.removeEventListener("mousemove", moveEvent); + if (upEvent) document.removeEventListener("mouseup", upEvent); + if (moveEvent) document.removeEventListener("mouseenter", enterEvent); + if (pxsim.svg.isTouchEnabled()) { + if (moveEvent) document.removeEventListener("touchmove", moveEvent); + if (upEvent) document.removeEventListener("touchend", upEvent); + if (upEvent) document.removeEventListener("touchcancel", upEvent); + } + } + } : undefined; + + const downEvent = down ? (ev: MouseEvent) => { + down.call(this, ev); + ev.preventDefault(); + ev.stopPropagation(); + + // Register document up and move events + if ((window as any).PointerEvent) { + if (moveEvent) document.addEventListener("pointermove", moveEvent); + if (upEvent) document.addEventListener("pointerup", upEvent); + if (upEvent) document.addEventListener("pointercancel", upEvent); + if (moveEvent) document.addEventListener("pointerenter", enterEvent); + } else { + if (moveEvent) document.addEventListener("mousemove", moveEvent); + if (upEvent) document.addEventListener("mouseup", upEvent); + if (moveEvent) document.addEventListener("mouseenter", enterEvent); + + if (pxsim.svg.isTouchEnabled()) { + if (moveEvent) document.addEventListener("touchmove", moveEvent); + if (upEvent) document.addEventListener("touchend", upEvent); + if (upEvent) document.addEventListener("touchcancel", upEvent); + } + } + } : undefined; + if ((window as any).PointerEvent) { - if (down) e.addEventListener("pointerdown", down); - if (up) e.addEventListener("pointerup", up); - if (leave) e.addEventListener("pointerleave", leave); - if (move) e.addEventListener("pointermove", move); + if (downEvent) e.addEventListener("pointerdown", downEvent); } else { - if (down) e.addEventListener("mousedown", down); - if (up) e.addEventListener("mouseup", up); - if (leave) e.addEventListener("mouseleave", leave); - if (move) e.addEventListener("mousemove", move); + if (downEvent) e.addEventListener("mousedown", downEvent); if (pxsim.svg.isTouchEnabled()) { - if (down) e.addEventListener("touchstart", down); - if (up) e.addEventListener("touchend", up); - if (leave) e.addEventListener("touchcancel", leave); - if (move) e.addEventListener("touchmove", move); + if (downEvent) e.addEventListener("touchstart", downEvent); } } }