Add Double-Click/Tap Gesture to MouseTracker (#300)

Fix for enhancement #300
Added double-click/tap gesture detection to MouseTracker.with
corresponding dblClickHandler event callback.
Added unit test for double-click detection.
Added Viewer dblClickHandler handling to optionally zoom on
double-click.
This commit is contained in:
Mark Salsbery 2014-04-22 09:23:56 -07:00
parent 7ae0452546
commit 7567a657bd
5 changed files with 289 additions and 78 deletions

View File

@ -40,10 +40,8 @@
/** /**
* @class MouseTracker * @class MouseTracker
* @classdesc Provides simplified handling of common pointer device (mouse, touch, pen, etc.) and keyboard * @classdesc Provides simplified handling of common pointer device (mouse, touch, pen, etc.) gestures
* events on a specific element, like 'enter', 'exit', 'press', 'release', * and keyboard events on a specified element.
* 'scroll', 'click', and 'drag'.
*
* @memberof OpenSeadragon * @memberof OpenSeadragon
* @param {Object} options * @param {Object} options
* Allows configurable properties to be entirely specified by passing * Allows configurable properties to be entirely specified by passing
@ -54,11 +52,17 @@
* A reference to an element or an element id for which the pointer/key * A reference to an element or an element id for which the pointer/key
* events will be monitored. * events will be monitored.
* @param {Number} options.clickTimeThreshold * @param {Number} options.clickTimeThreshold
* The number of milliseconds within which multiple pointer clicks * The number of milliseconds within which a pointer down-up event combination
* will be treated as a single event. * will be treated as a click gesture.
* @param {Number} options.clickDistThreshold * @param {Number} options.clickDistThreshold
* The distance between pointer click within multiple pointer clicks * The maximum distance allowed between a pointer down event and a pointer up event
* will be treated as a single event. * to be treated as a click gesture.
* @param {Number} options.dblClickTimeThreshold
* The number of milliseconds within which two pointer down-up event combinations
* will be treated as a double-click gesture.
* @param {Number} options.dblClickDistThreshold
* The maximum distance allowed between two pointer click events
* to be treated as a click gesture.
* @param {Number} [options.stopDelay=50] * @param {Number} [options.stopDelay=50]
* The number of milliseconds without pointer move before the stop * The number of milliseconds without pointer move before the stop
* event is fired. * event is fired.
@ -76,6 +80,8 @@
* An optional handler for mouse wheel scroll. * An optional handler for mouse wheel scroll.
* @param {OpenSeadragon.EventHandler} [options.clickHandler=null] * @param {OpenSeadragon.EventHandler} [options.clickHandler=null]
* An optional handler for pointer click. * An optional handler for pointer click.
* @param {OpenSeadragon.EventHandler} [options.dblClickHandler=null]
* An optional handler for pointer double-click.
* @param {OpenSeadragon.EventHandler} [options.dragHandler=null] * @param {OpenSeadragon.EventHandler} [options.dragHandler=null]
* An optional handler for the drag gesture. * An optional handler for the drag gesture.
* @param {OpenSeadragon.EventHandler} [options.dragEndHandler=null] * @param {OpenSeadragon.EventHandler} [options.dragEndHandler=null]
@ -111,34 +117,51 @@
*/ */
this.element = $.getElement( options.element ); this.element = $.getElement( options.element );
/** /**
* The number of milliseconds within which mutliple pointer clicks will be treated as a single event. * The number of milliseconds within which a pointer down-up event combination
* will be treated as a click gesture.
* @member {Number} clickTimeThreshold * @member {Number} clickTimeThreshold
* @memberof OpenSeadragon.MouseTracker# * @memberof OpenSeadragon.MouseTracker#
*/ */
this.clickTimeThreshold = options.clickTimeThreshold; this.clickTimeThreshold = options.clickTimeThreshold;
/** /**
* The distance between pointer click within multiple pointer clicks will be treated as a single event. * The maximum distance allowed between a pointer down event and a pointer up event
* to be treated as a click gesture.
* @member {Number} clickDistThreshold * @member {Number} clickDistThreshold
* @memberof OpenSeadragon.MouseTracker# * @memberof OpenSeadragon.MouseTracker#
*/ */
this.clickDistThreshold = options.clickDistThreshold; this.clickDistThreshold = options.clickDistThreshold;
this.userData = options.userData || null; /**
this.stopDelay = options.stopDelay || 50; * The number of milliseconds within which two pointer down-up event combinations
* will be treated as a double-click gesture.
* @member {Number} dblClickTimeThreshold
* @memberof OpenSeadragon.MouseTracker#
*/
this.dblClickTimeThreshold = options.dblClickTimeThreshold;
/**
* The maximum distance allowed between two pointer click events
* to be treated as a click gesture.
* @member {Number} clickDistThreshold
* @memberof OpenSeadragon.MouseTracker#
*/
this.dblClickDistThreshold = options.dblClickDistThreshold;
this.userData = options.userData || null;
this.stopDelay = options.stopDelay || 50;
this.enterHandler = options.enterHandler || null; this.enterHandler = options.enterHandler || null;
this.exitHandler = options.exitHandler || null; this.exitHandler = options.exitHandler || null;
this.pressHandler = options.pressHandler || null; this.pressHandler = options.pressHandler || null;
this.releaseHandler = options.releaseHandler || null; this.releaseHandler = options.releaseHandler || null;
this.moveHandler = options.moveHandler || null; this.moveHandler = options.moveHandler || null;
this.scrollHandler = options.scrollHandler || null; this.scrollHandler = options.scrollHandler || null;
this.clickHandler = options.clickHandler || null; this.clickHandler = options.clickHandler || null;
this.dragHandler = options.dragHandler || null; this.dblClickHandler = options.dblClickHandler || null;
this.dragEndHandler = options.dragEndHandler || null; this.dragHandler = options.dragHandler || null;
this.pinchHandler = options.pinchHandler || null; this.dragEndHandler = options.dragEndHandler || null;
this.stopHandler = options.stopHandler || null; this.pinchHandler = options.pinchHandler || null;
this.keyHandler = options.keyHandler || null; this.stopHandler = options.stopHandler || null;
this.focusHandler = options.focusHandler || null; this.keyHandler = options.keyHandler || null;
this.blurHandler = options.blurHandler || null; this.focusHandler = options.focusHandler || null;
this.blurHandler = options.blurHandler || null;
//Store private properties in a scope sealed hash map //Store private properties in a scope sealed hash map
var _this = this; var _this = this;
@ -154,6 +177,7 @@
setCaptureCapable: !!this.element.setCapture && !!this.element.releaseCapture, setCaptureCapable: !!this.element.setCapture && !!this.element.releaseCapture,
click: function ( event ) { onClick( _this, event ); }, click: function ( event ) { onClick( _this, event ); },
dblclick: function ( event ) { onDblClick( _this, event ); },
keypress: function ( event ) { onKeyPress( _this, event ); }, keypress: function ( event ) { onKeyPress( _this, event ); },
focus: function ( event ) { onFocus( _this, event ); }, focus: function ( event ) { onFocus( _this, event ); },
blur: function ( event ) { onBlur( _this, event ); }, blur: function ( event ) { onBlur( _this, event ); },
@ -207,6 +231,10 @@
// Legacy mouse event tracking // Legacy mouse event tracking
capturing: false, capturing: false,
// Tracking for double-click gesture
lastClickPos: null,
dblClickTimeOut: null,
// Tracking for pinch gesture // Tracking for pinch gesture
pinchGPoints: [], pinchGPoints: [],
lastPinchDist: 0, lastPinchDist: 0,
@ -456,7 +484,7 @@
* @param {OpenSeadragon.Point} event.position * @param {OpenSeadragon.Point} event.position
* The position of the event relative to the tracked element. * The position of the event relative to the tracked element.
* @param {Boolean} event.quick * @param {Boolean} event.quick
* True only if the clickDistThreshold and clickDeltaThreshold are both passed. Useful for ignoring events. * True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for ignoring drag events.
* @param {Boolean} event.shift * @param {Boolean} event.shift
* True if the shift key was pressed during this event. * True if the shift key was pressed during this event.
* @param {Boolean} event.isTouchEvent * @param {Boolean} event.isTouchEvent
@ -470,6 +498,30 @@
*/ */
clickHandler: function () { }, clickHandler: function () { },
/**
* Implement or assign implementation to these handlers during or after
* calling the constructor.
* @function
* @param {Object} event
* @param {OpenSeadragon.MouseTracker} event.eventSource
* A reference to the tracker instance.
* @param {String} event.pointerType
* "mouse", "touch", "pen", etc.
* @param {OpenSeadragon.Point} event.position
* The position of the event relative to the tracked element.
* @param {Boolean} event.shift
* True if the shift key was pressed during this event.
* @param {Boolean} event.isTouchEvent
* True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
* @param {Object} event.originalEvent
* The original event object.
* @param {Boolean} event.preventDefaultAction
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
* @param {Object} event.userData
* Arbitrary user-defined object.
*/
dblClickHandler: function () { },
/** /**
* Implement or assign implementation to these handlers during or after * Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
@ -746,7 +798,7 @@
/** /**
* Detect browser pointer device event model(s) and build appropriate list of events to subscribe to. * Detect browser pointer device event model(s) and build appropriate list of events to subscribe to.
*/ */
$.MouseTracker.subscribeEvents = [ "click", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ]; $.MouseTracker.subscribeEvents = [ "click", "dblclick", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ];
if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) { if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) {
// Older Firefox // Older Firefox
@ -877,6 +929,12 @@
* @memberof OpenSeadragon.MouseTracker.GesturePointList# * @memberof OpenSeadragon.MouseTracker.GesturePointList#
*/ */
this.contacts = 0; this.contacts = 0;
/**
* Current number of clicks for the device. Used for multiple click gesture tracking.
* @member {Number} clicks
* @memberof OpenSeadragon.MouseTracker.GesturePointList#
*/
this.clicks = 0;
}; };
$.MouseTracker.GesturePointList.prototype = /** @lends OpenSeadragon.MouseTracker.GesturePointList.prototype */{ $.MouseTracker.GesturePointList.prototype = /** @lends OpenSeadragon.MouseTracker.GesturePointList.prototype */{
/** /**
@ -1164,6 +1222,17 @@
} }
/**
* @private
* @inner
*/
function onDblClick( tracker, event ) {
if ( tracker.dblClickHandler ) {
$.cancelEvent( event );
}
}
/** /**
* @private * @private
* @inner * @inner
@ -1453,7 +1522,7 @@
captureMouse( tracker ); captureMouse( tracker );
} }
if ( tracker.clickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler ) { if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler ) {
$.cancelEvent( event ); $.cancelEvent( event );
} }
} }
@ -1798,7 +1867,7 @@
$.stopEvent( event ); $.stopEvent( event );
} }
if ( tracker.clickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) { if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) {
$.cancelEvent( event ); $.cancelEvent( event );
} }
} }
@ -2213,7 +2282,8 @@
curGPoint, curGPoint,
updateGPoint, updateGPoint,
releaseCapture = false, releaseCapture = false,
wasCaptured = false; wasCaptured = false,
quick;
if ( typeof event.buttons !== 'undefined' ) { if ( typeof event.buttons !== 'undefined' ) {
pointsList.buttons = event.buttons; pointsList.buttons = event.buttons;
@ -2320,28 +2390,63 @@
} }
} }
// Click // Click / Double-Click
if ( tracker.clickHandler && updateGPoint.insideElementPressed && updateGPoint.insideElement ) { if ( ( tracker.clickHandler || tracker.dblClickHandler ) && updateGPoint.insideElement ) {
var time = releaseTime - updateGPoint.contactTime, quick = releaseTime - updateGPoint.contactTime <= tracker.clickTimeThreshold &&
distance = updateGPoint.contactPos.distanceTo( releasePoint ), updateGPoint.contactPos.distanceTo( releasePoint ) <= tracker.clickDistThreshold;
quick = time <= tracker.clickTimeThreshold &&
distance <= tracker.clickDistThreshold;
propagate = tracker.clickHandler( // Click
{ if ( tracker.clickHandler ) {
eventSource: tracker, propagate = tracker.clickHandler(
pointerType: updateGPoint.type, {
position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ), eventSource: tracker,
quick: quick, pointerType: updateGPoint.type,
shift: event.shiftKey, position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ),
isTouchEvent: updateGPoint.type === 'touch', quick: quick,
originalEvent: event, shift: event.shiftKey,
preventDefaultAction: false, isTouchEvent: updateGPoint.type === 'touch',
userData: tracker.userData originalEvent: event,
preventDefaultAction: false,
userData: tracker.userData
}
);
if ( propagate === false ) {
$.cancelEvent( event );
}
}
// Double-Click
if ( tracker.dblClickHandler && quick ) {
pointsList.clicks++;
if ( pointsList.clicks === 1 ) {
delegate.lastClickPos = releasePoint;
/*jshint loopfunc:true*/
delegate.dblClickTimeOut = setTimeout( function() {
pointsList.clicks = 0;
}, tracker.dblClickTimeThreshold );
/*jshint loopfunc:false*/
} else if ( pointsList.clicks === 2 ) {
clearTimeout( delegate.dblClickTimeOut );
pointsList.clicks = 0;
if ( delegate.lastClickPos.distanceTo( releasePoint ) <= tracker.dblClickDistThreshold ) {
propagate = tracker.dblClickHandler(
{
eventSource: tracker,
pointerType: updateGPoint.type,
position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ),
shift: event.shiftKey,
isTouchEvent: updateGPoint.type === 'touch',
originalEvent: event,
preventDefaultAction: false,
userData: tracker.userData
}
);
if ( propagate === false ) {
$.cancelEvent( event );
}
}
delegate.lastClickPos = null;
} }
);
if ( propagate === false ) {
$.cancelEvent( event );
} }
} }
} else if ( pointsList.contacts === 2 ) { } else if ( pointsList.contacts === 2 ) {

View File

@ -267,12 +267,20 @@
* image requests in parallel as allowed by the browsers policy. * image requests in parallel as allowed by the browsers policy.
* *
* @property {Number} [clickTimeThreshold=300] * @property {Number} [clickTimeThreshold=300]
* If multiple mouse clicks occurs within less than this number of * The number of milliseconds within which a pointer down-up event combination
* milliseconds, treat them as a single click. * will be treated as a click gesture.
* *
* @property {Number} [clickDistThreshold=5] * @property {Number} [clickDistThreshold=5]
* If a mouse or touch drag occurs and the distance to the starting drag * The maximum distance allowed between a pointer down event and a pointer up event
* point is less than this many pixels, ignore the drag event. * to be treated as a click gesture.
*
* @property {Number} [dblClickTimeThreshold=300]
* The number of milliseconds within which two pointer down-up event combinations
* will be treated as a double-click gesture.
*
* @property {Number} [dblClickDistThreshold=20]
* The maximum distance allowed between two pointer click events
* to be treated as a double-click gesture.
* *
* @property {Number} [springStiffness=5.0] * @property {Number} [springStiffness=5.0]
* *
@ -284,6 +292,7 @@
* Settings for gestures generated by a mouse pointer device. (See {@link OpenSeadragon.GestureSettings}) * Settings for gestures generated by a mouse pointer device. (See {@link OpenSeadragon.GestureSettings})
* @property {Boolean} [gestureSettingsMouse.scrollToZoom=true] - Zoom on scroll gesture * @property {Boolean} [gestureSettingsMouse.scrollToZoom=true] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsMouse.clickToZoom=true] - Zoom on click gesture * @property {Boolean} [gestureSettingsMouse.clickToZoom=true] - Zoom on click gesture
* @property {Boolean} [gestureSettingsMouse.dblClickToZoom=false] - Zoom on double-click gesture
* @property {Boolean} [gestureSettingsMouse.pinchToZoom=false] - Zoom on pinch gesture * @property {Boolean} [gestureSettingsMouse.pinchToZoom=false] - Zoom on pinch gesture
* @property {Boolean} [gestureSettingsMouse.flickEnabled=false] - Enable flick gesture * @property {Boolean} [gestureSettingsMouse.flickEnabled=false] - Enable flick gesture
* @property {Number} [gestureSettingsMouse.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second) * @property {Number} [gestureSettingsMouse.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
@ -293,6 +302,7 @@
* Settings for gestures generated by a touch pointer device. (See {@link OpenSeadragon.GestureSettings}) * Settings for gestures generated by a touch pointer device. (See {@link OpenSeadragon.GestureSettings})
* @property {Boolean} [gestureSettingsTouch.scrollToZoom=false] - Zoom on scroll gesture * @property {Boolean} [gestureSettingsTouch.scrollToZoom=false] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsTouch.clickToZoom=false] - Zoom on click gesture * @property {Boolean} [gestureSettingsTouch.clickToZoom=false] - Zoom on click gesture
* @property {Boolean} [gestureSettingsTouch.dblClickToZoom=true] - Zoom on double-click gesture
* @property {Boolean} [gestureSettingsTouch.pinchToZoom=true] - Zoom on pinch gesture * @property {Boolean} [gestureSettingsTouch.pinchToZoom=true] - Zoom on pinch gesture
* @property {Boolean} [gestureSettingsTouch.flickEnabled=true] - Enable flick gesture * @property {Boolean} [gestureSettingsTouch.flickEnabled=true] - Enable flick gesture
* @property {Number} [gestureSettingsTouch.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second) * @property {Number} [gestureSettingsTouch.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
@ -302,6 +312,7 @@
* Settings for gestures generated by a pen pointer device. (See {@link OpenSeadragon.GestureSettings}) * Settings for gestures generated by a pen pointer device. (See {@link OpenSeadragon.GestureSettings})
* @property {Boolean} [gestureSettingsPen.scrollToZoom=false] - Zoom on scroll gesture * @property {Boolean} [gestureSettingsPen.scrollToZoom=false] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsPen.clickToZoom=true] - Zoom on click gesture * @property {Boolean} [gestureSettingsPen.clickToZoom=true] - Zoom on click gesture
* @property {Boolean} [gestureSettingsPen.dblClickToZoom=false] - Zoom on double-click gesture
* @property {Boolean} [gestureSettingsPen.pinchToZoom=false] - Zoom on pinch gesture * @property {Boolean} [gestureSettingsPen.pinchToZoom=false] - Zoom on pinch gesture
* @property {Boolean} [gestureSettingsPen.flickEnabled=false] - Enable flick gesture * @property {Boolean} [gestureSettingsPen.flickEnabled=false] - Enable flick gesture
* @property {Number} [gestureSettingsPen.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second) * @property {Number} [gestureSettingsPen.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
@ -311,13 +322,14 @@
* Settings for gestures generated by unknown pointer devices. (See {@link OpenSeadragon.GestureSettings}) * Settings for gestures generated by unknown pointer devices. (See {@link OpenSeadragon.GestureSettings})
* @property {Boolean} [gestureSettingsUnknown.scrollToZoom=true] - Zoom on scroll gesture * @property {Boolean} [gestureSettingsUnknown.scrollToZoom=true] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsUnknown.clickToZoom=false] - Zoom on click gesture * @property {Boolean} [gestureSettingsUnknown.clickToZoom=false] - Zoom on click gesture
* @property {Boolean} [gestureSettingsUnknown.dblClickToZoom=true] - Zoom on double-click gesture
* @property {Boolean} [gestureSettingsUnknown.pinchToZoom=true] - Zoom on pinch gesture * @property {Boolean} [gestureSettingsUnknown.pinchToZoom=true] - Zoom on pinch gesture
* @property {Boolean} [gestureSettingsUnknown.flickEnabled=true] - Enable flick gesture * @property {Boolean} [gestureSettingsUnknown.flickEnabled=true] - Enable flick gesture
* @property {Number} [gestureSettingsUnknown.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second) * @property {Number} [gestureSettingsUnknown.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
* @property {Number} [gestureSettingsUnknown.flickMomentum=0.40] - If flickEnabled is true, the momentum factor for the flick gesture * @property {Number} [gestureSettingsUnknown.flickMomentum=0.40] - If flickEnabled is true, the momentum factor for the flick gesture
* *
* @property {Number} [zoomPerClick=2.0] * @property {Number} [zoomPerClick=2.0]
* The "zoom distance" per mouse click or touch tap. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the click-to-zoom feature (also see gestureSettings[Mouse|Touch|Pen].clickToZoom).</em> * The "zoom distance" per mouse click or touch tap. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the click-to-zoom feature (also see gestureSettings[Mouse|Touch|Pen].clickToZoom/dblClickToZoom).</em>
* *
* @property {Number} [zoomPerScroll=1.2] * @property {Number} [zoomPerScroll=1.2]
* The "zoom distance" per mouse scroll or touch pinch. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the mouse-wheel zoom feature (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).</em> * The "zoom distance" per mouse scroll or touch pinch. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the mouse-wheel zoom feature (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).</em>
@ -534,6 +546,9 @@
* @property {Boolean} clickToZoom * @property {Boolean} clickToZoom
* Set to false to disable zooming on click gestures. * Set to false to disable zooming on click gestures.
* *
* @property {Boolean} dblClickToZoom
* Set to false to disable zooming on double-click gestures.
*
* @property {Boolean} pinchToZoom * @property {Boolean} pinchToZoom
* Set to false to disable zooming on pinch gestures. * Set to false to disable zooming on pinch gestures.
* *
@ -891,12 +906,14 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
//UI RESPONSIVENESS AND FEEL //UI RESPONSIVENESS AND FEEL
clickTimeThreshold: 300, clickTimeThreshold: 300,
clickDistThreshold: 5, clickDistThreshold: 5,
dblClickTimeThreshold: 300,
dblClickDistThreshold: 20,
springStiffness: 5.0, springStiffness: 5.0,
animationTime: 1.2, animationTime: 1.2,
gestureSettingsMouse: { scrollToZoom: true, clickToZoom: true, pinchToZoom: false, flickEnabled: false, flickMinSpeed: 20, flickMomentum: 0.40 }, gestureSettingsMouse: { scrollToZoom: true, clickToZoom: true, dblClickToZoom: false, pinchToZoom: false, flickEnabled: false, flickMinSpeed: 20, flickMomentum: 0.40 },
gestureSettingsTouch: { scrollToZoom: false, clickToZoom: false, pinchToZoom: true, flickEnabled: true, flickMinSpeed: 20, flickMomentum: 0.40 }, gestureSettingsTouch: { scrollToZoom: false, clickToZoom: false, dblClickToZoom: true, pinchToZoom: true, flickEnabled: true, flickMinSpeed: 20, flickMomentum: 0.40 },
gestureSettingsPen: { scrollToZoom: false, clickToZoom: true, pinchToZoom: false, flickEnabled: false, flickMinSpeed: 20, flickMomentum: 0.40 }, gestureSettingsPen: { scrollToZoom: false, clickToZoom: true, dblClickToZoom: false, pinchToZoom: false, flickEnabled: false, flickMinSpeed: 20, flickMomentum: 0.40 },
gestureSettingsUnknown: { scrollToZoom: false, clickToZoom: false, pinchToZoom: true, flickEnabled: true, flickMinSpeed: 20, flickMomentum: 0.40 }, gestureSettingsUnknown: { scrollToZoom: false, clickToZoom: false, dblClickToZoom: true, pinchToZoom: true, flickEnabled: true, flickMinSpeed: 20, flickMomentum: 0.40 },
zoomPerClick: 2, zoomPerClick: 2,
zoomPerScroll: 1.2, zoomPerScroll: 1.2,
zoomPerSecond: 1.0, zoomPerSecond: 1.0,

View File

@ -385,24 +385,29 @@ $.Viewer = function( options ) {
this.innerTracker = new $.MouseTracker({ this.innerTracker = new $.MouseTracker({
element: this.canvas, element: this.canvas,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold, clickDistThreshold: this.clickDistThreshold,
clickHandler: $.delegate( this, onCanvasClick ), dblClickTimeThreshold: this.dblClickTimeThreshold,
dragHandler: $.delegate( this, onCanvasDrag ), dblClickDistThreshold: this.dblClickDistThreshold,
dragEndHandler: $.delegate( this, onCanvasDragEnd ), clickHandler: $.delegate( this, onCanvasClick ),
releaseHandler: $.delegate( this, onCanvasRelease ), dblClickHandler: $.delegate( this, onCanvasDblClick ),
scrollHandler: $.delegate( this, onCanvasScroll ), dragHandler: $.delegate( this, onCanvasDrag ),
pinchHandler: $.delegate( this, onCanvasPinch ) dragEndHandler: $.delegate( this, onCanvasDragEnd ),
releaseHandler: $.delegate( this, onCanvasRelease ),
scrollHandler: $.delegate( this, onCanvasScroll ),
pinchHandler: $.delegate( this, onCanvasPinch )
}).setTracking( this.mouseNavEnabled ? true : false ); // default state }).setTracking( this.mouseNavEnabled ? true : false ); // default state
this.outerTracker = new $.MouseTracker({ this.outerTracker = new $.MouseTracker({
element: this.container, element: this.container,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold, clickDistThreshold: this.clickDistThreshold,
enterHandler: $.delegate( this, onContainerEnter ), dblClickTimeThreshold: this.dblClickTimeThreshold,
exitHandler: $.delegate( this, onContainerExit ), dblClickDistThreshold: this.dblClickDistThreshold,
releaseHandler: $.delegate( this, onContainerRelease ) enterHandler: $.delegate( this, onContainerEnter ),
exitHandler: $.delegate( this, onContainerExit ),
releaseHandler: $.delegate( this, onContainerRelease )
}).setTracking( this.mouseNavEnabled ? true : false ); // always tracking }).setTracking( this.mouseNavEnabled ? true : false ); // always tracking
if( this.toolbar ){ if( this.toolbar ){
@ -2253,7 +2258,7 @@ function onCanvasClick( event ) {
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event. * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Boolean} quick - True only if the clickDistThreshold and clickDeltaThreshold are both passed. Useful for differentiating between clicks and drags. * @property {Boolean} quick - True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for differentiating between clicks and drags.
* @property {Boolean} shift - True if the shift key was pressed during this event. * @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event. * @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object. * @property {?Object} userData - Arbitrary subscriber-defined object.
@ -2267,6 +2272,40 @@ function onCanvasClick( event ) {
}); });
} }
function onCanvasDblClick( event ) {
var gestureSettings;
if ( !event.preventDefaultAction && this.viewport ) {
gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
if ( gestureSettings.dblClickToZoom ) {
this.viewport.zoomBy(
event.shift ? 1.0 / this.zoomPerClick : this.zoomPerClick,
this.viewport.pointFromPixel( event.position, true )
);
this.viewport.applyConstraints();
}
}
/**
* Raised when a double mouse press/release or touch/remove occurs on the {@link OpenSeadragon.Viewer#canvas} element.
*
* @event canvas-double-click
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'canvas-double-click', {
tracker: event.eventSource,
position: event.position,
shift: event.shift,
originalEvent: event.originalEvent
});
}
function onCanvasDrag( event ) { function onCanvasDrag( event ) {
var gestureSettings; var gestureSettings;

View File

@ -37,6 +37,7 @@
origReleaseHandler, origReleaseHandler,
origMoveHandler, origMoveHandler,
origClickHandler, origClickHandler,
origDblClickHandler,
origDragHandler, origDragHandler,
origDragEndHandler, origDragEndHandler,
enterCount, enterCount,
@ -45,6 +46,7 @@
releaseCount, releaseCount,
moveCount, moveCount,
clickCount, clickCount,
dblClickCount,
dragCount, dragCount,
dragEndCount, dragEndCount,
insideElementPressed, insideElementPressed,
@ -111,6 +113,15 @@
return true; return true;
} }
}; };
origDblClickHandler = tracker.dblClickHandler;
tracker.dblClickHandler = function ( event ) {
dblClickCount++;
if (origDblClickHandler) {
return origDblClickHandler( event );
} else {
return true;
}
};
origDragHandler = tracker.dragHandler; origDragHandler = tracker.dragHandler;
tracker.dragHandler = function ( event ) { tracker.dragHandler = function ( event ) {
dragCount++; dragCount++;
@ -140,6 +151,7 @@
tracker.releaseHandler = origReleaseHandler; tracker.releaseHandler = origReleaseHandler;
tracker.moveHandler = origMoveHandler; tracker.moveHandler = origMoveHandler;
tracker.clickHandler = origClickHandler; tracker.clickHandler = origClickHandler;
tracker.dblClickHandler = origDblClickHandler;
tracker.dragHandler = origDragHandler; tracker.dragHandler = origDragHandler;
tracker.dragEndHandler = origDragEndHandler; tracker.dragEndHandler = origDragEndHandler;
}; };
@ -188,6 +200,7 @@
releaseCount = 0; releaseCount = 0;
moveCount = 0; moveCount = 0;
clickCount = 0; clickCount = 0;
dblClickCount = 0;
dragCount = 0; dragCount = 0;
dragEndCount = 0; dragEndCount = 0;
insideElementPressed = false; insideElementPressed = false;
@ -217,6 +230,9 @@
if ('clickCount' in expected) { if ('clickCount' in expected) {
equal( clickCount, expected.clickCount, expected.description + 'clickHandler event count matches expected (' + expected.clickCount + ')' ); equal( clickCount, expected.clickCount, expected.description + 'clickHandler event count matches expected (' + expected.clickCount + ')' );
} }
if ('dblClickCount' in expected) {
equal( dblClickCount, expected.dblClickCount, expected.description + 'dblClickHandler event count matches expected (' + expected.dblClickCount + ')' );
}
if ('dragCount' in expected) { if ('dragCount' in expected) {
equal( dragCount, expected.dragCount, expected.description + 'dragHandler event count matches expected (' + expected.dragCount + ')' ); equal( dragCount, expected.dragCount, expected.description + 'dragHandler event count matches expected (' + expected.dragCount + ')' );
} }
@ -269,6 +285,7 @@
releaseCount: 1, releaseCount: 1,
moveCount: 20, moveCount: 20,
clickCount: 0, clickCount: 0,
dblClickCount: 0,
dragCount: 0, dragCount: 0,
dragEndCount: 0, dragEndCount: 0,
insideElementPressed: false, insideElementPressed: false,
@ -293,6 +310,7 @@
releaseCount: 0, releaseCount: 0,
moveCount: 20, moveCount: 20,
clickCount: 0, clickCount: 0,
dblClickCount: 0,
dragCount: 0, dragCount: 0,
dragEndCount: 0, dragEndCount: 0,
//insideElementPressed: false, //insideElementPressed: false,
@ -315,6 +333,7 @@
releaseCount: 0, releaseCount: 0,
moveCount: 20, moveCount: 20,
clickCount: 0, clickCount: 0,
dblClickCount: 0,
dragCount: 0, dragCount: 0,
dragEndCount: 0, dragEndCount: 0,
//insideElementPressed: false, //insideElementPressed: false,
@ -324,7 +343,33 @@
//quickClick: false //quickClick: false
}); });
// enter-press-release-exit // enter-press-release-press-release-exit (double click)
resetForAssessment();
simulateEnter(0, 0);
simulateDown(0, 0);
simulateUp(0, 0);
simulateDown(0, 0);
simulateUp(0, 0);
simulateLeave(-1, -1);
assessGestureExpectations({
description: 'enter-press-release-press-release-exit (double click): ',
enterCount: 1,
exitCount: 1,
pressCount: 2,
releaseCount: 2,
moveCount: 0,
clickCount: 2,
dblClickCount: 1,
dragCount: 0,
dragEndCount: 0,
insideElementPressed: true,
insideElementReleased: true,
contacts: 0,
trackedPointers: 0
//quickClick: true
});
// enter-press-release-exit (click)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateDown(0, 0); simulateDown(0, 0);
@ -338,6 +383,7 @@
releaseCount: 1, releaseCount: 1,
moveCount: 0, moveCount: 0,
clickCount: 1, clickCount: 1,
dblClickCount: 0,
dragCount: 0, dragCount: 0,
dragEndCount: 0, dragEndCount: 0,
insideElementPressed: true, insideElementPressed: true,
@ -363,6 +409,7 @@
releaseCount: 1, releaseCount: 1,
moveCount: 200, moveCount: 200,
clickCount: 1, clickCount: 1,
dblClickCount: 0,
dragCount: 100, dragCount: 100,
dragEndCount: 1, dragEndCount: 1,
insideElementPressed: true, insideElementPressed: true,
@ -389,6 +436,7 @@
releaseCount: 1, releaseCount: 1,
moveCount: 15, moveCount: 15,
clickCount: 0, clickCount: 0,
dblClickCount: 0,
dragCount: 15, dragCount: 15,
dragEndCount: 1, dragEndCount: 1,
insideElementPressed: true, insideElementPressed: true,
@ -509,6 +557,8 @@
userData: userData, userData: userData,
clickTimeThreshold: OpenSeadragon.DEFAULT_SETTINGS.clickTimeThreshold, clickTimeThreshold: OpenSeadragon.DEFAULT_SETTINGS.clickTimeThreshold,
clickDistThreshold: OpenSeadragon.DEFAULT_SETTINGS.clickDistThreshold, clickDistThreshold: OpenSeadragon.DEFAULT_SETTINGS.clickDistThreshold,
dblClickTimeThreshold: OpenSeadragon.DEFAULT_SETTINGS.dblClickTimeThreshold,
dblClickDistThreshold: OpenSeadragon.DEFAULT_SETTINGS.dblClickDistThreshold,
focusHandler: onMouseTrackerFocus, focusHandler: onMouseTrackerFocus,
blurHandler: onMouseTrackerBlur, blurHandler: onMouseTrackerBlur,
enterHandler: onMouseTrackerEnter, enterHandler: onMouseTrackerEnter,

View File

@ -4,7 +4,7 @@
* Plugin to force OpenSeadragon to use the legacy mouse pointer event model * Plugin to force OpenSeadragon to use the legacy mouse pointer event model
*/ */
$.MouseTracker.subscribeEvents = [ "click", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ]; $.MouseTracker.subscribeEvents = [ "click", "dblclick", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ];
if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) { if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) {
// Older Firefox // Older Firefox