From a6e0a8a1838bffd675d7f249cc1e270b7400c09f Mon Sep 17 00:00:00 2001 From: abe33 Date: Wed, 15 Jul 2015 15:50:00 +0200 Subject: [PATCH] Add support for touch events for visible area dragging Closes #362 --- lib/minimap-element.coffee | 18 ++++++++++++----- spec/helpers/events.coffee | 34 ++++++++++++++++++++++++++++++++ spec/minimap-element-spec.coffee | 30 +++++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/lib/minimap-element.coffee b/lib/minimap-element.coffee index 954a8d36..d4a04bbd 100644 --- a/lib/minimap-element.coffee +++ b/lib/minimap-element.coffee @@ -181,11 +181,13 @@ class MinimapElement extends HTMLElement @addEventListener 'mousewheel', elementMousewheel @canvas.addEventListener 'mousedown', canvasMousedown @visibleArea.addEventListener 'mousedown', visibleAreaMousedown + @visibleArea.addEventListener 'touchstart', visibleAreaMousedown @subscriptions.add new Disposable => @removeEventListener 'mousewheel', elementMousewheel @canvas.removeEventListener 'mousedown', canvasMousedown @visibleArea.removeEventListener 'mousedown', visibleAreaMousedown + @visibleArea.removeEventListener 'touchstart', visibleAreaMousedown # Initializes the scroll indicator div when the `minimapScrollIndicator` # settings is enabled. @@ -512,11 +514,11 @@ class MinimapElement extends HTMLElement # area that starts the dragging gesture. # # event - The {Event} object. - startDrag: ({which, pageY}) -> - # if which is 2 - # @middleMousePressedOverCanvas({pageY}) + startDrag: (e) -> + {which, pageY} = e return unless @minimap - return if which isnt 1 and which isnt 2 + return if which isnt 1 and which isnt 2 and not e.touches? + {top} = @visibleArea.getBoundingClientRect() {top: offsetTop} = @getBoundingClientRect() @@ -531,11 +533,17 @@ class MinimapElement extends HTMLElement document.body.addEventListener('mouseup', mouseupHandler) document.body.addEventListener('mouseleave', mouseupHandler) + document.body.addEventListener('touchmove', mousemoveHandler) + document.body.addEventListener('touchend', mouseupHandler) + @dragSubscription = new Disposable -> document.body.removeEventListener('mousemove', mousemoveHandler) document.body.removeEventListener('mouseup', mouseupHandler) document.body.removeEventListener('mouseleave', mouseupHandler) + document.body.removeEventListener('touchmove', mousemoveHandler) + document.body.removeEventListener('touchend', mouseupHandler) + # Internal: The method called during the drag gesture. # # e - The {Event} object. @@ -546,7 +554,7 @@ class MinimapElement extends HTMLElement # drag start. drag: (e, initial) -> return unless @minimap - return if e.which isnt 1 and e.which isnt 2 + return if e.which isnt 1 and e.which isnt 2 and not e.touches? y = e.pageY - initial.offsetTop - initial.dragOffset ratio = y / (@minimap.getVisibleHeight() - @minimap.getTextEditorScaledHeight()) diff --git a/spec/helpers/events.coffee b/spec/helpers/events.coffee index 61d46416..526b670b 100644 --- a/spec/helpers/events.coffee +++ b/spec/helpers/events.coffee @@ -21,6 +21,28 @@ mouseEvent = (type, properties) -> new MouseEvent type, properties +touchEvent = (type, touches) -> + firstTouch = touches[0] + + properties = { + bubbles: true + cancelable: true + view: window + ctrlKey: false + altKey: false + shiftKey: false + metaKey: false + relatedTarget: undefined + } + + e = new Event(type, properties) + e.pageX = firstTouch.pageX + e.pageY = firstTouch.pageY + e.clientX = firstTouch.clientX + e.clientY = firstTouch.clientY + e.touches = e.targetTouches = e.changedTouches = touches + e + objectCenterCoordinates = (obj) -> {top, left, width, height} = obj.getBoundingClientRect() {x: left + width / 2, y: top + height / 2} @@ -40,3 +62,15 @@ module.exports = {objectCenterCoordinates, mouseEvent} module.exports.mousewheel = (obj, deltaX=0, deltaY=0) -> obj.dispatchEvent(mouseEvent 'mousewheel', {deltaX, deltaY}) + +['touchstart', 'touchmove', 'touchend'].forEach (key) -> + module.exports[key] = (obj, {x, y, cx, cy} = {}) -> + {x,y} = objectCenterCoordinates(obj) unless x? and y? + + unless cx? and cy? + cx = x + cy = y + + obj.dispatchEvent(touchEvent key, [ + {pageX: x, pageY: y, clientX: cx, clientY: cy} + ]) diff --git a/spec/minimap-element-spec.coffee b/spec/minimap-element-spec.coffee index e37c75c8..238a8958 100644 --- a/spec/minimap-element-spec.coffee +++ b/spec/minimap-element-spec.coffee @@ -3,7 +3,7 @@ path = require 'path' {TextEditor} = require 'atom' Minimap = require '../lib/minimap' MinimapElement = require '../lib/minimap-element' -{mousemove, mousedown, mouseup, mousewheel} = require './helpers/events' +{mousemove, mousedown, mouseup, mousewheel, touchstart, touchmove} = require './helpers/events' stylesheetPath = path.resolve __dirname, '..', 'styles', 'minimap.less' stylesheet = atom.themes.loadStylesheet(stylesheetPath) @@ -491,6 +491,34 @@ describe 'MinimapElement', -> expect(minimapElement.drag).not.toHaveBeenCalled() + describe 'dragging the visible area using touch events', -> + [visibleArea, originalTop] = [] + + beforeEach -> + visibleArea = minimapElement.visibleArea + {top: originalTop, left} = visibleArea.getBoundingClientRect() + + touchstart(visibleArea, x: left + 10, y: originalTop + 10) + touchmove(visibleArea, x: left + 10, y: originalTop + 50) + + nextAnimationFrame() + + afterEach -> + minimapElement.endDrag() + + it 'scrolls the editor so that the visible area was moved down by 40 pixels', -> + {top} = visibleArea.getBoundingClientRect() + expect(top).toBeCloseTo(originalTop + 40, -1) + + it 'stops the drag gesture when the mouse is released outside the minimap', -> + {top, left} = visibleArea.getBoundingClientRect() + mouseup(jasmineContent, x: left - 10, y: top + 80) + + spyOn(minimapElement, 'drag') + touchmove(visibleArea, x: left + 10, y: top + 50) + + expect(minimapElement.drag).not.toHaveBeenCalled() + describe 'when the minimap cannot scroll', -> [visibleArea, originalTop] = []