feat(features/label-editing): add touch based editing

Closes #84
This commit is contained in:
jdotzki 2014-07-07 17:17:41 +02:00 committed by Nico Rehwaldt
parent 1aa431ca36
commit 7e73e9d7c9
5 changed files with 243 additions and 137 deletions

View File

@ -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();
});

View File

@ -5,38 +5,34 @@ var _ = require('lodash');
function TouchInteraction(eventBus, canvas) {
var RANGE = { min: 0.2, max: 4 };
function cap(scale) {
return Math.max(RANGE.min, Math.min(RANGE.max, scale));
}
function zoom(pinchRatio, position) {
canvas.zoom(cap(pinchRatio), position);
}
function euclideanDistance(touch1, touch2) {
var dist =
Math.sqrt(
(touch1.clientX - touch2.clientX) *
(touch1.clientX - touch2.clientX) +
(touch1.clientY - touch2.clientY) *
(touch1.clientY - touch2.clientY)
);
return dist;
}
function init(element) {
function contextInit() {
context = {
start: {}
};
}
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;
@ -108,6 +104,58 @@ function TouchInteraction(eventBus, canvas) {
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));
}
function zoom(pinchRatio, position) {
canvas.zoom(cap(pinchRatio), position);
}
function euclideanDistance(touch1, touch2) {
var dist =
Math.sqrt(
(touch1.clientX - touch2.clientX) *
(touch1.clientX - touch2.clientX) +
(touch1.clientY - touch2.clientY) *
(touch1.clientY - touch2.clientY)
);
return dist;
}
function initMove(orgEvent) {
context.move = true;
context.moveId = orgEvent.touches[0].identifier;
@ -133,33 +181,11 @@ function TouchInteraction(eventBus, canvas) {
};
}
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;

View File

@ -1,4 +1,7 @@
module.exports = {
__depends__: [
require('diagram-js/lib/features/touch')
],
__init__: [ 'touchInteraction' ],
touchInteraction: [ 'type', require('./TouchInteraction') ]
};

View File

@ -44,7 +44,7 @@ describe('features - label-editing', function() {
}));
it('should cancel on ESC', inject(function(elementRegistry, bpmnRegistry, directEditing, eventBus) {
it('should cancel on <ESC>', 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 <canvas.click>', 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 <textarea /> element
var textarea = directEditing._textbox.textarea;
// when
// change + <canvas.click>
textarea.val(newName);
eventBus.fire('canvas.click', {});
// then
expect(directEditing.isActive()).toBe(false);
expect(task.name).toBe(newName);
}));
});

View File

@ -0,0 +1,55 @@
'use strict';
var Matchers = require('../../../Matchers'),
TestHelper = require('../../../TestHelper');
/* global bootstrapBpmnJS, inject */
var fs = require('fs');
var Modeler = require('../../../../../lib/Modeler');
var labelEditingModule = require('../../../../../lib/features/label-editing'),
touchModule = require('diagram-js/lib/features/touch');
describe('direct editing - touch integration', function() {
beforeEach(Matchers.add);
var container;
beforeEach(function() {
container = jasmine.getEnv().getTestContainer();
});
function createModeler(xml, done) {
var modeler = new Modeler({ container: container });
modeler.importXML(xml, function(err) {
done(err, modeler);
});
}
it('should work on modeler (manual test)', function(done) {
var xml = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf8');
createModeler(xml, done);
});
describe('event integration', function() {
var diagramXML = fs.readFileSync('test/fixtures/bpmn/features/label-editing/labels.bpmn', 'utf-8');
var testModules = [ labelEditingModule, touchModule ];
beforeEach(bootstrapBpmnJS(diagramXML, { modules: testModules }));
it('should work via dbltap (manual test)', function() { });
});
});