Skip to content

Commit 48ec1f6

Browse files
author
epriestley
committed
Provide basic structure for keyboard shortcuts
Summary: Implements a simple infrastructure for keyboard shortcuts, see T184, and a "help" shortcut. There's a lot of room for refinement here but I think it basically works. Each shortcut can also provide a "tooltip" handler which allows it to show help when the alt/option key is held down. Test Plan: Pressed "?" and got help. Pressed "?" in various contexts where it should not activate (modifier keys, text input focused) and didn't get help. Reviewers: aran, tuomaspelkonen, jungejason CC: moskov Differential Revision: 362
1 parent 404c328 commit 48ec1f6

File tree

14 files changed

+411
-45
lines changed

14 files changed

+411
-45
lines changed

scripts/celerity_mapper.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
'javelin-mask',
2828
'javelin-workflow',
2929
'javelin-behavior-workflow',
30+
'javelin-behavior-aphront-form-disable-on-submit',
31+
'phabricator-keyboard-shortcut-manager',
32+
'phabricator-keyboard-shortcut',
33+
'javelin-behavior-phabricator-keyboard-shortcuts',
3034
),
3135
'core.pkg.css' => array(
3236
'phabricator-core-css',

src/__celerity_resource_map__.php

Lines changed: 88 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -188,24 +188,24 @@
188188
),
189189
'disk' => '/rsrc/css/application/differential/revision-comment-list.css',
190190
),
191-
0 =>
191+
'differential-revision-detail-css' =>
192192
array(
193-
'uri' => '/res/39de799e/rsrc/js/javelin/docs/Base.js',
194-
'type' => 'js',
193+
'uri' => '/res/ea9de420/rsrc/css/application/differential/revision-detail.css',
194+
'type' => 'css',
195195
'requires' =>
196196
array(
197-
0 => 'javelin-install',
198197
),
199-
'disk' => '/rsrc/js/javelin/docs/Base.js',
198+
'disk' => '/rsrc/css/application/differential/revision-detail.css',
200199
),
201-
'differential-revision-detail-css' =>
200+
0 =>
202201
array(
203-
'uri' => '/res/ea9de420/rsrc/css/application/differential/revision-detail.css',
204-
'type' => 'css',
202+
'uri' => '/res/39de799e/rsrc/js/javelin/docs/Base.js',
203+
'type' => 'js',
205204
'requires' =>
206205
array(
206+
0 => 'javelin-install',
207207
),
208-
'disk' => '/rsrc/css/application/differential/revision-detail.css',
208+
'disk' => '/rsrc/js/javelin/docs/Base.js',
209209
),
210210
'differential-revision-history-css' =>
211211
array(
@@ -546,6 +546,19 @@
546546
),
547547
'disk' => '/rsrc/js/application/owners/owners-path-editor.js',
548548
),
549+
'javelin-behavior-phabricator-keyboard-shortcuts' =>
550+
array(
551+
'uri' => '/res/5a23bcc8/rsrc/js/application/core/behavior-keyboard-shortcuts.js',
552+
'type' => 'js',
553+
'requires' =>
554+
array(
555+
0 => 'javelin-behavior',
556+
1 => 'javelin-workflow',
557+
2 => 'javelin-json',
558+
3 => 'phabricator-keyboard-shortcut',
559+
),
560+
'disk' => '/rsrc/js/application/core/behavior-keyboard-shortcuts.js',
561+
),
549562
'javelin-behavior-phabricator-object-selector' =>
550563
array(
551564
'uri' => '/res/12d4d90d/rsrc/js/application/core/behavior-object-selector.js',
@@ -923,6 +936,31 @@
923936
),
924937
'disk' => '/rsrc/js/application/core/DragAndDropFileUpload.js',
925938
),
939+
'phabricator-keyboard-shortcut' =>
940+
array(
941+
'uri' => '/res/beed38cd/rsrc/js/application/core/KeyboardShortcut.js',
942+
'type' => 'js',
943+
'requires' =>
944+
array(
945+
0 => 'javelin-install',
946+
1 => 'javelin-util',
947+
2 => 'phabricator-keyboard-shortcut-manager',
948+
),
949+
'disk' => '/rsrc/js/application/core/KeyboardShortcut.js',
950+
),
951+
'phabricator-keyboard-shortcut-manager' =>
952+
array(
953+
'uri' => '/res/b32845bd/rsrc/js/application/core/KeyboardShortcutManager.js',
954+
'type' => 'js',
955+
'requires' =>
956+
array(
957+
0 => 'javelin-install',
958+
1 => 'javelin-util',
959+
2 => 'javelin-stratcom',
960+
3 => 'javelin-dom',
961+
),
962+
'disk' => '/rsrc/js/application/core/KeyboardShortcutManager.js',
963+
),
926964
'phabricator-object-selector-css' =>
927965
array(
928966
'uri' => '/res/ced4098a/rsrc/css/application/objectselector/object-selector.css',
@@ -965,7 +1003,7 @@
9651003
),
9661004
'phabricator-standard-page-view' =>
9671005
array(
968-
'uri' => '/res/4db19fcb/rsrc/css/application/base/standard-page-view.css',
1006+
'uri' => '/res/02ae6920/rsrc/css/application/base/standard-page-view.css',
9691007
'type' => 'css',
9701008
'requires' =>
9711009
array(
@@ -1003,18 +1041,6 @@
10031041
'uri' => '/res/pkg/03ef179e/diffusion.pkg.css',
10041042
'type' => 'css',
10051043
),
1006-
'122a6b6d' =>
1007-
array (
1008-
'name' => 'workflow.pkg.js',
1009-
'symbols' =>
1010-
array (
1011-
0 => 'javelin-mask',
1012-
1 => 'javelin-workflow',
1013-
2 => 'javelin-behavior-workflow',
1014-
),
1015-
'uri' => '/res/pkg/122a6b6d/workflow.pkg.js',
1016-
'type' => 'js',
1017-
),
10181044
'33f413ef' =>
10191045
array (
10201046
'name' => 'typeahead.pkg.js',
@@ -1048,7 +1074,7 @@
10481074
'uri' => '/res/pkg/613cf273/differential.pkg.css',
10491075
'type' => 'css',
10501076
),
1051-
'c9c3eee2' =>
1077+
'64383b02' =>
10521078
array (
10531079
'name' => 'core.pkg.css',
10541080
'symbols' =>
@@ -1069,7 +1095,7 @@
10691095
13 => 'phabricator-remarkup-css',
10701096
14 => 'syntax-highlighting-css',
10711097
),
1072-
'uri' => '/res/pkg/c9c3eee2/core.pkg.css',
1098+
'uri' => '/res/pkg/64383b02/core.pkg.css',
10731099
'type' => 'css',
10741100
),
10751101
'db95a6d0' =>
@@ -1091,6 +1117,22 @@
10911117
'uri' => '/res/pkg/db95a6d0/javelin.pkg.js',
10921118
'type' => 'js',
10931119
),
1120+
'e26c5e06' =>
1121+
array (
1122+
'name' => 'workflow.pkg.js',
1123+
'symbols' =>
1124+
array (
1125+
0 => 'javelin-mask',
1126+
1 => 'javelin-workflow',
1127+
2 => 'javelin-behavior-workflow',
1128+
3 => 'javelin-behavior-aphront-form-disable-on-submit',
1129+
4 => 'phabricator-keyboard-shortcut-manager',
1130+
5 => 'phabricator-keyboard-shortcut',
1131+
6 => 'javelin-behavior-phabricator-keyboard-shortcuts',
1132+
),
1133+
'uri' => '/res/pkg/e26c5e06/workflow.pkg.js',
1134+
'type' => 'js',
1135+
),
10941136
'ed383f69' =>
10951137
array (
10961138
'name' => 'differential.pkg.js',
@@ -1108,15 +1150,15 @@
11081150
),
11091151
'reverse' =>
11101152
array (
1111-
'aphront-crumbs-view-css' => 'c9c3eee2',
1112-
'aphront-dialog-view-css' => 'c9c3eee2',
1113-
'aphront-form-view-css' => 'c9c3eee2',
1114-
'aphront-list-filter-view-css' => 'c9c3eee2',
1115-
'aphront-panel-view-css' => 'c9c3eee2',
1116-
'aphront-side-nav-view-css' => 'c9c3eee2',
1117-
'aphront-table-view-css' => 'c9c3eee2',
1118-
'aphront-tokenizer-control-css' => 'c9c3eee2',
1119-
'aphront-typeahead-control-css' => 'c9c3eee2',
1153+
'aphront-crumbs-view-css' => '64383b02',
1154+
'aphront-dialog-view-css' => '64383b02',
1155+
'aphront-form-view-css' => '64383b02',
1156+
'aphront-list-filter-view-css' => '64383b02',
1157+
'aphront-panel-view-css' => '64383b02',
1158+
'aphront-side-nav-view-css' => '64383b02',
1159+
'aphront-table-view-css' => '64383b02',
1160+
'aphront-tokenizer-control-css' => '64383b02',
1161+
'aphront-typeahead-control-css' => '64383b02',
11201162
'differential-changeset-view-css' => '613cf273',
11211163
'differential-core-view-css' => '613cf273',
11221164
'differential-revision-add-comment-css' => '613cf273',
@@ -1128,17 +1170,19 @@
11281170
'diffusion-commit-view-css' => '03ef179e',
11291171
'javelin-behavior' => 'db95a6d0',
11301172
'javelin-behavior-aphront-basic-tokenizer' => '33f413ef',
1173+
'javelin-behavior-aphront-form-disable-on-submit' => 'e26c5e06',
11311174
'javelin-behavior-differential-diff-radios' => 'ed383f69',
11321175
'javelin-behavior-differential-edit-inline-comments' => 'ed383f69',
11331176
'javelin-behavior-differential-feedback-preview' => 'ed383f69',
11341177
'javelin-behavior-differential-populate' => 'ed383f69',
11351178
'javelin-behavior-differential-show-more' => 'ed383f69',
1136-
'javelin-behavior-workflow' => '122a6b6d',
1179+
'javelin-behavior-phabricator-keyboard-shortcuts' => 'e26c5e06',
1180+
'javelin-behavior-workflow' => 'e26c5e06',
11371181
'javelin-dom' => 'db95a6d0',
11381182
'javelin-event' => 'db95a6d0',
11391183
'javelin-install' => 'db95a6d0',
11401184
'javelin-json' => 'db95a6d0',
1141-
'javelin-mask' => '122a6b6d',
1185+
'javelin-mask' => 'e26c5e06',
11421186
'javelin-request' => 'db95a6d0',
11431187
'javelin-stratcom' => 'db95a6d0',
11441188
'javelin-tokenizer' => '33f413ef',
@@ -1150,12 +1194,14 @@
11501194
'javelin-uri' => 'db95a6d0',
11511195
'javelin-util' => 'db95a6d0',
11521196
'javelin-vector' => 'db95a6d0',
1153-
'javelin-workflow' => '122a6b6d',
1154-
'phabricator-core-buttons-css' => 'c9c3eee2',
1155-
'phabricator-core-css' => 'c9c3eee2',
1156-
'phabricator-directory-css' => 'c9c3eee2',
1157-
'phabricator-remarkup-css' => 'c9c3eee2',
1158-
'phabricator-standard-page-view' => 'c9c3eee2',
1159-
'syntax-highlighting-css' => 'c9c3eee2',
1197+
'javelin-workflow' => 'e26c5e06',
1198+
'phabricator-core-buttons-css' => '64383b02',
1199+
'phabricator-core-css' => '64383b02',
1200+
'phabricator-directory-css' => '64383b02',
1201+
'phabricator-keyboard-shortcut' => 'e26c5e06',
1202+
'phabricator-keyboard-shortcut-manager' => 'e26c5e06',
1203+
'phabricator-remarkup-css' => '64383b02',
1204+
'phabricator-standard-page-view' => '64383b02',
1205+
'syntax-highlighting-css' => '64383b02',
11601206
),
11611207
));

src/__phutil_library_map__.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@
330330
'PhabricatorFileViewController' => 'applications/files/controller/view',
331331
'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing',
332332
'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector',
333+
'PhabricatorHelpController' => 'applications/help/controller/base',
334+
'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/keyboardshortcut',
333335
'PhabricatorIRCBot' => 'infrastructure/daemon/irc/bot',
334336
'PhabricatorIRCHandler' => 'infrastructure/daemon/irc/handler/base',
335337
'PhabricatorIRCMessage' => 'infrastructure/daemon/irc/message',
@@ -777,6 +779,8 @@
777779
'PhabricatorFileUploadController' => 'PhabricatorFileController',
778780
'PhabricatorFileViewController' => 'PhabricatorFileController',
779781
'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker',
782+
'PhabricatorHelpController' => 'PhabricatorController',
783+
'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController',
780784
'PhabricatorIRCBot' => 'PhabricatorDaemon',
781785
'PhabricatorIRCObjectNameHandler' => 'PhabricatorIRCHandler',
782786
'PhabricatorIRCProtocolHandler' => 'PhabricatorIRCHandler',

src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,9 @@ public function getURIMap() {
305305

306306
'/status/$' => 'PhabricatorStatusController',
307307

308+
'/help/' => array(
309+
'keyboardshortcut/$' => 'PhabricatorHelpKeyboardShortcutController',
310+
),
308311
);
309312
}
310313

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
/*
4+
* Copyright 2011 Facebook, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
abstract class PhabricatorHelpController extends PhabricatorController {
20+
21+
public function buildStandardPageResponse($view, array $data) {
22+
$page = $this->buildStandardPageView();
23+
24+
$page->setApplicationName('Help');
25+
$page->setBaseURI('/help/');
26+
$page->setTitle(idx($data, 'title'));
27+
$page->setGlyph('?');
28+
$page->appendChild($view);
29+
30+
$response = new AphrontWebpageResponse();
31+
return $response->setContent($page->render());
32+
}
33+
34+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
/**
3+
* This file is automatically generated. Lint this module to rebuild it.
4+
* @generated
5+
*/
6+
7+
8+
9+
phutil_require_module('phabricator', 'aphront/response/webpage');
10+
phutil_require_module('phabricator', 'applications/base/controller/base');
11+
12+
phutil_require_module('phutil', 'utils');
13+
14+
15+
phutil_require_source('PhabricatorHelpController.php');
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
/*
4+
* Copyright 2011 Facebook, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
class PhabricatorHelpKeyboardShortcutController
20+
extends PhabricatorHelpController {
21+
22+
public function processRequest() {
23+
$request = $this->getRequest();
24+
$user = $request->getUser();
25+
26+
$keys = $request->getStr('keys');
27+
$keys = json_decode($keys, true);
28+
if (!is_array($keys)) {
29+
return new Aphront400Response();
30+
}
31+
32+
$rows = array();
33+
foreach ($keys as $shortcut) {
34+
$keystrokes = array();
35+
foreach ($shortcut['keys'] as $stroke) {
36+
$keystrokes[] = '<kbd>'.phutil_escape_html($stroke).'</kbd>';
37+
}
38+
$keystrokes = implode(' or ', $keystrokes);
39+
$rows[] =
40+
'<tr>'.
41+
'<th>'.$keystrokes.'</th>'.
42+
'<td>'.phutil_escape_html($shortcut['description']).'</td>'.
43+
'</tr>';
44+
}
45+
46+
$table =
47+
'<table class="keyboard-shortcut-help">'.
48+
implode('', $rows).
49+
'</table>';
50+
51+
$dialog = id(new AphrontDialogView())
52+
->setUser($user)
53+
->setTitle('Keyboard Shortcuts')
54+
->appendChild($table)
55+
->addCancelButton('#', 'Close');
56+
57+
return id(new AphrontDialogResponse())
58+
->setDialog($dialog);
59+
}
60+
61+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
/**
3+
* This file is automatically generated. Lint this module to rebuild it.
4+
* @generated
5+
*/
6+
7+
8+
9+
phutil_require_module('phabricator', 'aphront/response/400');
10+
phutil_require_module('phabricator', 'aphront/response/dialog');
11+
phutil_require_module('phabricator', 'applications/help/controller/base');
12+
phutil_require_module('phabricator', 'view/dialog');
13+
14+
phutil_require_module('phutil', 'markup');
15+
phutil_require_module('phutil', 'utils');
16+
17+
18+
phutil_require_source('PhabricatorHelpKeyboardShortcutController.php');

0 commit comments

Comments
 (0)