From 8821ab57c00fb6fa679e0f872371dcef9897311c Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 8 Mar 2013 21:19:24 -0800 Subject: [PATCH] Allow speical keys to be bound to keystrokes Summary: Fixes T2711. Allows special keys (arrows, tab, return) to be bound as shortcuts. Binds left and right as shortcuts in Pholio. Test Plan: Pressed left. Pressed right. Reviewers: chad Reviewed By: chad CC: aran Maniphest Tasks: T2711 Differential Revision: https://secure.phabricator.com/D5303 --- src/__celerity_resource_map__.php | 194 +++++++++--------- ...bricatorHelpKeyboardShortcutController.php | 11 + .../core/KeyboardShortcutManager.js | 28 ++- .../pholio/behavior-pholio-mock-view.js | 4 +- 4 files changed, 135 insertions(+), 102 deletions(-) diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 0b95bd5f04..b34e247c9c 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -805,7 +805,7 @@ celerity_register_resource_map(array( ), 'conpherence-widget-pane-css' => array( - 'uri' => '/res/e67ad581/rsrc/css/application/conpherence/widget-pane.css', + 'uri' => '/res/6e5755bb/rsrc/css/application/conpherence/widget-pane.css', 'type' => 'css', 'requires' => array( @@ -1167,30 +1167,28 @@ celerity_register_resource_map(array( ), 'javelin-behavior-conpherence-menu' => array( - 'uri' => '/res/cb1a5cf0/rsrc/js/application/conpherence/behavior-menu.js', + 'uri' => '/res/0ad6ab54/rsrc/js/application/conpherence/behavior-menu.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-dom', + 2 => 'javelin-workflow', + 3 => 'javelin-util', + 4 => 'javelin-stratcom', + 5 => 'javelin-uri', + ), + 'disk' => '/rsrc/js/application/conpherence/behavior-menu.js', + ), + 'javelin-behavior-conpherence-pontificate' => + array( + 'uri' => '/res/06214a06/rsrc/js/application/conpherence/behavior-pontificate.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-behavior', 1 => 'javelin-dom', 2 => 'javelin-request', - 3 => 'javelin-stratcom', - 4 => 'javelin-uri', - 5 => 'javelin-util', - 6 => 'javelin-workflow', - ), - 'disk' => '/rsrc/js/application/conpherence/behavior-menu.js', - ), - 'javelin-behavior-conpherence-pontificate' => - array( - 'uri' => '/res/15263692/rsrc/js/application/conpherence/behavior-pontificate.js', - 'type' => 'js', - 'requires' => - array( - 0 => 'javelin-behavior', - 1 => 'javelin-dom', - 2 => 'javelin-util', - 3 => 'javelin-workflow', ), 'disk' => '/rsrc/js/application/conpherence/behavior-pontificate.js', ), @@ -1935,7 +1933,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-pholio-mock-view' => array( - 'uri' => '/res/bee3cef2/rsrc/js/application/pholio/behavior-pholio-mock-view.js', + 'uri' => '/res/6b8ca4e4/rsrc/js/application/pholio/behavior-pholio-mock-view.js', 'type' => 'js', 'requires' => array( @@ -2666,7 +2664,7 @@ celerity_register_resource_map(array( ), 'phabricator-core-css' => array( - 'uri' => '/res/1e7afaa9/rsrc/css/core/core.css', + 'uri' => '/res/b34e5c75/rsrc/css/core/core.css', 'type' => 'css', 'requires' => array( @@ -2819,7 +2817,7 @@ celerity_register_resource_map(array( ), 'phabricator-keyboard-shortcut-manager' => array( - 'uri' => '/res/0be80136/rsrc/js/application/core/KeyboardShortcutManager.js', + 'uri' => '/res/d0bee7c7/rsrc/js/application/core/KeyboardShortcutManager.js', 'type' => 'js', 'requires' => array( @@ -3057,7 +3055,7 @@ celerity_register_resource_map(array( ), 'phabricator-standard-page-view' => array( - 'uri' => '/res/70fa2da4/rsrc/css/application/base/standard-page-view.css', + 'uri' => '/res/252faaf4/rsrc/css/application/base/standard-page-view.css', 'type' => 'css', 'requires' => array( @@ -3502,7 +3500,7 @@ celerity_register_resource_map(array( ), array( 'packages' => array( - 'fdd3bb5f' => + '8e2735e2' => array( 'name' => 'core.pkg.css', 'symbols' => @@ -3545,10 +3543,10 @@ celerity_register_resource_map(array( 35 => 'phabricator-object-item-list-view-css', 36 => 'global-drag-and-drop-css', ), - 'uri' => '/res/pkg/fdd3bb5f/core.pkg.css', + 'uri' => '/res/pkg/8e2735e2/core.pkg.css', 'type' => 'css', ), - 'f24c209c' => + '0ed3458f' => array( 'name' => 'core.pkg.js', 'symbols' => @@ -3588,7 +3586,7 @@ celerity_register_resource_map(array( 32 => 'javelin-behavior-global-drag-and-drop', 33 => 'javelin-behavior-phabricator-reveal-content', ), - 'uri' => '/res/pkg/f24c209c/core.pkg.js', + 'uri' => '/res/pkg/0ed3458f/core.pkg.js', 'type' => 'js', ), 'dca4a03d' => @@ -3735,17 +3733,17 @@ celerity_register_resource_map(array( 'reverse' => array( 'aphront-attached-file-view-css' => 'eb35a026', - 'aphront-crumbs-view-css' => 'fdd3bb5f', - 'aphront-dialog-view-css' => 'fdd3bb5f', - 'aphront-error-view-css' => 'fdd3bb5f', - 'aphront-form-view-css' => 'fdd3bb5f', - 'aphront-list-filter-view-css' => 'fdd3bb5f', - 'aphront-pager-view-css' => 'fdd3bb5f', - 'aphront-panel-view-css' => 'fdd3bb5f', - 'aphront-table-view-css' => 'fdd3bb5f', - 'aphront-tokenizer-control-css' => 'fdd3bb5f', - 'aphront-tooltip-css' => 'fdd3bb5f', - 'aphront-typeahead-control-css' => 'fdd3bb5f', + 'aphront-crumbs-view-css' => '8e2735e2', + 'aphront-dialog-view-css' => '8e2735e2', + 'aphront-error-view-css' => '8e2735e2', + 'aphront-form-view-css' => '8e2735e2', + 'aphront-list-filter-view-css' => '8e2735e2', + 'aphront-pager-view-css' => '8e2735e2', + 'aphront-panel-view-css' => '8e2735e2', + 'aphront-table-view-css' => '8e2735e2', + 'aphront-tokenizer-control-css' => '8e2735e2', + 'aphront-tooltip-css' => '8e2735e2', + 'aphront-typeahead-control-css' => '8e2735e2', 'differential-changeset-view-css' => '8aaacd1b', 'differential-core-view-css' => '8aaacd1b', 'differential-inline-comment-editor' => '322728f3', @@ -3759,19 +3757,19 @@ celerity_register_resource_map(array( 'differential-table-of-contents-css' => '8aaacd1b', 'diffusion-commit-view-css' => 'c8ce2d88', 'diffusion-icons-css' => 'c8ce2d88', - 'global-drag-and-drop-css' => 'fdd3bb5f', + 'global-drag-and-drop-css' => '8e2735e2', 'inline-comment-summary-css' => '8aaacd1b', - 'javelin-aphlict' => 'f24c209c', + 'javelin-aphlict' => '0ed3458f', 'javelin-behavior' => 'cd1d650a', - 'javelin-behavior-aphlict-dropdown' => 'f24c209c', - 'javelin-behavior-aphlict-listen' => 'f24c209c', - 'javelin-behavior-aphront-basic-tokenizer' => 'f24c209c', + 'javelin-behavior-aphlict-dropdown' => '0ed3458f', + 'javelin-behavior-aphlict-listen' => '0ed3458f', + 'javelin-behavior-aphront-basic-tokenizer' => '0ed3458f', 'javelin-behavior-aphront-drag-and-drop' => '322728f3', 'javelin-behavior-aphront-drag-and-drop-textarea' => '322728f3', - 'javelin-behavior-aphront-form-disable-on-submit' => 'f24c209c', + 'javelin-behavior-aphront-form-disable-on-submit' => '0ed3458f', 'javelin-behavior-audit-preview' => 'f96657b8', 'javelin-behavior-dark-console' => 'dca4a03d', - 'javelin-behavior-device' => 'f24c209c', + 'javelin-behavior-device' => '0ed3458f', 'javelin-behavior-differential-accept-with-errors' => '322728f3', 'javelin-behavior-differential-add-reviewers-and-ccs' => '322728f3', 'javelin-behavior-differential-comment-jump' => '322728f3', @@ -3787,31 +3785,31 @@ celerity_register_resource_map(array( 'javelin-behavior-diffusion-commit-graph' => 'f96657b8', 'javelin-behavior-diffusion-pull-lastmodified' => 'f96657b8', 'javelin-behavior-error-log' => 'dca4a03d', - 'javelin-behavior-global-drag-and-drop' => 'f24c209c', - 'javelin-behavior-history-install' => 'f24c209c', - 'javelin-behavior-konami' => 'f24c209c', - 'javelin-behavior-lightbox-attachments' => 'f24c209c', + 'javelin-behavior-global-drag-and-drop' => '0ed3458f', + 'javelin-behavior-history-install' => '0ed3458f', + 'javelin-behavior-konami' => '0ed3458f', + 'javelin-behavior-lightbox-attachments' => '0ed3458f', 'javelin-behavior-load-blame' => '322728f3', 'javelin-behavior-maniphest-batch-selector' => '7707de41', 'javelin-behavior-maniphest-subpriority-editor' => '7707de41', 'javelin-behavior-maniphest-transaction-controls' => '7707de41', 'javelin-behavior-maniphest-transaction-expand' => '7707de41', 'javelin-behavior-maniphest-transaction-preview' => '7707de41', - 'javelin-behavior-phabricator-active-nav' => 'f24c209c', - 'javelin-behavior-phabricator-autofocus' => 'f24c209c', - 'javelin-behavior-phabricator-keyboard-shortcuts' => 'f24c209c', - 'javelin-behavior-phabricator-nav' => 'f24c209c', + 'javelin-behavior-phabricator-active-nav' => '0ed3458f', + 'javelin-behavior-phabricator-autofocus' => '0ed3458f', + 'javelin-behavior-phabricator-keyboard-shortcuts' => '0ed3458f', + 'javelin-behavior-phabricator-nav' => '0ed3458f', 'javelin-behavior-phabricator-object-selector' => '322728f3', - 'javelin-behavior-phabricator-oncopy' => 'f24c209c', - 'javelin-behavior-phabricator-remarkup-assist' => 'f24c209c', - 'javelin-behavior-phabricator-reveal-content' => 'f24c209c', - 'javelin-behavior-phabricator-search-typeahead' => 'f24c209c', - 'javelin-behavior-phabricator-tooltips' => 'f24c209c', - 'javelin-behavior-phabricator-watch-anchor' => 'f24c209c', - 'javelin-behavior-refresh-csrf' => 'f24c209c', + 'javelin-behavior-phabricator-oncopy' => '0ed3458f', + 'javelin-behavior-phabricator-remarkup-assist' => '0ed3458f', + 'javelin-behavior-phabricator-reveal-content' => '0ed3458f', + 'javelin-behavior-phabricator-search-typeahead' => '0ed3458f', + 'javelin-behavior-phabricator-tooltips' => '0ed3458f', + 'javelin-behavior-phabricator-watch-anchor' => '0ed3458f', + 'javelin-behavior-refresh-csrf' => '0ed3458f', 'javelin-behavior-repository-crossreference' => '322728f3', - 'javelin-behavior-toggle-class' => 'f24c209c', - 'javelin-behavior-workflow' => 'f24c209c', + 'javelin-behavior-toggle-class' => '0ed3458f', + 'javelin-behavior-workflow' => '0ed3458f', 'javelin-dom' => 'cd1d650a', 'javelin-event' => 'cd1d650a', 'javelin-install' => 'cd1d650a', @@ -3830,48 +3828,48 @@ celerity_register_resource_map(array( 'javelin-util' => 'cd1d650a', 'javelin-vector' => 'cd1d650a', 'javelin-workflow' => 'cd1d650a', - 'lightbox-attachment-css' => 'fdd3bb5f', + 'lightbox-attachment-css' => '8e2735e2', 'maniphest-task-summary-css' => 'eb35a026', 'maniphest-transaction-detail-css' => 'eb35a026', - 'phabricator-busy' => 'f24c209c', + 'phabricator-busy' => '0ed3458f', 'phabricator-content-source-view-css' => '8aaacd1b', - 'phabricator-core-buttons-css' => 'fdd3bb5f', - 'phabricator-core-css' => 'fdd3bb5f', - 'phabricator-crumbs-view-css' => 'fdd3bb5f', - 'phabricator-directory-css' => 'fdd3bb5f', + 'phabricator-core-buttons-css' => '8e2735e2', + 'phabricator-core-css' => '8e2735e2', + 'phabricator-crumbs-view-css' => '8e2735e2', + 'phabricator-directory-css' => '8e2735e2', 'phabricator-drag-and-drop-file-upload' => '322728f3', - 'phabricator-dropdown-menu' => 'f24c209c', - 'phabricator-file-upload' => 'f24c209c', - 'phabricator-filetree-view-css' => 'fdd3bb5f', - 'phabricator-flag-css' => 'fdd3bb5f', - 'phabricator-form-view-css' => 'fdd3bb5f', - 'phabricator-header-view-css' => 'fdd3bb5f', - 'phabricator-jump-nav' => 'fdd3bb5f', - 'phabricator-keyboard-shortcut' => 'f24c209c', - 'phabricator-keyboard-shortcut-manager' => 'f24c209c', - 'phabricator-main-menu-view' => 'fdd3bb5f', - 'phabricator-menu-item' => 'f24c209c', - 'phabricator-nav-view-css' => 'fdd3bb5f', - 'phabricator-notification' => 'f24c209c', - 'phabricator-notification-css' => 'fdd3bb5f', - 'phabricator-notification-menu-css' => 'fdd3bb5f', - 'phabricator-object-item-list-view-css' => 'fdd3bb5f', + 'phabricator-dropdown-menu' => '0ed3458f', + 'phabricator-file-upload' => '0ed3458f', + 'phabricator-filetree-view-css' => '8e2735e2', + 'phabricator-flag-css' => '8e2735e2', + 'phabricator-form-view-css' => '8e2735e2', + 'phabricator-header-view-css' => '8e2735e2', + 'phabricator-jump-nav' => '8e2735e2', + 'phabricator-keyboard-shortcut' => '0ed3458f', + 'phabricator-keyboard-shortcut-manager' => '0ed3458f', + 'phabricator-main-menu-view' => '8e2735e2', + 'phabricator-menu-item' => '0ed3458f', + 'phabricator-nav-view-css' => '8e2735e2', + 'phabricator-notification' => '0ed3458f', + 'phabricator-notification-css' => '8e2735e2', + 'phabricator-notification-menu-css' => '8e2735e2', + 'phabricator-object-item-list-view-css' => '8e2735e2', 'phabricator-object-selector-css' => '8aaacd1b', - 'phabricator-paste-file-upload' => 'f24c209c', - 'phabricator-prefab' => 'f24c209c', + 'phabricator-paste-file-upload' => '0ed3458f', + 'phabricator-prefab' => '0ed3458f', 'phabricator-project-tag-css' => 'eb35a026', - 'phabricator-remarkup-css' => 'fdd3bb5f', + 'phabricator-remarkup-css' => '8e2735e2', 'phabricator-shaped-request' => '322728f3', - 'phabricator-side-menu-view-css' => 'fdd3bb5f', - 'phabricator-standard-page-view' => 'fdd3bb5f', - 'phabricator-textareautils' => 'f24c209c', - 'phabricator-tooltip' => 'f24c209c', - 'phabricator-transaction-view-css' => 'fdd3bb5f', - 'phabricator-zindex-css' => 'fdd3bb5f', - 'sprite-apps-large-css' => 'fdd3bb5f', - 'sprite-gradient-css' => 'fdd3bb5f', - 'sprite-icon-css' => 'fdd3bb5f', - 'sprite-menu-css' => 'fdd3bb5f', - 'syntax-highlighting-css' => 'fdd3bb5f', + 'phabricator-side-menu-view-css' => '8e2735e2', + 'phabricator-standard-page-view' => '8e2735e2', + 'phabricator-textareautils' => '0ed3458f', + 'phabricator-tooltip' => '0ed3458f', + 'phabricator-transaction-view-css' => '8e2735e2', + 'phabricator-zindex-css' => '8e2735e2', + 'sprite-apps-large-css' => '8e2735e2', + 'sprite-gradient-css' => '8e2735e2', + 'sprite-icon-css' => '8e2735e2', + 'sprite-menu-css' => '8e2735e2', + 'syntax-highlighting-css' => '8e2735e2', ), )); diff --git a/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php b/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php index 06ea9e9d0e..d92bdc923f 100644 --- a/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php +++ b/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php @@ -21,10 +21,21 @@ final class PhabricatorHelpKeyboardShortcutController 'description' => 'Close any dialog, including this one.', ); + $stroke_map = array( + 'left' => "\xE2\x86\x90", + 'right' => "\xE2\x86\x92", + 'up' => "\xE2\x86\x91", + 'down' => "\xE2\x86\x93", + 'return' => "\xE2\x8F\x8E", + 'tab' => "\xE2\x87\xA5", + 'delete' => "\xE2\x8C\xAB", + ); + $rows = array(); foreach ($keys as $shortcut) { $keystrokes = array(); foreach ($shortcut['keys'] as $stroke) { + $stroke = idx($stroke_map, $stroke, $stroke); $keystrokes[] = phutil_tag('kbd', array(), $stroke); } $keystrokes = phutil_implode_html(' or ', $keystrokes); diff --git a/webroot/rsrc/js/application/core/KeyboardShortcutManager.js b/webroot/rsrc/js/application/core/KeyboardShortcutManager.js index 637fc1f8c3..5677335781 100644 --- a/webroot/rsrc/js/application/core/KeyboardShortcutManager.js +++ b/webroot/rsrc/js/application/core/KeyboardShortcutManager.js @@ -20,6 +20,18 @@ JX.install('KeyboardShortcutManager', { statics : { _instance : null, + + /** + * Some keys don't invoke keypress events in some browsers. We handle these + * on keydown instead of keypress. + */ + _downkeys : { + left: 1, + right: 1, + up: 1, + down: 1 + }, + getInstance : function() { if (!JX.KeyboardShortcutManager._instance) { JX.KeyboardShortcutManager._instance = new JX.KeyboardShortcutManager(); @@ -98,6 +110,11 @@ JX.install('KeyboardShortcutManager', { this._focusReticle = null; }, _onkeypress : function(e) { + if (!(this._getKey(e) in JX.KeyboardShortcutManager._downkeys)) { + this._onkeyhit(e); + } + }, + _onkeyhit : function(e) { var raw = e.getRawEvent(); if (raw.altKey || raw.ctrlKey || raw.metaKey) { @@ -113,8 +130,8 @@ JX.install('KeyboardShortcutManager', { // focused. return; } - // TODO: This likely needs to be refined to deal with arrow keys, etc. - var key = String.fromCharCode(raw.charCode); + + var key = this._getKey(e); var shortcuts = this._shortcuts; for (var ii = 0; ii < shortcuts.length; ii++) { @@ -130,10 +147,17 @@ JX.install('KeyboardShortcutManager', { }, _onkeydown : function(e) { this._handleTooltipKeyEvent(e, true); + + if (this._getKey(e) in JX.KeyboardShortcutManager._downkeys) { + this._onkeyhit(e); + } }, _onkeyup : function(e) { this._handleTooltipKeyEvent(e, false); }, + _getKey : function(e) { + return e.getSpecialKey() || String.fromCharCode(e.getRawEvent().charCode); + }, _handleTooltipKeyEvent : function(e, is_keydown) { if (e.getRawEvent().keyCode != 18) { // If this isn't the alt/option key, don't do anything. diff --git a/webroot/rsrc/js/application/pholio/behavior-pholio-mock-view.js b/webroot/rsrc/js/application/pholio/behavior-pholio-mock-view.js index 4d5105fe93..b3c2cd6aa4 100644 --- a/webroot/rsrc/js/application/pholio/behavior-pholio-mock-view.js +++ b/webroot/rsrc/js/application/pholio/behavior-pholio-mock-view.js @@ -586,13 +586,13 @@ JX.behavior('pholio-mock-view', function(config) { /* -( Keyboard Shortcuts )------------------------------------------------- */ - new JX.KeyboardShortcut('j', 'Show next image.') + new JX.KeyboardShortcut(['j', 'right'], 'Show next image.') .setHandler(function() { switch_image(1); }) .register(); - new JX.KeyboardShortcut('k', 'Show previous image.') + new JX.KeyboardShortcut(['k', 'left'], 'Show previous image.') .setHandler(function() { switch_image(-1); })