Bläddra i källkod

Merge pull request #812 from jitsi/attach-shortcuts-to-features

Attach keyboard shortcuts to features
master
hristoterezov 8 år sedan
förälder
incheckning
cac7ccf176

+ 5
- 0
css/keyboard-shortcuts.css Visa fil

@@ -16,4 +16,9 @@
16 16
 #keyboard-shortcuts .item-action {
17 17
     color: #209EFF;
18 18
     font-size: 14pt;
19
+    padding-right: 5px;
20
+}
21
+
22
+#keyboard-shortcuts-list {
23
+    list-style-type: none;
19 24
 }

+ 1
- 61
index.html Visa fil

@@ -279,67 +279,7 @@
279 279
     <div id="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;">
280 280
         <div class="header"><h3 data-i18n="keyboardShortcuts.keyboardShortcuts"></h3></div>
281 281
         <div class="content">
282
-            <ul class="item">
283
-                <li>
284
-                    <span class="item-action">
285
-                        <kbd class="regular-key">M</kbd>
286
-                    </span>
287
-                    <span class="item-description" data-i18n="keyboardShortcuts.mute"></span>
288
-                </li>
289
-                <li>
290
-                    <span class="item-action">
291
-                        <kbd class="regular-key">V</kbd>
292
-                    </span>
293
-                    <span class="item-description" data-i18n="keyboardShortcuts.videoMute"></span>
294
-                </li>
295
-                <li>
296
-                    <span class="item-action">
297
-                        <kbd class="regular-key">C</kbd>
298
-                    </span>
299
-                    <span class="item-description" data-i18n="keyboardShortcuts.toggleChat"></span>
300
-                </li>
301
-                <li>
302
-                    <span class="item-action">
303
-                        <kbd class="regular-key">R</kbd>
304
-                    </span>
305
-                    <span class="item-description" data-i18n="keyboardShortcuts.raiseHand"></span>
306
-                </li>
307
-                <li>
308
-                    <span class="item-action">
309
-                        <kbd class="regular-key">T</kbd>
310
-                    </span>
311
-                    <span class="item-description" data-i18n="keyboardShortcuts.pushToTalk"></span>
312
-                </li>
313
-                <li>
314
-                    <span class="item-action">
315
-                        <kbd class="regular-key">D</kbd>
316
-                    </span>
317
-                    <span class="item-description" data-i18n="keyboardShortcuts.toggleScreensharing"></span>
318
-                </li>
319
-                <li class="item-details">
320
-                    <span class="item-action">
321
-                        <kbd class="regular-key">F</kbd>
322
-                    </span>
323
-                    <span class="item-description" data-i18n="keyboardShortcuts.toggleFilmstrip"></span>
324
-                </li>
325
-                <li>
326
-                    <span class="item-action">
327
-                        <kbd class="regular-key">?</kbd>
328
-                    </span>
329
-                    <span class="item-description" data-i18n="keyboardShortcuts.toggleShortcuts"></span>
330
-                </li>
331
-                <li>
332
-                    <span class="item-action">
333
-                        <kbd class="regular-key">0</kbd>
334
-                    </span>
335
-                    <span class="item-description" data-i18n="keyboardShortcuts.focusLocal"></span>
336
-                </li>
337
-                <li>
338
-                    <span class="item-action">
339
-                        <kbd class="regular-key">1-9</kbd>
340
-                    </span>
341
-                    <span class="item-description" data-i18n="keyboardShortcuts.focusRemote"></span>
342
-                </li>
282
+            <ul id="keyboard-shortcuts-list" class="item">
343 283
             </ul>
344 284
         </div>
345 285
     </div>

+ 33
- 3
modules/UI/toolbars/BottomToolbar.js Visa fil

@@ -3,9 +3,22 @@ import UIUtil from '../util/UIUtil';
3 3
 import UIEvents from '../../../service/UI/UIEvents';
4 4
 
5 5
 const defaultBottomToolbarButtons = {
6
-    'chat':      '#bottom_toolbar_chat',
7
-    'contacts':  '#bottom_toolbar_contact_list',
8
-    'filmstrip': '#bottom_toolbar_film_strip'
6
+    'chat': {
7
+        id: '#bottom_toolbar_chat'
8
+    },
9
+    'contacts': {
10
+        id: '#bottom_toolbar_contact_list'
11
+    },
12
+    'filmstrip': {
13
+        id: '#bottom_toolbar_film_strip',
14
+        shortcut: "F",
15
+        shortcutAttr: "filmstripPopover",
16
+        shortcutFunc: function() {
17
+            JitsiMeetJS.analytics.sendEvent("shortcut.film.toggled");
18
+            APP.UI.toggleFilmStrip();
19
+        },
20
+        shortcutDescription: "keyboardShortcuts.toggleFilmstrip"
21
+    }
9 22
 };
10 23
 
11 24
 const BottomToolbar = {
@@ -32,6 +45,7 @@ const BottomToolbar = {
32 45
     isEnabled() {
33 46
         return this.enabled;
34 47
     },
48
+
35 49
     setupListeners (emitter) {
36 50
         UIUtil.hideDisabledButtons(defaultBottomToolbarButtons);
37 51
 
@@ -52,6 +66,22 @@ const BottomToolbar = {
52 66
             }
53 67
         };
54 68
 
69
+        Object.keys(defaultBottomToolbarButtons).forEach(
70
+                id => {
71
+                if (UIUtil.isButtonEnabled(id)) {
72
+                    var button = defaultBottomToolbarButtons[id];
73
+
74
+                    if (button.shortcut)
75
+                        APP.keyboardshortcut.registerShortcut(
76
+                            button.shortcut,
77
+                            button.shortcutAttr,
78
+                            button.shortcutFunc,
79
+                            button.shortcutDescription
80
+                        );
81
+                }
82
+            }
83
+        );
84
+
55 85
         Object.keys(buttonHandlers).forEach(
56 86
             buttonId => $(`#${buttonId}`).click(buttonHandlers[buttonId])
57 87
         );

+ 76
- 11
modules/UI/toolbars/Toolbar.js Visa fil

@@ -110,7 +110,8 @@ const buttonHandlers = {
110 110
     },
111 111
     "toolbar_button_fullScreen": function() {
112 112
         JitsiMeetJS.analytics.sendEvent('toolbar.fullscreen.enabled');
113
-        UIUtil.buttonClick("#toolbar_button_fullScreen", "icon-full-screen icon-exit-full-screen");
113
+        UIUtil.buttonClick("#toolbar_button_fullScreen",
114
+            "icon-full-screen icon-exit-full-screen");
114 115
         emitter.emit(UIEvents.FULLSCREEN_TOGGLE);
115 116
     },
116 117
     "toolbar_button_sip": function () {
@@ -152,16 +153,64 @@ const buttonHandlers = {
152 153
     }
153 154
 };
154 155
 const defaultToolbarButtons = {
155
-    'microphone': '#toolbar_button_mute',
156
-    'camera':     '#toolbar_button_camera',
157
-    'desktop':    '#toolbar_button_desktopsharing',
158
-    'security':   '#toolbar_button_security',
159
-    'invite':     '#toolbar_button_link',
160
-    'chat':       '#toolbar_button_chat',
161
-    'etherpad':   '#toolbar_button_etherpad',
162
-    'fullscreen': '#toolbar_button_fullScreen',
163
-    'settings':   '#toolbar_button_settings',
164
-    'hangup':     '#toolbar_button_hangup'
156
+    'microphone': {
157
+        id: '#toolbar_button_mute',
158
+        shortcut: 'M',
159
+        shortcutAttr: 'mutePopover',
160
+        shortcutFunc: function() {
161
+            JitsiMeetJS.analytics.sendEvent('shortcut.audiomute.toggled');
162
+            APP.conference.toggleAudioMuted();
163
+        },
164
+        shortcutDescription: "keyboardShortcuts.mute"
165
+    },
166
+    'camera': {
167
+        id: '#toolbar_button_camera',
168
+        shortcut: 'V',
169
+        shortcutAttr: 'toggleVideoPopover',
170
+        shortcutFunc: function() {
171
+            JitsiMeetJS.analytics.sendEvent('shortcut.videomute.toggled');
172
+            APP.conference.toggleVideoMuted();
173
+        },
174
+        shortcutDescription: "keyboardShortcuts.videoMute"
175
+    },
176
+    'desktop': {
177
+        id: '#toolbar_button_desktopsharing',
178
+        shortcut: 'D',
179
+        shortcutAttr: 'toggleDesktopSharingPopover',
180
+        shortcutFunc: function() {
181
+            JitsiMeetJS.analytics.sendEvent('shortcut.screen.toggled');
182
+            APP.conference.toggleScreenSharing();
183
+        },
184
+        shortcutDescription: "keyboardShortcuts.toggleScreensharing"
185
+    },
186
+    'security': {
187
+        id: '#toolbar_button_security'
188
+    },
189
+    'invite': {
190
+        id: '#toolbar_button_link'
191
+    },
192
+    'chat': {
193
+        id: '#toolbar_button_chat',
194
+        shortcut: 'C',
195
+        shortcutAttr: 'toggleChatPopover',
196
+        shortcutFunc: function() {
197
+            JitsiMeetJS.analytics.sendEvent('shortcut.chat.toggled');
198
+            APP.UI.toggleChat();
199
+        },
200
+        shortcutDescription: "keyboardShortcuts.toggleChat"
201
+    },
202
+    'etherpad': {
203
+        id: '#toolbar_button_etherpad'
204
+    },
205
+    'fullscreen': {
206
+        id: '#toolbar_button_fullScreen'
207
+    },
208
+    'settings': {
209
+        id: '#toolbar_button_settings'
210
+    },
211
+    'hangup': {
212
+        id: '#toolbar_button_hangup'
213
+    }
165 214
 };
166 215
 
167 216
 function dialpadButtonClicked() {
@@ -197,6 +246,22 @@ const Toolbar = {
197 246
 
198 247
         UIUtil.hideDisabledButtons(defaultToolbarButtons);
199 248
 
249
+        Object.keys(defaultToolbarButtons).forEach(
250
+            id => {
251
+                if (UIUtil.isButtonEnabled(id)) {
252
+                    var button = defaultToolbarButtons[id];
253
+
254
+                    if (button.shortcut)
255
+                        APP.keyboardshortcut.registerShortcut(
256
+                            button.shortcut,
257
+                            button.shortcutAttr,
258
+                            button.shortcutFunc,
259
+                            button.shortcutDescription
260
+                        );
261
+                }
262
+            }
263
+        );
264
+
200 265
         Object.keys(buttonHandlers).forEach(
201 266
             buttonId => $(`#${buttonId}`).click(function(event) {
202 267
                 !$(this).prop('disabled') && buttonHandlers[buttonId](event);

+ 1
- 1
modules/UI/util/UIUtil.js Visa fil

@@ -130,7 +130,7 @@
130 130
         var selector = Object.keys(mappings)
131 131
           .map(function (buttonName) {
132 132
                 return UIUtil.isButtonEnabled(buttonName)
133
-                    ? null : mappings[buttonName]; })
133
+                    ? null : mappings[buttonName].id; })
134 134
           .filter(function (item) { return item; })
135 135
           .join(',');
136 136
         $(selector).hide();

+ 144
- 84
modules/keyboardshortcut/keyboardshortcut.js Visa fil

@@ -1,91 +1,62 @@
1 1
 /* global APP, $, JitsiMeetJS */
2
-//maps keycode to character, id of popover for given function and function
3
-var shortcuts = {};
4
-function initShortcutHandlers() {
5
-    shortcuts = {
6
-        "ESCAPE": {
7
-            character: "Esc",
8
-            function: function() {
9
-                APP.UI.showKeyboardShortcutsPanel(false);
10
-            }
11
-        },
12
-        "C": {
13
-            character: "C",
14
-            id: "toggleChatPopover",
15
-            function: function() {
16
-                JitsiMeetJS.analytics.sendEvent('shortcut.chat.toggled');
17
-                APP.UI.toggleChat();
18
-            }
19
-        },
20
-        "D": {
21
-            character: "D",
22
-            id: "toggleDesktopSharingPopover",
23
-            function: function () {
24
-                JitsiMeetJS.analytics.sendEvent('shortcut.screen.toggled');
25
-                APP.conference.toggleScreenSharing();
26
-            }
27
-        },
28
-        "F": {
29
-            character: "F",
30
-            id: "filmstripPopover",
31
-            function: function() {
32
-                JitsiMeetJS.analytics.sendEvent('shortcut.film.toggled');
33
-                APP.UI.toggleFilmStrip();
34
-            }
35
-        },
36
-        "M": {
37
-            character: "M",
38
-            id: "mutePopover",
39
-            function: function() {
40
-                JitsiMeetJS.analytics.sendEvent('shortcut.audiomute.toggled');
41
-                APP.conference.toggleAudioMuted();
42
-            }
43
-        },
44
-        "R": {
45
-            character: "R",
46
-            function: function() {
47
-                JitsiMeetJS.analytics.sendEvent('shortcut.raisedhand.toggled');
48
-                APP.conference.maybeToggleRaisedHand();
49
-            }
50 2
 
51
-        },
52
-        "T": {
53
-            character: "T",
54
-            function: function() {
55
-                JitsiMeetJS.analytics.sendEvent('shortcut.talk.clicked');
56
-                APP.conference.muteAudio(true);
57
-            }
58
-        },
59
-        "V": {
60
-            character: "V",
61
-            id: "toggleVideoPopover",
62
-            function: function() {
63
-                JitsiMeetJS.analytics.sendEvent('shortcut.videomute.toggled');
64
-                APP.conference.toggleVideoMuted();
65
-            }
66
-        },
67
-        "?": {
68
-            character: "?",
69
-            function: function(e) {
70
-                JitsiMeetJS.analytics.sendEvent('shortcut.shortcut.help');
71
-                APP.UI.toggleKeyboardShortcutsPanel();
72
-            }
73
-        }
74
-    };
3
+/**
4
+ * Initialise global shortcuts.
5
+ * Global shortcuts are shortcuts for features that don't have a button or
6
+ * link associated with the action. In other words they represent actions
7
+ * triggered _only_ with a shortcut.
8
+ */
9
+function initGlobalShortcuts() {
10
+
11
+    KeyboardShortcut.registerShortcut("ESCAPE", null, function() {
12
+        APP.UI.showKeyboardShortcutsPanel(false);
13
+    });
14
+
15
+    KeyboardShortcut.registerShortcut("?", null, function() {
16
+        JitsiMeetJS.analytics.sendEvent("shortcut.shortcut.help");
17
+        APP.UI.toggleKeyboardShortcutsPanel();
18
+    }, "keyboardShortcuts.toggleShortcuts");
19
+
20
+    KeyboardShortcut.registerShortcut("R", null, function() {
21
+        JitsiMeetJS.analytics.sendEvent("shortcut.raisedhand.toggled");
22
+        APP.conference.maybeToggleRaisedHand();
23
+    }, "keyboardShortcuts.raiseHand");
24
+
25
+    KeyboardShortcut.registerShortcut("T", null, function() {
26
+        JitsiMeetJS.analytics.sendEvent("shortcut.talk.clicked");
27
+        APP.conference.muteAudio(true);
28
+    }, "keyboardShortcuts.pushToTalk");
29
+
30
+    /**
31
+     * FIXME: Currently focus keys are directly implemented below in onkeyup.
32
+     * They should be moved to the SmallVideo instead.
33
+     */
34
+    KeyboardShortcut._addShortcutToHelp("0", "keyboardShortcuts.focusLocal");
35
+    KeyboardShortcut._addShortcutToHelp("1-9", "keyboardShortcuts.focusRemote");
75 36
 }
76 37
 
38
+/**
39
+ * Map of shortcuts. When a shortcut is registered it enters the mapping.
40
+ * @type {{}}
41
+ */
42
+let _shortcuts = {};
43
+
44
+/**
45
+ * Maps keycode to character, id of popover for given function and function.
46
+ */
77 47
 var KeyboardShortcut = {
78 48
     init: function () {
79
-        initShortcutHandlers();
49
+        initGlobalShortcuts();
50
+
80 51
         var self = this;
81 52
         window.onkeyup = function(e) {
82
-            var key = self.getKeyboardKey(e).toUpperCase();
53
+            var key = self._getKeyboardKey(e).toUpperCase();
83 54
             var num = parseInt(key, 10);
84 55
             if(!($(":focus").is("input[type=text]") ||
85 56
                 $(":focus").is("input[type=password]") ||
86 57
                 $(":focus").is("textarea"))) {
87
-                if (shortcuts.hasOwnProperty(key)) {
88
-                    shortcuts[key].function(e);
58
+                if (_shortcuts.hasOwnProperty(key)) {
59
+                    _shortcuts[key].function(e);
89 60
                 }
90 61
                 else if (!isNaN(num) && num >= 0 && num <= 9) {
91 62
                     APP.UI.clickOnVideo(num + 1);
@@ -101,7 +72,7 @@ var KeyboardShortcut = {
101 72
             if(!($(":focus").is("input[type=text]") ||
102 73
                 $(":focus").is("input[type=password]") ||
103 74
                 $(":focus").is("textarea"))) {
104
-                var key = self.getKeyboardKey(e).toUpperCase();
75
+                var key = self._getKeyboardKey(e).toUpperCase();
105 76
                 if(key === "T") {
106 77
                     if(APP.conference.isLocalAudioMuted())
107 78
                         APP.conference.muteAudio(false);
@@ -112,20 +83,58 @@ var KeyboardShortcut = {
112 83
             trigger: 'click hover',
113 84
             content: function() {
114 85
                 return this.getAttribute("content") +
115
-                    self.getShortcut(this.getAttribute("shortcut"));
86
+                    self._getShortcut(this.getAttribute("shortcut"));
116 87
             }
117 88
         });
118 89
     },
90
+
91
+    /**
92
+     * Registers a new shortcut.
93
+     *
94
+     * @param shortcutChar the shortcut character triggering the action
95
+     * @param shortcutAttr the "shortcut" html element attribute mappring an
96
+     * element to this shortcut and used to show the shortcut character on the
97
+     * element tooltip
98
+     * @param exec the function to be executed when the shortcut is pressed
99
+     * @param helpDescription the description of the shortcut that would appear
100
+     * in the help menu
101
+     */
102
+    registerShortcut: function( shortcutChar,
103
+                                shortcutAttr,
104
+                                exec,
105
+                                helpDescription) {
106
+        _shortcuts[shortcutChar] = {
107
+            character: shortcutChar,
108
+            shortcutAttr: shortcutAttr,
109
+            function: exec
110
+        };
111
+
112
+        if (helpDescription)
113
+            this._addShortcutToHelp(shortcutChar, helpDescription);
114
+    },
115
+
116
+    /**
117
+     * Unregisters a shortcut.
118
+     *
119
+     * @param shortcutChar unregisters the given shortcut, which means it will
120
+     * no longer be usable
121
+     */
122
+    unregisterShortcut: function(shortcutChar) {
123
+        _shortcuts.remove(shortcutChar);
124
+
125
+        this._removeShortcutFromHelp(shortcutChar);
126
+    },
127
+
119 128
     /**
120 129
      *
121 130
      * @param id indicates the popover associated with the shortcut
122 131
      * @returns {string} the keyboard shortcut used for the id given
123 132
      */
124
-    getShortcut: function (id) {
125
-        for (var key in shortcuts) {
126
-            if (shortcuts.hasOwnProperty(key)) {
127
-                if (shortcuts[key].id === id) {
128
-                    return " (" + shortcuts[key].character + ")";
133
+    _getShortcut: function (id) {
134
+        for (var key in _shortcuts) {
135
+            if (_shortcuts.hasOwnProperty(key)) {
136
+                if (_shortcuts[key].shortcutAttr === id) {
137
+                    return " (" + _shortcuts[key].character + ")";
129 138
                 }
130 139
             }
131 140
         }
@@ -135,7 +144,7 @@ var KeyboardShortcut = {
135 144
      * @param e a KeyboardEvent
136 145
      * @returns {string} e.key or something close if not supported
137 146
      */
138
-    getKeyboardKey: function (e) {
147
+    _getKeyboardKey: function (e) {
139 148
         if (typeof e.key === "string") {
140 149
             return e.key;
141 150
         }
@@ -156,6 +165,57 @@ var KeyboardShortcut = {
156 165
         } else {
157 166
             return String.fromCharCode(e.which).toLowerCase();
158 167
         }
168
+    },
169
+
170
+    /**
171
+     * Adds the given shortcut to the help dialog.
172
+     *
173
+     * @param shortcutChar the shortcut character
174
+     * @param shortcutDescriptionKey the description of the shortcut
175
+     * @private
176
+     */
177
+    _addShortcutToHelp: function (shortcutChar, shortcutDescriptionKey) {
178
+
179
+        var listElement = document.createElement("li");
180
+        listElement.id = shortcutChar;
181
+
182
+        var spanElement = document.createElement("span");
183
+        spanElement.className = "item-action";
184
+
185
+        var kbdElement = document.createElement("kbd");
186
+        kbdElement.className = "regular-key";
187
+        kbdElement.innerHTML = shortcutChar;
188
+        spanElement.appendChild(kbdElement);
189
+
190
+        var descriptionElement = document.createElement("span");
191
+        descriptionElement.className = "item-description";
192
+        descriptionElement.setAttribute("data-i18n", shortcutDescriptionKey);
193
+        descriptionElement.innerHTML
194
+            = APP.translation.translateString(shortcutDescriptionKey);
195
+
196
+        listElement.appendChild(spanElement);
197
+        listElement.appendChild(descriptionElement);
198
+
199
+        var parentListElement
200
+            = document.getElementById("keyboard-shortcuts-list");
201
+
202
+        if (parentListElement)
203
+            parentListElement.appendChild(listElement);
204
+    },
205
+
206
+    /**
207
+     * Removes the list element corresponding to the given shortcut from the
208
+     * help dialog
209
+     * @private
210
+     */
211
+    _removeShortcutFromHelp: function (shortcutChar) {
212
+        var parentListElement
213
+            = document.getElementById("keyboard-shortcuts-list");
214
+
215
+        var shortcutElement = document.getElementById(shortcutChar);
216
+
217
+        if (shortcutElement)
218
+            parentListElement.removeChild(shortcutElement);
159 219
     }
160 220
 };
161 221
 

Laddar…
Avbryt
Spara