From 2831771af5a74f556e9d430104e411cbc6b28622 Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Fri, 16 Jan 2015 16:26:30 -0800 Subject: [PATCH] MouseTracker - keyboard handling 1) MouseTracker - added keydown and keyup handlers 2) Modifier keys ignored in keyboard navigation handlers (#503) 3) Arrow key navigation fixed across platforms (#565) --- src/mousetracker.js | 133 +++++++++++++++++++++++++++++++++++++++++- src/referencestrip.js | 90 +++++++++++++++++++--------- src/viewer.js | 44 ++++++++++++-- 3 files changed, 231 insertions(+), 36 deletions(-) diff --git a/src/mousetracker.js b/src/mousetracker.js index 20748ba0..9e8f25ef 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -95,6 +95,10 @@ * An optional handler for after a drag gesture. * @param {OpenSeadragon.EventHandler} [options.pinchHandler=null] * An optional handler for the pinch gesture. + * @param {OpenSeadragon.EventHandler} [options.keyDownHandler=null] + * An optional handler for keydown. + * @param {OpenSeadragon.EventHandler} [options.keyUpHandler=null] + * An optional handler for keyup. * @param {OpenSeadragon.EventHandler} [options.keyHandler=null] * An optional handler for keypress. * @param {OpenSeadragon.EventHandler} [options.focusHandler=null] @@ -170,6 +174,8 @@ this.dragEndHandler = options.dragEndHandler || null; this.pinchHandler = options.pinchHandler || null; this.stopHandler = options.stopHandler || null; + this.keyDownHandler = options.keyDownHandler || null; + this.keyUpHandler = options.keyUpHandler || null; this.keyHandler = options.keyHandler || null; this.focusHandler = options.focusHandler || null; this.blurHandler = options.blurHandler || null; @@ -185,6 +191,8 @@ THIS[ this.hash ] = { click: function ( event ) { onClick( _this, event ); }, dblclick: function ( event ) { onDblClick( _this, event ); }, + keydown: function ( event ) { onKeyDown( _this, event ); }, + keyup: function ( event ) { onKeyUp( _this, event ); }, keypress: function ( event ) { onKeyPress( _this, event ); }, focus: function ( event ) { onFocus( _this, event ); }, blur: function ( event ) { onBlur( _this, event ); }, @@ -743,8 +751,66 @@ * A reference to the tracker instance. * @param {Number} event.keyCode * The key code that was pressed. + * @param {Boolean} event.ctrl + * True if the ctrl key was pressed during this event. * @param {Boolean} event.shift * True if the shift key was pressed during this event. + * @param {Boolean} event.alt + * True if the alt key was pressed during this event. + * @param {Boolean} event.meta + * True if the meta key was pressed during this event. + * @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. + */ + keyDownHandler: 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 {Number} event.keyCode + * The key code that was pressed. + * @param {Boolean} event.ctrl + * True if the ctrl key was pressed during this event. + * @param {Boolean} event.shift + * True if the shift key was pressed during this event. + * @param {Boolean} event.alt + * True if the alt key was pressed during this event. + * @param {Boolean} event.meta + * True if the meta key was pressed during this event. + * @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. + */ + keyUpHandler: 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 {Number} event.keyCode + * The key code that was pressed. + * @param {Boolean} event.ctrl + * True if the ctrl key was pressed during this event. + * @param {Boolean} event.shift + * True if the shift key was pressed during this event. + * @param {Boolean} event.alt + * True if the alt key was pressed during this event. + * @param {Boolean} event.meta + * True if the meta key was pressed during this event. * @param {Object} event.originalEvent * The original event object. * @param {Boolean} event.preventDefaultAction @@ -904,7 +970,7 @@ /** * Detect browser pointer device event model(s) and build appropriate list of events to subscribe to. */ - $.MouseTracker.subscribeEvents = [ "click", "dblclick", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ]; + $.MouseTracker.subscribeEvents = [ "click", "dblclick", "keydown", "keyup", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ]; if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) { // Older Firefox @@ -1440,12 +1506,72 @@ } + /** + * @private + * @inner + */ + function onKeyDown( tracker, event ) { + //$.console.log( "keydown %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey ); + var propagate; + if ( tracker.keyDownHandler ) { + event = $.getEvent( event ); + propagate = tracker.keyDownHandler( + { + eventSource: tracker, + position: getMouseRelative( event, tracker.element ), + keyCode: event.keyCode ? event.keyCode : event.charCode, + ctrl: event.ctrlKey, + shift: event.shiftKey, + alt: event.altKey, + meta: event.metaKey, + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( !propagate ) { + $.cancelEvent( event ); + } + } + } + + + /** + * @private + * @inner + */ + function onKeyUp( tracker, event ) { + //$.console.log( "keyup %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey ); + var propagate; + if ( tracker.keyUpHandler ) { + event = $.getEvent( event ); + propagate = tracker.keyUpHandler( + { + eventSource: tracker, + position: getMouseRelative( event, tracker.element ), + keyCode: event.keyCode ? event.keyCode : event.charCode, + ctrl: event.ctrlKey, + shift: event.shiftKey, + alt: event.altKey, + meta: event.metaKey, + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( !propagate ) { + $.cancelEvent( event ); + } + } + } + + /** * @private * @inner */ function onKeyPress( tracker, event ) { - //console.log( "keypress %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey ); + //$.console.log( "keypress %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey ); var propagate; if ( tracker.keyHandler ) { event = $.getEvent( event ); @@ -1454,7 +1580,10 @@ eventSource: tracker, position: getMouseRelative( event, tracker.element ), keyCode: event.keyCode ? event.keyCode : event.charCode, + ctrl: event.ctrlKey, shift: event.shiftKey, + alt: event.altKey, + meta: event.metaKey, originalEvent: event, preventDefaultAction: false, userData: tracker.userData diff --git a/src/referencestrip.js b/src/referencestrip.js index f68744b2..a872ca58 100644 --- a/src/referencestrip.js +++ b/src/referencestrip.js @@ -125,6 +125,7 @@ $.ReferenceStrip = function ( options ) { scrollHandler: $.delegate( this, onStripScroll ), enterHandler: $.delegate( this, onStripEnter ), exitHandler: $.delegate( this, onStripExit ), + keyDownHandler: $.delegate( this, onKeyDown ), keyHandler: $.delegate( this, onKeyPress ) } ).setTracking( true ); @@ -511,6 +512,37 @@ function onStripExit( event ) { } +/** + * @private + * @inner + * @function + */ +function onKeyDown( event ) { + //console.log( event.keyCode ); + + if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) { + switch ( event.keyCode ) { + case 38: //up arrow + onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); + return false; + case 40: //down arrow + onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); + return false; + case 37: //left arrow + onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); + return false; + case 39: //right arrow + onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); + return false; + default: + //console.log( 'navigator keycode %s', event.keyCode ); + return true; + } + } else { + return true; + } +} + /** * @private @@ -520,35 +552,35 @@ function onStripExit( event ) { function onKeyPress( event ) { //console.log( event.keyCode ); - switch ( event.keyCode ) { - case 61: //=|+ - onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); - return false; - case 45: //-|_ - onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); - return false; - case 48: //0|) - case 119: //w - case 87: //W - case 38: //up arrow - onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); - return false; - case 115: //s - case 83: //S - case 40: //down arrow - onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); - return false; - case 97: //a - case 37: //left arrow - onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); - return false; - case 100: //d - case 39: //right arrow - onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); - return false; - default: - //console.log( 'navigator keycode %s', event.keyCode ); - return true; + if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) { + switch ( event.keyCode ) { + case 61: //=|+ + onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); + return false; + case 45: //-|_ + onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); + return false; + case 48: //0|) + case 119: //w + case 87: //W + onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); + return false; + case 115: //s + case 83: //S + onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); + return false; + case 97: //a + onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); + return false; + case 100: //d + onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); + return false; + default: + //console.log( 'navigator keycode %s', event.keyCode ); + return true; + } + } else { + return true; } } diff --git a/src/viewer.js b/src/viewer.js index 215f9432..1a7490d0 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -325,8 +325,44 @@ $.Viewer = function( options ) { } }, + keyDownHandler: function( event ){ + if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) { + switch( event.keyCode ){ + case 38://up arrow + if ( event.shift ) { + _this.viewport.zoomBy(1.1); + } else { + _this.viewport.panBy(new $.Point(0, -0.05)); + } + _this.viewport.applyConstraints(); + return false; + case 40://down arrow + if ( event.shift ) { + _this.viewport.zoomBy(0.9); + } else { + _this.viewport.panBy(new $.Point(0, 0.05)); + } + _this.viewport.applyConstraints(); + return false; + case 37://left arrow + _this.viewport.panBy(new $.Point(-0.05, 0)); + _this.viewport.applyConstraints(); + return false; + case 39://right arrow + _this.viewport.panBy(new $.Point(0.05, 0)); + _this.viewport.applyConstraints(); + return false; + default: + //console.log( 'navigator keycode %s', event.keyCode ); + return true; + } + } else { + return true; + } + }, + keyHandler: function( event ){ - if ( !event.preventDefaultAction ) { + if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) { switch( event.keyCode ){ case 61://=|+ _this.viewport.zoomBy(1.1); @@ -342,7 +378,6 @@ $.Viewer = function( options ) { return false; case 119://w case 87://W - case 38://up arrow if ( event.shift ) { _this.viewport.zoomBy(1.1); } else { @@ -352,7 +387,6 @@ $.Viewer = function( options ) { return false; case 115://s case 83://S - case 40://down arrow if ( event.shift ) { _this.viewport.zoomBy(0.9); } else { @@ -361,12 +395,10 @@ $.Viewer = function( options ) { _this.viewport.applyConstraints(); return false; case 97://a - case 37://left arrow _this.viewport.panBy(new $.Point(-0.05, 0)); _this.viewport.applyConstraints(); return false; case 100://d - case 39://right arrow _this.viewport.panBy(new $.Point(0.05, 0)); _this.viewport.applyConstraints(); return false; @@ -374,6 +406,8 @@ $.Viewer = function( options ) { //console.log( 'navigator keycode %s', event.keyCode ); return true; } + } else { + return true; } } }).setTracking( true ); // default state