@theia/core 1.77.0-next.56 → 1.77.0-next.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/catalog.json +66 -0
- package/lib/browser/common-frontend-contribution.d.ts +6 -1
- package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
- package/lib/browser/common-frontend-contribution.js +18 -1
- package/lib/browser/common-frontend-contribution.js.map +1 -1
- package/lib/browser/keybinding.d.ts +95 -17
- package/lib/browser/keybinding.d.ts.map +1 -1
- package/lib/browser/keybinding.js +405 -75
- package/lib/browser/keybinding.js.map +1 -1
- package/lib/browser/keybinding.spec.js +786 -30
- package/lib/browser/keybinding.spec.js.map +1 -1
- package/lib/browser/keyboard/browser-keyboard-frontend-contribution.js +1 -1
- package/lib/browser/keyboard/browser-keyboard-frontend-contribution.js.map +1 -1
- package/lib/browser/keyboard/browser-keyboard-layout-provider.d.ts +3 -4
- package/lib/browser/keyboard/browser-keyboard-layout-provider.d.ts.map +1 -1
- package/lib/browser/keyboard/browser-keyboard-layout-provider.js +3 -3
- package/lib/browser/keyboard/browser-keyboard-layout-provider.js.map +1 -1
- package/lib/browser/keyboard/browser-keyboard-layout-provider.spec.js +39 -0
- package/lib/browser/keyboard/browser-keyboard-layout-provider.spec.js.map +1 -1
- package/lib/browser/keyboard/keyboard-layout-service.d.ts +67 -15
- package/lib/browser/keyboard/keyboard-layout-service.d.ts.map +1 -1
- package/lib/browser/keyboard/keyboard-layout-service.js +309 -98
- package/lib/browser/keyboard/keyboard-layout-service.js.map +1 -1
- package/lib/browser/keyboard/keyboard-layout-service.spec.js +538 -12
- package/lib/browser/keyboard/keyboard-layout-service.spec.js.map +1 -1
- package/lib/browser/keyboard/keys.spec.js +183 -0
- package/lib/browser/keyboard/keys.spec.js.map +1 -1
- package/lib/browser/menu/action-menu-node.d.ts +5 -3
- package/lib/browser/menu/action-menu-node.d.ts.map +1 -1
- package/lib/browser/menu/action-menu-node.js +2 -11
- package/lib/browser/menu/action-menu-node.js.map +1 -1
- package/lib/browser/menu/action-menu-node.spec.d.ts +2 -0
- package/lib/browser/menu/action-menu-node.spec.d.ts.map +1 -0
- package/lib/browser/menu/action-menu-node.spec.js +54 -0
- package/lib/browser/menu/action-menu-node.spec.js.map +1 -0
- package/lib/browser/menu/browser-menu-plugin.js +1 -1
- package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
- package/lib/browser/test/jsdom.d.ts.map +1 -1
- package/lib/browser/test/jsdom.js +11 -0
- package/lib/browser/test/jsdom.js.map +1 -1
- package/lib/common/keyboard/keyboard-layout-provider.d.ts +7 -0
- package/lib/common/keyboard/keyboard-layout-provider.d.ts.map +1 -1
- package/lib/common/keyboard/keyboard-layout-provider.js.map +1 -1
- package/lib/common/keys.d.ts +86 -3
- package/lib/common/keys.d.ts.map +1 -1
- package/lib/common/keys.js +262 -23
- package/lib/common/keys.js.map +1 -1
- package/lib/common/ternary-search-tree.js +2 -2
- package/lib/common/ternary-search-tree.js.map +1 -1
- package/lib/common/ternary-search-tree.spec.js +12 -0
- package/lib/common/ternary-search-tree.spec.js.map +1 -1
- package/lib/electron-browser/keyboard/electron-keyboard-module.d.ts +2 -0
- package/lib/electron-browser/keyboard/electron-keyboard-module.d.ts.map +1 -1
- package/lib/electron-browser/keyboard/electron-keyboard-module.js +8 -1
- package/lib/electron-browser/keyboard/electron-keyboard-module.js.map +1 -1
- package/lib/electron-browser/keyboard/electron-keyboard-module.spec.d.ts +2 -0
- package/lib/electron-browser/keyboard/electron-keyboard-module.spec.d.ts.map +1 -0
- package/lib/electron-browser/keyboard/electron-keyboard-module.spec.js +28 -0
- package/lib/electron-browser/keyboard/electron-keyboard-module.spec.js.map +1 -0
- package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.js +3 -2
- package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
- package/lib/electron-browser/menu/electron-menu-contribution.d.ts.map +1 -1
- package/lib/electron-browser/menu/electron-menu-contribution.js +3 -0
- package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
- package/lib/electron-common/electron-api.d.ts +5 -0
- package/lib/electron-common/electron-api.d.ts.map +1 -1
- package/lib/electron-common/electron-api.js +21 -0
- package/lib/electron-common/electron-api.js.map +1 -1
- package/lib/electron-common/electron-api.spec.d.ts +2 -0
- package/lib/electron-common/electron-api.spec.d.ts.map +1 -0
- package/lib/electron-common/electron-api.spec.js +40 -0
- package/lib/electron-common/electron-api.spec.js.map +1 -0
- package/lib/electron-main/electron-api-main.d.ts +0 -1
- package/lib/electron-main/electron-api-main.d.ts.map +1 -1
- package/lib/electron-main/electron-api-main.js +1 -12
- package/lib/electron-main/electron-api-main.js.map +1 -1
- package/package.json +4 -4
- package/src/browser/common-frontend-contribution.ts +16 -0
- package/src/browser/keybinding.spec.ts +878 -34
- package/src/browser/keybinding.ts +470 -85
- package/src/browser/keyboard/browser-keyboard-frontend-contribution.ts +1 -1
- package/src/browser/keyboard/browser-keyboard-layout-provider.spec.ts +52 -1
- package/src/browser/keyboard/browser-keyboard-layout-provider.ts +5 -5
- package/src/browser/keyboard/keyboard-layout-service.spec.ts +640 -15
- package/src/browser/keyboard/keyboard-layout-service.ts +358 -106
- package/src/browser/keyboard/keys.spec.ts +206 -1
- package/src/browser/menu/action-menu-node.spec.ts +60 -0
- package/src/browser/menu/action-menu-node.ts +8 -14
- package/src/browser/menu/browser-menu-plugin.ts +1 -1
- package/src/browser/test/jsdom.ts +11 -0
- package/src/common/keyboard/keyboard-layout-provider.ts +8 -0
- package/src/common/keys.ts +311 -26
- package/src/common/ternary-search-tree.spec.ts +14 -0
- package/src/common/ternary-search-tree.ts +2 -2
- package/src/electron-browser/keyboard/electron-keyboard-module.spec.ts +29 -0
- package/src/electron-browser/keyboard/electron-keyboard-module.ts +9 -2
- package/src/electron-browser/menu/electron-main-menu-factory.ts +3 -3
- package/src/electron-browser/menu/electron-menu-contribution.ts +4 -0
- package/src/electron-common/electron-api.spec.ts +42 -0
- package/src/electron-common/electron-api.ts +23 -0
- package/src/electron-main/electron-api-main.ts +2 -13
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { enableJSDOM } from '../../browser/test/jsdom';
|
|
18
18
|
let disableJSDOM = enableJSDOM();
|
|
19
19
|
|
|
20
|
-
import { KeyCode, Key, KeyModifier, KeySequence } from './keys';
|
|
20
|
+
import { KeyCode, Key, KeyModifier, KeySequence, NormalizedKeyboardInput, normalizeKeyboardInput, readAltGraph } from './keys';
|
|
21
21
|
import * as os from '../../common/os';
|
|
22
22
|
import * as chai from 'chai';
|
|
23
23
|
import * as sinon from 'sinon';
|
|
@@ -117,6 +117,139 @@ describe('keys api', () => {
|
|
|
117
117
|
expect(equalKeyCode(parsedKeyCode, keyCode)).to.be.true;
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
+
it('should keep dispatch strings equivalent to serialized strings', () => {
|
|
121
|
+
const keyCodes = [
|
|
122
|
+
...Array.from({ length: 16 }, (_, modifiers) => new KeyCode({
|
|
123
|
+
key: Key.KEY_A,
|
|
124
|
+
ctrl: !!(modifiers & 1),
|
|
125
|
+
shift: !!(modifiers & 2),
|
|
126
|
+
alt: !!(modifiers & 4),
|
|
127
|
+
meta: !!(modifiers & 8)
|
|
128
|
+
})),
|
|
129
|
+
new KeyCode({}),
|
|
130
|
+
KeyCode.parse('ctrl+['),
|
|
131
|
+
KeyCode.parse('shift+/'),
|
|
132
|
+
...KeySequence.parse('ctrlcmd+k ctrlcmd+c')
|
|
133
|
+
];
|
|
134
|
+
for (const keyCode of keyCodes) {
|
|
135
|
+
expect(keyCode.dispatchString()).to.equal(keyCode.toString());
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should distinguish layout modifiers in dispatch identity without changing serialization', () => {
|
|
140
|
+
const command = new KeyCode({ key: Key.DIGIT8, ctrl: true, alt: true });
|
|
141
|
+
const layout = new KeyCode({ key: Key.DIGIT8, ctrl: true, layoutModifiers: 'altGraph', character: '[' });
|
|
142
|
+
const shiftedLayout = new KeyCode({ key: Key.SLASH, layoutModifiers: 'shift', character: '/' });
|
|
143
|
+
|
|
144
|
+
expect(command.toString()).to.equal('alt+ctrl+8');
|
|
145
|
+
expect(layout.toString()).to.equal('ctrl+8');
|
|
146
|
+
expect(layout.dispatchString()).to.equal('ctrl+8@altgraph');
|
|
147
|
+
expect(shiftedLayout.toString()).to.equal('/');
|
|
148
|
+
expect(shiftedLayout.dispatchString()).to.equal('/@shift');
|
|
149
|
+
expect(command.equals(layout)).to.be.false;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should keep equality and dispatch identity equivalent for layout modifiers', () => {
|
|
153
|
+
const layers = ['none', 'shift', 'altGraph', 'shiftAltGraph'] as const;
|
|
154
|
+
const keyCodes = Array.from({ length: 64 }, (_, modifiers) => new KeyCode({
|
|
155
|
+
key: Key.KEY_A,
|
|
156
|
+
ctrl: !!(modifiers & 1),
|
|
157
|
+
shift: !!(modifiers & 2),
|
|
158
|
+
alt: !!(modifiers & 4),
|
|
159
|
+
meta: !!(modifiers & 8),
|
|
160
|
+
layoutModifiers: layers[modifiers >> 4]
|
|
161
|
+
}));
|
|
162
|
+
for (const left of keyCodes) {
|
|
163
|
+
for (const right of keyCodes) {
|
|
164
|
+
expect(left.equals(right)).to.equal(left.dispatchString() === right.dispatchString());
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should keep interpretation provenance outside dispatch identity', () => {
|
|
170
|
+
const command = new KeyCode({ key: Key.DIGIT8, ctrl: true, interpretation: 'commandModifiers' });
|
|
171
|
+
const layout = new KeyCode({ key: Key.DIGIT8, ctrl: true, interpretation: 'layoutModifiers' });
|
|
172
|
+
|
|
173
|
+
expect(command.dispatchString()).to.equal(layout.dispatchString());
|
|
174
|
+
expect(command.equals(layout)).to.be.true;
|
|
175
|
+
expect(command.interpretation).to.equal('commandModifiers');
|
|
176
|
+
expect(layout.interpretation).to.equal('layoutModifiers');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should normalize explicit and native AltGraph state', () => {
|
|
180
|
+
expect(readAltGraph({ altGraph: false, getModifierState: () => true })).to.be.false;
|
|
181
|
+
expect(readAltGraph({ getModifierState: modifier => modifier === 'AltGraph' })).to.be.true;
|
|
182
|
+
expect(readAltGraph({ code: 'Digit8' })).to.be.false;
|
|
183
|
+
|
|
184
|
+
expect(normalizeKeyboardInput({
|
|
185
|
+
key: '[',
|
|
186
|
+
code: 'Digit8',
|
|
187
|
+
keyCode: 56,
|
|
188
|
+
ctrlKey: true,
|
|
189
|
+
altGraph: true,
|
|
190
|
+
location: 0
|
|
191
|
+
})).to.deep.equal({
|
|
192
|
+
key: '[',
|
|
193
|
+
code: 'Digit8',
|
|
194
|
+
keyCode: 56,
|
|
195
|
+
charCode: undefined,
|
|
196
|
+
ctrlKey: true,
|
|
197
|
+
shiftKey: undefined,
|
|
198
|
+
altKey: undefined,
|
|
199
|
+
metaKey: undefined,
|
|
200
|
+
altGraph: true,
|
|
201
|
+
repeat: undefined,
|
|
202
|
+
isComposing: undefined,
|
|
203
|
+
location: 0,
|
|
204
|
+
timeStamp: undefined,
|
|
205
|
+
keyIdentifier: undefined,
|
|
206
|
+
getModifierState: undefined
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('should preserve every normalized keyboard input field', () => {
|
|
211
|
+
const getModifierState = (modifier: string) => modifier === 'AltGraph';
|
|
212
|
+
const input: Required<NormalizedKeyboardInput> = {
|
|
213
|
+
key: '[',
|
|
214
|
+
code: 'Digit8',
|
|
215
|
+
keyCode: 56,
|
|
216
|
+
charCode: 91,
|
|
217
|
+
ctrlKey: true,
|
|
218
|
+
shiftKey: true,
|
|
219
|
+
altKey: false,
|
|
220
|
+
metaKey: true,
|
|
221
|
+
altGraph: true,
|
|
222
|
+
repeat: true,
|
|
223
|
+
isComposing: false,
|
|
224
|
+
location: 2,
|
|
225
|
+
timeStamp: 42,
|
|
226
|
+
keyIdentifier: 'U+005B',
|
|
227
|
+
getModifierState
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const normalized = normalizeKeyboardInput(input);
|
|
231
|
+
|
|
232
|
+
expect({ ...normalized, getModifierState: undefined }).to.deep.equal({ ...input, getModifierState: undefined });
|
|
233
|
+
expect(normalized.getModifierState?.('AltGraph')).to.be.true;
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should create key codes from normalized plain data', () => {
|
|
237
|
+
const keyCode = KeyCode.createKeyCode({
|
|
238
|
+
key: '[',
|
|
239
|
+
code: 'Digit8',
|
|
240
|
+
keyCode: 56,
|
|
241
|
+
ctrlKey: true
|
|
242
|
+
});
|
|
243
|
+
expect(keyCode.key).to.equal(Key.DIGIT8);
|
|
244
|
+
expect(keyCode.ctrl).to.be.true;
|
|
245
|
+
expect(keyCode.character).to.equal('[');
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('should preserve legacy key identifier fallback in normalized data', () => {
|
|
249
|
+
const input = normalizeKeyboardInput({ keyIdentifier: Key.F1.code });
|
|
250
|
+
expect(KeyCode.createKeyCode(input).key).to.equal(Key.F1);
|
|
251
|
+
});
|
|
252
|
+
|
|
120
253
|
it('the order of the modifiers should not matter when parsing the key code', () => {
|
|
121
254
|
const left = KeySequence.parse('shift+alt+a');
|
|
122
255
|
const right = KeySequence.parse('alt+shift+a');
|
|
@@ -231,6 +364,78 @@ describe('keys api', () => {
|
|
|
231
364
|
expect(second.key).is.equal(Key.KEY_B);
|
|
232
365
|
});
|
|
233
366
|
|
|
367
|
+
it('should parse physical scan codes and preserve them when serializing', () => {
|
|
368
|
+
const keycode = KeyCode.parse('ctrl+[BracketLeft]');
|
|
369
|
+
expect(keycode.ctrl).to.be.true;
|
|
370
|
+
expect(keycode.key).to.equal(Key.BRACKET_LEFT);
|
|
371
|
+
expect(keycode.physical).to.be.true;
|
|
372
|
+
expect(keycode.toString()).to.equal('ctrl+[BracketLeft]');
|
|
373
|
+
expect(() => KeyCode.parse('[UnknownPhysicalKey]')).to.throw(/Unknown scan code/);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('should parse bare and escaped logical characters', () => {
|
|
377
|
+
for (const character of ['^', '§', '²', 'ä']) {
|
|
378
|
+
const keycode = KeyCode.parse(`ctrl+${character}`);
|
|
379
|
+
expect(keycode.character).to.equal(character);
|
|
380
|
+
expect(keycode.toString()).to.equal(`ctrl+${character}`);
|
|
381
|
+
}
|
|
382
|
+
expect(KeyCode.parse('ctrl+[char:0x2B]').character).to.equal('+');
|
|
383
|
+
expect(KeyCode.parse('ctrl+[char:0x20]').character).to.equal(' ');
|
|
384
|
+
expect(KeyCode.parse('ctrl+[char:0x5D]').character).to.equal(']');
|
|
385
|
+
expect(KeyCode.parse('ctrl+[char:§]').character).to.equal('§');
|
|
386
|
+
expect(KeyCode.parse('ctrl+[char:0x2B]').toString()).to.equal('ctrl+[char:0x2B]');
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('should keep bracketed separators and whitespace within one key token', () => {
|
|
390
|
+
expect(KeySequence.parse('ctrl+[char:0x20] ctrl+[BracketLeft]')).to.have.length(2);
|
|
391
|
+
expect(KeySequence.parse('ctrl+[ ctrl+]')).to.have.length(2);
|
|
392
|
+
expect(KeySequence.parse('ctrl+] ctrl+[')).to.have.length(2);
|
|
393
|
+
expect(KeyCode.parse('ctrl+[char:0x2B]').character).to.equal('+');
|
|
394
|
+
expect(KeyCode.parse('ctrl+[char:0x2D]').character).to.equal('-');
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('should reject invalid and multi-code-point logical characters', () => {
|
|
398
|
+
expect(() => KeyCode.parse('[char:0x110000]')).to.throw(/Invalid Unicode/);
|
|
399
|
+
expect(() => KeyCode.parse('[char:0xD800]')).to.throw(/Invalid Unicode/);
|
|
400
|
+
expect(() => KeyCode.parse('[char:ab]')).to.throw(/Invalid Unicode/);
|
|
401
|
+
expect(() => KeyCode.parse('👨👩👧👦')).to.throw(/Unrecognized key/);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('should serialize recorder strokes as logical characters, named keys, or physical scan codes', () => {
|
|
405
|
+
expect(new KeyCode({ key: Key.DIGIT8, ctrl: true, character: '[' }).toAuthoredKeybindingString()).to.equal('ctrl+[');
|
|
406
|
+
expect(new KeyCode({ key: Key.EQUAL, ctrl: true, character: '+' }).toAuthoredKeybindingString()).to.equal('ctrl+[char:0x2B]');
|
|
407
|
+
|
|
408
|
+
const namedKeys: Array<[Key, string]> = [
|
|
409
|
+
[Key.F1, 'f1'],
|
|
410
|
+
[Key.DELETE, 'delete'],
|
|
411
|
+
[Key.BACKSPACE, 'backspace'],
|
|
412
|
+
[Key.ENTER, 'enter'],
|
|
413
|
+
[Key.ARROW_LEFT, 'left'],
|
|
414
|
+
[Key.SPACE, 'space'],
|
|
415
|
+
[Key.ESCAPE, 'escape']
|
|
416
|
+
];
|
|
417
|
+
for (const [key, name] of namedKeys) {
|
|
418
|
+
const serialized = new KeyCode({ key, ctrl: true }).toAuthoredKeybindingString();
|
|
419
|
+
expect(serialized).to.equal(`ctrl+${name}`);
|
|
420
|
+
expect(KeyCode.parse(serialized).key).to.equal(key);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const physicalPrintable = new KeyCode({ key: Key.DIGIT8, ctrl: true }).toAuthoredKeybindingString();
|
|
424
|
+
expect(physicalPrintable).to.equal('ctrl+[Digit8]');
|
|
425
|
+
expect(KeyCode.parse(physicalPrintable).key).to.equal(Key.DIGIT8);
|
|
426
|
+
|
|
427
|
+
const shiftedLetter = new KeyCode({ key: Key.KEY_P, ctrl: true, shift: true, character: 'P' });
|
|
428
|
+
expect(shiftedLetter.toAuthoredKeybindingString()).to.equal('shift+ctrl+p');
|
|
429
|
+
expect(KeyCode.parse(shiftedLetter.toAuthoredKeybindingString()).dispatchString()).to.equal('shift+ctrl+p');
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it('should serialize recorder strokes for Space and numpad keys by name', () => {
|
|
433
|
+
expect(new KeyCode({ key: Key.SPACE, meta: true, shift: true, character: ' ' }).toAuthoredKeybindingString()).to.equal('meta+shift+space');
|
|
434
|
+
expect(new KeyCode({ key: Key.ADD, ctrl: true, character: '+' }).toAuthoredKeybindingString()).to.equal('ctrl+add');
|
|
435
|
+
expect(new KeyCode({ key: Key.DECIMAL, ctrl: true, character: '.' }).toAuthoredKeybindingString()).to.equal('ctrl+decimal');
|
|
436
|
+
expect(new KeyCode({ key: Key.DIGIT1, ctrl: true, character: '1' }).toAuthoredKeybindingString()).to.equal('ctrl+1');
|
|
437
|
+
});
|
|
438
|
+
|
|
234
439
|
it('should parse minus as key', () => {
|
|
235
440
|
const keycode = KeyCode.parse('ctrl+-');
|
|
236
441
|
expect(keycode.ctrl).to.be.true;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { expect } from 'chai';
|
|
18
|
+
import { Key, KeyCode } from '../keyboard/keys';
|
|
19
|
+
import { KeybindingRegistry } from '../keybinding';
|
|
20
|
+
import { ActionMenuNode } from './action-menu-node';
|
|
21
|
+
|
|
22
|
+
describe('ActionMenuNode accelerators', () => {
|
|
23
|
+
it('selects logical and physical accelerator forms explicitly', () => {
|
|
24
|
+
const binding = { command: 'test.command', keybinding: 'ctrl+[' };
|
|
25
|
+
const state = { inactive: false };
|
|
26
|
+
const keybindingRegistry = Object.create(KeybindingRegistry.prototype) as KeybindingRegistry;
|
|
27
|
+
keybindingRegistry.getKeybindingsForCommand = () => [{ ...binding, scope: 0 }];
|
|
28
|
+
keybindingRegistry.isEnabledInScope = () => true;
|
|
29
|
+
keybindingRegistry.isKeybindingInactive = () => state.inactive;
|
|
30
|
+
keybindingRegistry.resolveKeybinding = () => [new KeyCode({
|
|
31
|
+
key: Key.DIGIT8,
|
|
32
|
+
ctrl: true,
|
|
33
|
+
character: '[',
|
|
34
|
+
layoutModifiers: 'altGraph'
|
|
35
|
+
})];
|
|
36
|
+
Object.defineProperty(keybindingRegistry, 'keyboardLayoutService', {
|
|
37
|
+
value: { getKeyboardCharacter: (key: Key) => key.easyString }
|
|
38
|
+
});
|
|
39
|
+
const commands = {
|
|
40
|
+
getAllHandlers: () => [],
|
|
41
|
+
getCommand: () => ({ id: 'test.command' })
|
|
42
|
+
};
|
|
43
|
+
const contextKeyService = {
|
|
44
|
+
parseKeys: () => undefined
|
|
45
|
+
};
|
|
46
|
+
const node = new ActionMenuNode(
|
|
47
|
+
{ commandId: 'test.command' },
|
|
48
|
+
commands as never,
|
|
49
|
+
keybindingRegistry,
|
|
50
|
+
contextKeyService as never
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
expect(node.getAccelerator(undefined, 'logical')).to.deep.equal(['Ctrl+[']);
|
|
54
|
+
expect(node.getAccelerator(undefined, 'physical')).to.deep.equal(['Ctrl+AltGr+8']);
|
|
55
|
+
|
|
56
|
+
state.inactive = true;
|
|
57
|
+
expect(node.getAccelerator(undefined, 'logical')).to.deep.equal(['Ctrl+[']);
|
|
58
|
+
expect(node.getAccelerator(undefined, 'physical')).to.deep.equal([]);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -14,13 +14,16 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import { KeybindingRegistry } from '../keybinding';
|
|
17
|
+
import { AcceleratorForm, KeybindingRegistry } from '../keybinding';
|
|
18
18
|
import { ContextKeyService } from '../context-key-service';
|
|
19
|
-
import { DisposableCollection, isObject, CommandRegistry, Emitter
|
|
19
|
+
import { DisposableCollection, isObject, CommandRegistry, Emitter } from '../../common';
|
|
20
20
|
import { CommandMenu, ContextExpressionMatcher, MenuAction, MenuPath } from '../../common/menu/menu-types';
|
|
21
21
|
|
|
22
|
+
export { AcceleratorForm } from '../keybinding';
|
|
23
|
+
|
|
22
24
|
export interface AcceleratorSource {
|
|
23
|
-
|
|
25
|
+
/** Return the accelerator rendered in the explicitly requested logical or physical form. */
|
|
26
|
+
getAccelerator(context: HTMLElement | undefined, form: AcceleratorForm): string[];
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
export namespace AcceleratorSource {
|
|
@@ -80,17 +83,8 @@ export class ActionMenuNode implements CommandMenu {
|
|
|
80
83
|
return true;
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
getAccelerator(context: HTMLElement | undefined): string[] {
|
|
84
|
-
|
|
85
|
-
// Only consider the first active keybinding.
|
|
86
|
-
if (bindings.length) {
|
|
87
|
-
const binding = bindings.find(b => this.keybindingRegistry.isEnabledInScope(b, context));
|
|
88
|
-
if (binding) {
|
|
89
|
-
const asciiOnly = environment.electron.is();
|
|
90
|
-
return this.keybindingRegistry.acceleratorFor(binding, '+', asciiOnly);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return [];
|
|
86
|
+
getAccelerator(context: HTMLElement | undefined, form: AcceleratorForm): string[] {
|
|
87
|
+
return this.keybindingRegistry.acceleratorForCommand(this.action.commandId, context, form);
|
|
94
88
|
}
|
|
95
89
|
|
|
96
90
|
isEnabled(effeciveMenuPath: MenuPath, ...args: unknown[]): boolean {
|
|
@@ -368,7 +368,7 @@ export class DynamicMenuWidget extends MenuWidget {
|
|
|
368
368
|
iconClass: node.icon,
|
|
369
369
|
});
|
|
370
370
|
|
|
371
|
-
const accelerator = (AcceleratorSource.is(node) ? node.getAccelerator(this.options.context) : []);
|
|
371
|
+
const accelerator = (AcceleratorSource.is(node) ? node.getAccelerator(this.options.context, 'logical') : []);
|
|
372
372
|
if (accelerator.length > 0) {
|
|
373
373
|
phCommandRegistry.addKeyBinding({
|
|
374
374
|
command: id,
|
|
@@ -58,6 +58,17 @@ export function enableJSDOM(): () => void {
|
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
(dom.window.document as any)['queryCommandSupported'] = function (): void { };
|
|
61
|
+
dom.window.matchMedia = () => ({
|
|
62
|
+
matches: false,
|
|
63
|
+
media: '',
|
|
64
|
+
// eslint-disable-next-line no-null/no-null
|
|
65
|
+
onchange: null,
|
|
66
|
+
addListener: () => undefined,
|
|
67
|
+
removeListener: () => undefined,
|
|
68
|
+
addEventListener: () => undefined,
|
|
69
|
+
removeEventListener: () => undefined,
|
|
70
|
+
dispatchEvent: () => false
|
|
71
|
+
});
|
|
61
72
|
|
|
62
73
|
const disableJSDOM = (global as any)['_disableJSDOM'] = () => {
|
|
63
74
|
let property: string | undefined;
|
|
@@ -21,8 +21,11 @@ export const keyboardPath = '/services/keyboard';
|
|
|
21
21
|
|
|
22
22
|
export const KeyboardLayoutProvider = Symbol('KeyboardLayoutProvider');
|
|
23
23
|
|
|
24
|
+
export type KeyboardLayoutSource = 'navigator.keyboard' | 'user-choice' | 'pressed-keys' | 'native-keymap';
|
|
25
|
+
|
|
24
26
|
export interface KeyboardLayoutProvider {
|
|
25
27
|
getNativeLayout(): Promise<NativeKeyboardLayout>;
|
|
28
|
+
readonly layoutSource?: KeyboardLayoutSource;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export const KeyboardLayoutChangeNotifier = Symbol('KeyboardLayoutChangeNotifier');
|
|
@@ -37,6 +40,11 @@ export interface KeyValidationInput {
|
|
|
37
40
|
shiftKey?: boolean;
|
|
38
41
|
ctrlKey?: boolean;
|
|
39
42
|
altKey?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Whether the AltGr layer is active for the platform (Linux: AltGraph, macOS: Option, Windows: AltGraph or Ctrl+Alt),
|
|
45
|
+
* not the raw `AltGraph` modifier state.
|
|
46
|
+
*/
|
|
47
|
+
altGraph?: boolean;
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
export const KeyValidator = Symbol('KeyValidator');
|