浏览代码

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

Attach keyboard shortcuts to features
master
hristoterezov 8 年前
父节点
当前提交
cac7ccf176

+ 5
- 0
css/keyboard-shortcuts.css 查看文件

16
 #keyboard-shortcuts .item-action {
16
 #keyboard-shortcuts .item-action {
17
     color: #209EFF;
17
     color: #209EFF;
18
     font-size: 14pt;
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 查看文件

279
     <div id="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;">
279
     <div id="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;">
280
         <div class="header"><h3 data-i18n="keyboardShortcuts.keyboardShortcuts"></h3></div>
280
         <div class="header"><h3 data-i18n="keyboardShortcuts.keyboardShortcuts"></h3></div>
281
         <div class="content">
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
             </ul>
283
             </ul>
344
         </div>
284
         </div>
345
     </div>
285
     </div>

+ 33
- 3
modules/UI/toolbars/BottomToolbar.js 查看文件

3
 import UIEvents from '../../../service/UI/UIEvents';
3
 import UIEvents from '../../../service/UI/UIEvents';
4
 
4
 
5
 const defaultBottomToolbarButtons = {
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
 const BottomToolbar = {
24
 const BottomToolbar = {
32
     isEnabled() {
45
     isEnabled() {
33
         return this.enabled;
46
         return this.enabled;
34
     },
47
     },
48
+
35
     setupListeners (emitter) {
49
     setupListeners (emitter) {
36
         UIUtil.hideDisabledButtons(defaultBottomToolbarButtons);
50
         UIUtil.hideDisabledButtons(defaultBottomToolbarButtons);
37
 
51
 
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
         Object.keys(buttonHandlers).forEach(
85
         Object.keys(buttonHandlers).forEach(
56
             buttonId => $(`#${buttonId}`).click(buttonHandlers[buttonId])
86
             buttonId => $(`#${buttonId}`).click(buttonHandlers[buttonId])
57
         );
87
         );

+ 76
- 11
modules/UI/toolbars/Toolbar.js 查看文件

110
     },
110
     },
111
     "toolbar_button_fullScreen": function() {
111
     "toolbar_button_fullScreen": function() {
112
         JitsiMeetJS.analytics.sendEvent('toolbar.fullscreen.enabled');
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
         emitter.emit(UIEvents.FULLSCREEN_TOGGLE);
115
         emitter.emit(UIEvents.FULLSCREEN_TOGGLE);
115
     },
116
     },
116
     "toolbar_button_sip": function () {
117
     "toolbar_button_sip": function () {
152
     }
153
     }
153
 };
154
 };
154
 const defaultToolbarButtons = {
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
 function dialpadButtonClicked() {
216
 function dialpadButtonClicked() {
197
 
246
 
198
         UIUtil.hideDisabledButtons(defaultToolbarButtons);
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
         Object.keys(buttonHandlers).forEach(
265
         Object.keys(buttonHandlers).forEach(
201
             buttonId => $(`#${buttonId}`).click(function(event) {
266
             buttonId => $(`#${buttonId}`).click(function(event) {
202
                 !$(this).prop('disabled') && buttonHandlers[buttonId](event);
267
                 !$(this).prop('disabled') && buttonHandlers[buttonId](event);

+ 1
- 1
modules/UI/util/UIUtil.js 查看文件

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

+ 144
- 84
modules/keyboardshortcut/keyboardshortcut.js 查看文件

1
 /* global APP, $, JitsiMeetJS */
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
 var KeyboardShortcut = {
47
 var KeyboardShortcut = {
78
     init: function () {
48
     init: function () {
79
-        initShortcutHandlers();
49
+        initGlobalShortcuts();
50
+
80
         var self = this;
51
         var self = this;
81
         window.onkeyup = function(e) {
52
         window.onkeyup = function(e) {
82
-            var key = self.getKeyboardKey(e).toUpperCase();
53
+            var key = self._getKeyboardKey(e).toUpperCase();
83
             var num = parseInt(key, 10);
54
             var num = parseInt(key, 10);
84
             if(!($(":focus").is("input[type=text]") ||
55
             if(!($(":focus").is("input[type=text]") ||
85
                 $(":focus").is("input[type=password]") ||
56
                 $(":focus").is("input[type=password]") ||
86
                 $(":focus").is("textarea"))) {
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
                 else if (!isNaN(num) && num >= 0 && num <= 9) {
61
                 else if (!isNaN(num) && num >= 0 && num <= 9) {
91
                     APP.UI.clickOnVideo(num + 1);
62
                     APP.UI.clickOnVideo(num + 1);
101
             if(!($(":focus").is("input[type=text]") ||
72
             if(!($(":focus").is("input[type=text]") ||
102
                 $(":focus").is("input[type=password]") ||
73
                 $(":focus").is("input[type=password]") ||
103
                 $(":focus").is("textarea"))) {
74
                 $(":focus").is("textarea"))) {
104
-                var key = self.getKeyboardKey(e).toUpperCase();
75
+                var key = self._getKeyboardKey(e).toUpperCase();
105
                 if(key === "T") {
76
                 if(key === "T") {
106
                     if(APP.conference.isLocalAudioMuted())
77
                     if(APP.conference.isLocalAudioMuted())
107
                         APP.conference.muteAudio(false);
78
                         APP.conference.muteAudio(false);
112
             trigger: 'click hover',
83
             trigger: 'click hover',
113
             content: function() {
84
             content: function() {
114
                 return this.getAttribute("content") +
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
      * @param id indicates the popover associated with the shortcut
130
      * @param id indicates the popover associated with the shortcut
122
      * @returns {string} the keyboard shortcut used for the id given
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
      * @param e a KeyboardEvent
144
      * @param e a KeyboardEvent
136
      * @returns {string} e.key or something close if not supported
145
      * @returns {string} e.key or something close if not supported
137
      */
146
      */
138
-    getKeyboardKey: function (e) {
147
+    _getKeyboardKey: function (e) {
139
         if (typeof e.key === "string") {
148
         if (typeof e.key === "string") {
140
             return e.key;
149
             return e.key;
141
         }
150
         }
156
         } else {
165
         } else {
157
             return String.fromCharCode(e.which).toLowerCase();
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
 

正在加载...
取消
保存