From 7e73e9d7c99f4ed56a41017f5e4c9f7d55cc6964 Mon Sep 17 00:00:00 2001 From: jdotzki Date: Mon, 7 Jul 2014 17:17:41 +0200 Subject: [PATCH] feat(features/label-editing): add touch based editing Closes #84 --- .../label-editing/LabelEditingProvider.js | 9 +- lib/features/touch/TouchInteraction.js | 286 ++++++++++-------- lib/features/touch/index.js | 3 + .../label-editing/LabelEditingProviderSpec.js | 27 +- .../label-editing/TouchIntegrationSpec.js | 55 ++++ 5 files changed, 243 insertions(+), 137 deletions(-) create mode 100644 test/spec/browser/features/label-editing/TouchIntegrationSpec.js diff --git a/lib/features/label-editing/LabelEditingProvider.js b/lib/features/label-editing/LabelEditingProvider.js index 2e31c4c7..845beee8 100644 --- a/lib/features/label-editing/LabelEditingProvider.js +++ b/lib/features/label-editing/LabelEditingProvider.js @@ -4,9 +4,8 @@ var _ = require('lodash'); var UpdateTextHandler = require('./cmd/UpdateTextHandler'); -var LabelUtil = require('./LabelUtil'); - -var DiUtil = require('../../util/Di'); +var LabelUtil = require('./LabelUtil'), + DiUtil = require('../../util/Di'); var minBounds = { @@ -20,7 +19,6 @@ function LabelEditingProvider(eventBus, canvas, directEditing, commandStack, bpm directEditing.registerProvider(this); commandStack.registerHandler('bpmnElement.updateText', UpdateTextHandler); - // per default, listen to double click events eventBus.on('shape.dblclick', function(event) { directEditing.activate(event.element); @@ -31,8 +29,7 @@ function LabelEditingProvider(eventBus, canvas, directEditing, commandStack, bpm directEditing.activate(event.element); }); - // intercept direct canvas clicks to deselect all - // selected shapes + // intercept direct canvas clicks to deselect all selected shapes eventBus.on('canvas.click', function(event) { directEditing.complete(); }); diff --git a/lib/features/touch/TouchInteraction.js b/lib/features/touch/TouchInteraction.js index 7ecb0301..d2ccb572 100644 --- a/lib/features/touch/TouchInteraction.js +++ b/lib/features/touch/TouchInteraction.js @@ -5,7 +5,136 @@ var _ = require('lodash'); function TouchInteraction(eventBus, canvas) { + var context; + var RANGE = { min: 0.2, max: 4 }; + var DOUBLE_TAP_THRESHOLD = 300; // ms + + function handleTouchStart(event) { + + var orgEvent = event.originalEvent ? event.originalEvent : event; + + _.forEach(orgEvent.changedTouches, function (touch) { + context.start[touch.identifier] = touch; + }); + + if (orgEvent.touches.length === 1) { + initMove(orgEvent); + } + + if (orgEvent.touches.length === 2) { + initScale(orgEvent); + } + + //Stop all interaction for more than 2 touch sources + if (orgEvent.touches.length > 2) { + contextInit(); + } + + event.preventDefault(); + } + + function handleTouchMove(event) { + var orgEvent = event.originalEvent; + var cX = 0; + var cY = 0; + var mX = 0; + var mY = 0; + + if (context.move && !context.scale) { + + _.forEach(orgEvent.changedTouches, function (touch) { + if (touch.identifier === context.moveId) { + cX = touch.clientX; + cY = touch.clientY; + } + }); + + if (!context.lasttouch.cX) { + context.lasttouch.cX = cX; + } + + if (!context.lasttouch.cY) { + context.lasttouch.cY = cY; + } + + mX = cX - context.lasttouch.cX; + mY = cY - context.lasttouch.cY; + + canvas.scroll({ + dx: mX, + dy: mY + }); + + context.lasttouch.cX = cX; + context.lasttouch.cY = cY; + } + + if (context.scale) { + var dist = euclideanDistance(orgEvent.touches[0], orgEvent.touches[1]); + var distRatio = dist / context.start.dist; + + zoom(distRatio * context.start.zoom, context.start.mid); + context.lastDistance = dist; + } + + // prevent select + event.preventDefault(); + } + + function handleTouchEnd(event) { + + var orgEvent = event.originalEvent; + + if (orgEvent.touches.length < 1) { + contextInit(); + } + if (orgEvent.touches.length === 1) { + initMove(orgEvent); + } + if (orgEvent.touches.length === 2) { + initScale(orgEvent); + } + + _.forEach(orgEvent.changedTouches, function (touch) { + delete context[touch.identifier]; + }); + + // prevent select + event.preventDefault(); + } + + function init(element) { + + contextInit(); + + //--- general handlers + $(element).on('touchstart', handleTouchStart); + $(element).on('touchmove', handleTouchMove); + $(document).on('touchend', handleTouchEnd); + + + //--- snap handler + + eventBus.on('shape.touchstart', function(event) { + // --- + // TODO must forwarded to touchstart???# + handleTouchStart(event); + }); + + eventBus.on('shape.touchend', function(event) { + + if(event.touches.length !== 0) { + return; + } + + if((Date.now() - context.lastTap.time) < DOUBLE_TAP_THRESHOLD) { + eventBus.fire('shape.dbltap', event); + } + + context.lastTap.time = Date.now(); + }); + } function cap(scale) { return Math.max(RANGE.min, Math.min(RANGE.max, scale)); @@ -27,139 +156,36 @@ function TouchInteraction(eventBus, canvas) { return dist; } - function init(element) { - - function contextInit() { - context = { - start: {} + function initMove(orgEvent) { + context.move = true; + context.moveId = orgEvent.touches[0].identifier; + context.scale = false; + if (!context.lasttouch) { + context.lasttouch = { + cX: undefined, + cY: undefined, + mX: 0, + mY: 0 }; } + } - var context; - contextInit(); + function initScale(orgEvent) { + context.scale = true; + context.start.dist = euclideanDistance(orgEvent.touches[0], orgEvent.touches[1]); + context.lastDistance = context.start.dist; + context.start.zoom = canvas.zoom(); + context.start.mid = { + x: (orgEvent.touches[0].clientX + orgEvent.touches[1].clientX) / 2, + y: (orgEvent.touches[0].clientY + orgEvent.touches[1].clientY) / 2 + }; + } - function handleTouchMove(event) { - var orgEvent = event.originalEvent; - var cX = 0; - var cY = 0; - var mX = 0; - var mY = 0; - - if (context.move && !context.scale) { - - _.forEach(orgEvent.changedTouches, function (touch) { - if (touch.identifier === context.moveId) { - cX = touch.clientX; - cY = touch.clientY; - } - }); - - if (!context.lasttouch.cX) { - context.lasttouch.cX = cX; - } - - if (!context.lasttouch.cY) { - context.lasttouch.cY = cY; - } - - mX = cX - context.lasttouch.cX; - mY = cY - context.lasttouch.cY; - - canvas.scroll({ - dx: mX, - dy: mY - }); - - context.lasttouch.cX = cX; - context.lasttouch.cY = cY; - } - - if (context.scale) { - var dist = euclideanDistance(orgEvent.touches[0], orgEvent.touches[1]); - var distRatio = dist / context.start.dist; - - zoom(distRatio * context.start.zoom, context.start.mid); - context.lastDistance = dist; - } - - // prevent select - event.preventDefault(); - } - - function handleTouchEnd(event) { - - var orgEvent = event.originalEvent; - - if (orgEvent.touches.length < 1) { - contextInit(); - } - if (orgEvent.touches.length === 1) { - initMove(orgEvent); - } - if (orgEvent.touches.length === 2) { - initScale(orgEvent); - } - - _.forEach(orgEvent.changedTouches, function (touch) { - delete context[touch.identifier]; - }); - - // prevent select - event.preventDefault(); - } - - function initMove(orgEvent) { - context.move = true; - context.moveId = orgEvent.touches[0].identifier; - context.scale = false; - if (!context.lasttouch) { - context.lasttouch = { - cX: undefined, - cY: undefined, - mX: 0, - mY: 0 - }; - } - } - - function initScale(orgEvent) { - context.scale = true; - context.start.dist = euclideanDistance(orgEvent.touches[0], orgEvent.touches[1]); - context.lastDistance = context.start.dist; - context.start.zoom = canvas.zoom(); - context.start.mid = { - x: (orgEvent.touches[0].clientX + orgEvent.touches[1].clientX) / 2, - y: (orgEvent.touches[0].clientY + orgEvent.touches[1].clientY) / 2 - }; - } - - function handleTouchStart(event) { - - var orgEvent = event.originalEvent; - - _.forEach(orgEvent.changedTouches, function (touch) { - context.start[touch.identifier] = touch; - }); - - if (orgEvent.touches.length === 1) { - initMove(orgEvent); - } - - if (orgEvent.touches.length === 2) { - initScale(orgEvent); - } - - //Stop all interaction for more than 2 touch sources - if (orgEvent.touches.length > 2) { - contextInit(); - } - - event.preventDefault(); - } - - $(element).on('touchstart', handleTouchStart); - $(element).on('touchmove', handleTouchMove); - $(document).on('touchend', handleTouchEnd); + function contextInit() { + context = { + start: {}, + lastTap: {} + }; } eventBus.on('canvas.init', function(e) { @@ -167,6 +193,6 @@ function TouchInteraction(eventBus, canvas) { }); } -TouchInteraction.$inject = ['eventBus', 'canvas']; +TouchInteraction.$inject = [ 'eventBus', 'canvas', 'touchFix' ]; module.exports = TouchInteraction; \ No newline at end of file diff --git a/lib/features/touch/index.js b/lib/features/touch/index.js index 86309fa9..605aa198 100644 --- a/lib/features/touch/index.js +++ b/lib/features/touch/index.js @@ -1,4 +1,7 @@ module.exports = { + __depends__: [ + require('diagram-js/lib/features/touch') + ], __init__: [ 'touchInteraction' ], touchInteraction: [ 'type', require('./TouchInteraction') ] }; \ No newline at end of file diff --git a/test/spec/browser/features/label-editing/LabelEditingProviderSpec.js b/test/spec/browser/features/label-editing/LabelEditingProviderSpec.js index 3343bf7d..334880d2 100644 --- a/test/spec/browser/features/label-editing/LabelEditingProviderSpec.js +++ b/test/spec/browser/features/label-editing/LabelEditingProviderSpec.js @@ -44,7 +44,7 @@ describe('features - label-editing', function() { })); - it('should cancel on ESC', inject(function(elementRegistry, bpmnRegistry, directEditing, eventBus) { + it('should cancel on ', inject(function(elementRegistry, bpmnRegistry, directEditing, eventBus) { // given var shape = elementRegistry.getById('task-nested-embedded'); @@ -68,6 +68,31 @@ describe('features - label-editing', function() { expect(task.name).toBe(oldName); })); + + it('should submit on ', inject(function(elementRegistry, bpmnRegistry, directEditing, eventBus) { + + // given + var shape = elementRegistry.getById('task-nested-embedded'); + var task = bpmnRegistry.getSemantic('task-nested-embedded'); + + // activate + eventBus.fire('shape.dblclick', { element: shape }); + + var newName = 'new value'; + + // a jQuery