diff --git a/src/vs/workbench/services/keybinding/common/keyboardEventCode.ts b/src/vs/workbench/services/keybinding/common/keyboardEventCode.ts index 503fd36f137..f24c6873f28 100644 --- a/src/vs/workbench/services/keybinding/common/keyboardEventCode.ts +++ b/src/vs/workbench/services/keybinding/common/keyboardEventCode.ts @@ -410,4 +410,6 @@ export const enum KeyboardEventCode { MailReply, MailForward, MailSend, + + MAX_VALUE } \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/common/keyboardMapper.ts b/src/vs/workbench/services/keybinding/common/keyboardMapper.ts index 950ad6021de..87693fa1c71 100644 --- a/src/vs/workbench/services/keybinding/common/keyboardMapper.ts +++ b/src/vs/workbench/services/keybinding/common/keyboardMapper.ts @@ -6,10 +6,12 @@ 'use strict'; import { OperatingSystem } from 'vs/base/common/platform'; -import { SimpleKeybinding, KeyCode } from 'vs/base/common/keyCodes'; +import { SimpleKeybinding, KeyCode, ResolvedKeybinding, Keybinding, KeyCodeUtils } from 'vs/base/common/keyCodes'; import { KeyboardEventCode, KeyboardEventCodeUtils } from 'vs/workbench/services/keybinding/common/keyboardEventCode'; import { CharCode } from 'vs/base/common/charCode'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/abstractKeybindingService'; +import { IHTMLContentElement } from 'vs/base/common/htmlContent'; +import { PrintableKeypress, UILabelProvider, AriaLabelProvider } from 'vs/platform/keybinding/common/keybindingLabels'; export interface IKeyMapping { value: string; @@ -43,6 +45,7 @@ function cannotMapSimpleKeybinding(keybinding: SimpleKeybinding, OS: OperatingSy * -1 if a KeyCode => keyboardEvent.code mapping depends on kb layout. */ const IMMUTABLE_KEY_CODE_TO_CODE: KeyboardEventCode[] = []; +const IMMUTABLE_CODE_TO_KEY_CODE: KeyCode[] = []; /** * Chars that will be remapped. @@ -130,7 +133,7 @@ const enum ModifierState { ShiftAltGr = 3 } -export class HardwareKeyPress { +export class HardwareKeypress { readonly ctrlKey: boolean; readonly shiftKey: boolean; readonly altKey: boolean; @@ -144,35 +147,131 @@ export class HardwareKeyPress { this.metaKey = metaKey; this.code = code; } + + public toPrintableKeypress(key: string): PrintableKeypress { + return new PrintableKeypress(this.ctrlKey, this.shiftKey, this.altKey, this.metaKey, key); + } +} + +export class NativeResolvedKeybinding extends ResolvedKeybinding { + + private readonly _mapper: KeyboardMapper; + private readonly _OS: OperatingSystem; + private readonly _firstPart: HardwareKeypress; + private readonly _chordPart: HardwareKeypress; + + constructor(mapper: KeyboardMapper, OS: OperatingSystem, firstPart: HardwareKeypress, chordPart: HardwareKeypress) { + super(); + this._mapper = mapper; + this._OS = OS; + this._firstPart = firstPart; + this._chordPart = chordPart; + } + + public getLabel(): string { + let firstPart = this._firstPart.toPrintableKeypress(this._mapper.getUILabelForHardwareCode(this._firstPart.code)); + let chordPart = this._chordPart ? this._chordPart.toPrintableKeypress(this._mapper.getUILabelForHardwareCode(this._chordPart.code)) : null; + + return UILabelProvider.toLabel2(firstPart, chordPart, this._OS); + } + + public getAriaLabel(): string { + let firstPart = this._firstPart.toPrintableKeypress(this._mapper.getAriaLabelForHardwareCode(this._firstPart.code)); + let chordPart = this._chordPart ? this._chordPart.toPrintableKeypress(this._mapper.getAriaLabelForHardwareCode(this._chordPart.code)) : null; + + return AriaLabelProvider.toLabel2(firstPart, chordPart, this._OS); + } + + public getHTMLLabel(): IHTMLContentElement[] { + let firstPart = this._firstPart.toPrintableKeypress(this._mapper.getUILabelForHardwareCode(this._firstPart.code)); + let chordPart = this._chordPart ? this._chordPart.toPrintableKeypress(this._mapper.getUILabelForHardwareCode(this._chordPart.code)) : null; + + return UILabelProvider.toHTMLLabel2(firstPart, chordPart, this._OS); + } + + public getElectronAccelerator(): string { + throw new Error('TODO!'); + // const usResolvedKeybinding = new USLayoutResolvedKeybinding(this._actual, OS); + + // if (OS === OperatingSystem.Windows) { + // // electron menus always do the correct rendering on Windows + // return usResolvedKeybinding.getElectronAccelerator(); + // } + + // let usLabel = usResolvedKeybinding.getLabel(); + // let label = this.getLabel(); + // if (usLabel !== label) { + // // electron menus are incorrect in rendering (linux) and in rendering and interpreting (mac) + // // for non US standard keyboard layouts + // return null; + // } + + // return usResolvedKeybinding.getElectronAccelerator(); + } + + public getUserSettingsLabel(): string { + throw new Error('TODO!'); + // return KeybindingIO.writeKeybinding(this._actual, OS); + } +} + +interface IHardwareCodeMapping { + value: number; + withShift: number; + withAltGr: number; + withShiftAltGr: number; + + valueIsDeadKey: boolean; + withShiftIsDeadKey: boolean; + withAltGrIsDeadKey: boolean; + withShiftAltGrIsDeadKey: boolean; } export class KeyboardMapper { private readonly _OS: OperatingSystem; - private readonly _remapChars2: HardwareKeyPress[][]; + private readonly _remapChars: HardwareKeypress[][]; + private readonly _mappings: IHardwareCodeMapping[]; - constructor(mapping: IKeyboardMapping, OS: OperatingSystem) { + constructor(mappings: IKeyboardMapping, OS: OperatingSystem) { - this._remapChars2 = []; + this._remapChars = []; let maxCharCode = REMAP_CHARS.reduce((prev, curr) => Math.max(prev, curr)); for (let i = 0; i <= maxCharCode; i++) { - this._remapChars2[i] = null; + this._remapChars[i] = null; } - for (let strCode in mapping) { - if (mapping.hasOwnProperty(strCode)) { + this._mappings = []; + for (let strCode in mappings) { + if (mappings.hasOwnProperty(strCode)) { const code = KeyboardEventCodeUtils.toEnum(strCode); if (code === KeyboardEventCode.None) { log(`Unknown code ${strCode} in mapping.`); continue; } - const results = mapping[strCode]; + const mapping = mappings[strCode]; + const value = KeyboardMapper._getCharCode(mapping.value); + const withShift = KeyboardMapper._getCharCode(mapping.withShift); + const withAltGr = KeyboardMapper._getCharCode(mapping.withAltGr); + const withShiftAltGr = KeyboardMapper._getCharCode(mapping.withShiftAltGr); - this._register(code, false, false, false, KeyboardMapper._getCharCode(results.value)); - this._register(code, false, true, false, KeyboardMapper._getCharCode(results.withShift)); - this._register(code, true, false, true, KeyboardMapper._getCharCode(results.withAltGr)); - this._register(code, true, true, true, KeyboardMapper._getCharCode(results.withShiftAltGr)); + this._mappings[code] = { + value: value, + withShift: withShift, + withAltGr: withAltGr, + withShiftAltGr: withShiftAltGr, + + valueIsDeadKey: mapping.valueIsDeadKey, + withShiftIsDeadKey: mapping.withShiftIsDeadKey, + withAltGrIsDeadKey: mapping.withAltGrIsDeadKey, + withShiftAltGrIsDeadKey: mapping.withShiftAltGrIsDeadKey, + }; + + this._register(code, false, false, false, value); + this._register(code, false, true, false, withShift); + this._register(code, true, false, true, withAltGr); + this._register(code, true, true, true, withShiftAltGr); } } @@ -180,7 +279,7 @@ export class KeyboardMapper { for (let i = 0; i < REMAP_CHARS.length; i++) { const charCode = REMAP_CHARS[i]; - let combos = this._remapChars2[charCode]; + let combos = this._remapChars[charCode]; if (combos === null) { log(`Could not find any key combination producing '${String.fromCharCode(charCode)}'`); } else if (combos.length > 1) { @@ -238,15 +337,15 @@ export class KeyboardMapper { return; } - let entry = new HardwareKeyPress(ctrlKey, shiftKey, altKey, false, code); + let entry = new HardwareKeypress(ctrlKey, shiftKey, altKey, false, code); - if (this._remapChars2[charCode] === null) { + if (this._remapChars[charCode] === null) { // no duplicates so far - this._remapChars2[charCode] = [entry]; + this._remapChars[charCode] = [entry]; return; } - const list = this._remapChars2[charCode]; + const list = this._remapChars[charCode]; // Do not register if it already sits under the same code for (let i = 0, len = list.length; i < len; i++) { if (list[i].code === code) { @@ -256,7 +355,7 @@ export class KeyboardMapper { list.push(entry); } - private _doMapSimpleKeybinding(source: SimpleKeybinding, keyCombo: HardwareKeyPress, ctrlKey: boolean, altKey: boolean, metaKey: boolean, charCode: number): HardwareKeyPress { + private _doMapSimpleKeybinding(source: SimpleKeybinding, keyCombo: HardwareKeypress, ctrlKey: boolean, altKey: boolean, metaKey: boolean, charCode: number): HardwareKeypress { if ((keyCombo.ctrlKey && ctrlKey) || (keyCombo.altKey && altKey)) { cannotMapSimpleKeybinding(source, this._OS, `ctrl or alt modifiers are needed to produce '${String.fromCharCode(charCode)}'`); return null; @@ -270,13 +369,13 @@ export class KeyboardMapper { altKey = true; } - return new HardwareKeyPress(ctrlKey, shiftKey, altKey, metaKey, keyCombo.code); + return new HardwareKeypress(ctrlKey, shiftKey, altKey, metaKey, keyCombo.code); } - private _mapSimpleKeybinding(source: SimpleKeybinding, ctrlKey: boolean, altKey: boolean, metaKey: boolean, charCode: number): HardwareKeyPress[] { - const keyCombos = this._remapChars2[charCode]; + private _mapSimpleKeybinding(source: SimpleKeybinding, ctrlKey: boolean, altKey: boolean, metaKey: boolean, charCode: number): HardwareKeypress[] { + const keyCombos = this._remapChars[charCode]; - let result: HardwareKeyPress[] = [], resultLen = 0; + let result: HardwareKeypress[] = [], resultLen = 0; if (keyCombos !== null) { for (let i = 0, len = keyCombos.length; i < len; i++) { const keyCombo = keyCombos[i]; @@ -292,7 +391,7 @@ export class KeyboardMapper { return result; } - public mapSimpleKeybinding(keybinding: SimpleKeybinding): HardwareKeyPress[] { + public mapSimpleKeybinding(keybinding: SimpleKeybinding): HardwareKeypress[] { const ctrlCmd = keybinding.hasCtrlCmd(); const winCtrl = keybinding.hasWinCtrl(); @@ -304,7 +403,7 @@ export class KeyboardMapper { if (IMMUTABLE_KEY_CODE_TO_CODE[keyCode] !== -1) { const keyboardEventCode = IMMUTABLE_KEY_CODE_TO_CODE[keyCode]; - return [new HardwareKeyPress(ctrlKey, shiftKey, altKey, metaKey, keyboardEventCode)]; + return [new HardwareKeypress(ctrlKey, shiftKey, altKey, metaKey, keyboardEventCode)]; } let desiredCharCode = 0; @@ -362,18 +461,90 @@ export class KeyboardMapper { return this._mapSimpleKeybinding(keybinding, ctrlKey, altKey, metaKey, desiredCharCode); } + + public getUILabelForHardwareCode(code: KeyboardEventCode): string { + return this._getLabelForHardwareCode(code, true); + } + + public getAriaLabelForHardwareCode(code: KeyboardEventCode): string { + return this._getLabelForHardwareCode(code, false); + } + + private _getLabelForHardwareCode(code: KeyboardEventCode, isUI: boolean): string { + if (isUI && this._OS === OperatingSystem.Macintosh) { + switch (code) { + case KeyboardEventCode.ArrowLeft: + return '←'; + case KeyboardEventCode.ArrowUp: + return '↑'; + case KeyboardEventCode.ArrowRight: + return '→'; + case KeyboardEventCode.ArrowDown: + return '↓'; + } + } + if (IMMUTABLE_CODE_TO_KEY_CODE[code] !== -1) { + const keyCode = IMMUTABLE_CODE_TO_KEY_CODE[code]; + return KeyCodeUtils.toString(keyCode); + } + + const mapping = this._mappings[code]; + if (!mapping) { + // uh-oh + return 'Unknown'; + } + + if (mapping.value >= CharCode.a && mapping.value <= CharCode.z) { + return String.fromCharCode(CharCode.A + (mapping.value - CharCode.a)); + } + + if (mapping.value) { + return String.fromCharCode(mapping.value); + } + + throw new Error('TODO!'); + } + + public resolveKeybinding(keybinding: Keybinding): NativeResolvedKeybinding[] { + let result: NativeResolvedKeybinding[] = [], resultLen = 0; + + if (keybinding.isChord()) { + const firstParts = this.mapSimpleKeybinding(keybinding.extractFirstPart()); + const chordParts = this.mapSimpleKeybinding(keybinding.extractChordPart()); + + for (let i = 0, len = firstParts.length; i < len; i++) { + const firstPart = firstParts[i]; + for (let j = 0, lenJ = chordParts.length; j < lenJ; j++) { + const chordPart = chordParts[j]; + + result[resultLen++] = new NativeResolvedKeybinding(this, this._OS, firstPart, chordPart); + } + } + } else { + const firstParts = this.mapSimpleKeybinding(keybinding); + + for (let i = 0, len = firstParts.length; i < len; i++) { + const firstPart = firstParts[i]; + + result[resultLen++] = new NativeResolvedKeybinding(this, this._OS, firstPart, null); + } + } + + return result; + } } (function () { - let currentLength = 0; + for (let i = 0; i <= KeyCode.MAX_VALUE; i++) { + IMMUTABLE_KEY_CODE_TO_CODE[i] = -1; + } + for (let i = 0; i <= KeyboardEventCode.MAX_VALUE; i++) { + IMMUTABLE_CODE_TO_KEY_CODE[i] = -1; + } + function d(keyCode: KeyCode, code: KeyboardEventCode): void { - if (keyCode > currentLength) { - for (let i = currentLength; i < keyCode; i++) { - IMMUTABLE_KEY_CODE_TO_CODE[i] = -1; - } - } IMMUTABLE_KEY_CODE_TO_CODE[keyCode] = code; - currentLength = keyCode + 1; + IMMUTABLE_CODE_TO_KEY_CODE[code] = keyCode; } // Unknown = 0, diff --git a/src/vs/workbench/services/keybinding/test/keyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/keyboardMapper.test.ts index 58d32bd7e21..d70a94892c9 100644 --- a/src/vs/workbench/services/keybinding/test/keyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/keyboardMapper.test.ts @@ -8,12 +8,13 @@ import * as assert from 'assert'; import { mac_de_ch } from 'vs/workbench/services/keybinding/test/mac_de_ch'; import { linux_de_ch } from 'vs/workbench/services/keybinding/test/linux_de_ch'; -import { KeyMod, KeyCode, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; +import { KeyMod, KeyCode, SimpleKeybinding, createKeybinding, Keybinding } from 'vs/base/common/keyCodes'; import { KeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; import { OperatingSystem } from 'vs/base/common/platform'; import { UserSettingsLabelProvider, PrintableKeypress } from 'vs/platform/keybinding/common/keybindingLabels'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/abstractKeybindingService'; -import { KeyboardEventCodeUtils } from "vs/workbench/services/keybinding/common/keyboardEventCode"; +import { KeyboardEventCodeUtils } from 'vs/workbench/services/keybinding/common/keyboardEventCode'; +import { IHTMLContentElement } from "vs/base/common/htmlContent"; function _assertKeybindingTranslation(mapper: KeyboardMapper, OS: OperatingSystem, kb: number, expected: string[]): void { let actualHardwareKeypresses = mapper.mapSimpleKeybinding(new SimpleKeybinding(kb)); @@ -325,6 +326,104 @@ suite('keyboardMapper - MAC de_ch', () => { // OEM_8 // OEM_102 }); + + test('resolveKeybinding - all labels', () => { + function _assertAllLabels(keybinding: Keybinding, labels: string[], ariaLabels: string[], htmlLabel: IHTMLContentElement[][]): void { + const kb = mapper.resolveKeybinding(keybinding); + + let actualLabels = kb.map(k => k.getLabel()); + assert.deepEqual(actualLabels, labels); + + let actualAriaLabels = kb.map(k => k.getAriaLabel()); + assert.deepEqual(actualAriaLabels, ariaLabels); + + let actualHTMLLabels = kb.map(k => k.getHTMLLabel()); + assert.deepEqual(actualHTMLLabels, htmlLabel); + } + + function assertAllLabels(keybinding: Keybinding, label: string | string[], ariaLabel: string | string[], htmlLabel: IHTMLContentElement[][]): void { + let _labels = (typeof label === 'string' ? [label] : label); + let _ariaLabels = (typeof ariaLabel === 'string' ? [ariaLabel] : ariaLabel); + _assertAllLabels(keybinding, _labels, _ariaLabels, htmlLabel); + } + + + // TODO: ElectronAccelerator, UserSettings + assertAllLabels( + createKeybinding(KeyMod.CtrlCmd | KeyCode.KEY_Z), + '⌘Z', + 'Command+Z', + [[{ + tagName: 'span', + className: 'monaco-kb', + children: [ + { tagName: 'span', className: 'monaco-kbkey', text: '⌘' }, + { tagName: 'span', className: 'monaco-kbkey', text: 'Z' }, + ] + }]] + ); + }); + + test('resolveKeybinding - aria labels', () => { + function assertAriaLabels(keybinding: number, label: string | string[]): void { + let _labels = (typeof label === 'string' ? [label] : label); + const kb = mapper.resolveKeybinding(createKeybinding(keybinding)); + let actualLabels = kb.map(k => k.getAriaLabel()); + assert.deepEqual(actualLabels, _labels); + } + + assertAriaLabels(KeyMod.CtrlCmd | KeyCode.KEY_1, 'Command+1'); + assertAriaLabels(KeyMod.CtrlCmd | KeyCode.KEY_B, 'Command+B'); + assertAriaLabels(KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_B, 'Shift+Command+B'); + assertAriaLabels(KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_B, 'Control+Shift+Alt+Command+B'); + assertAriaLabels(KeyMod.CtrlCmd | KeyCode.KEY_Z, 'Command+Z'); + assertAriaLabels(KeyMod.CtrlCmd | KeyCode.KEY_Y, 'Command+Y'); + + // ; + assertAriaLabels(KeyCode.US_SEMICOLON, 'Shift+,'); + // : + assertAriaLabels(KeyMod.Shift | KeyCode.US_SEMICOLON, 'Shift+.'); + // = + assertAriaLabels(KeyCode.US_EQUAL, 'Shift+0'); + // + + assertAriaLabels(KeyMod.Shift | KeyCode.US_EQUAL, 'Shift+1'); + // , + assertAriaLabels(KeyCode.US_COMMA, ','); + // < + assertAriaLabels(KeyMod.Shift | KeyCode.US_COMMA, '<'); + // - + assertAriaLabels(KeyCode.US_MINUS, '-'); + // _ + assertAriaLabels(KeyMod.Shift | KeyCode.US_MINUS, 'Shift+-'); + // . + assertAriaLabels(KeyCode.US_DOT, '.'); + // > + assertAriaLabels(KeyMod.Shift | KeyCode.US_DOT, 'Shift+<'); + // / + assertAriaLabels(KeyCode.US_SLASH, 'Shift+7'); + // ? + assertAriaLabels(KeyMod.Shift | KeyCode.US_SLASH, 'Shift+\''); + // ` + assertAriaLabels(KeyCode.US_BACKTICK, 'Shift+^'); + // ~ + assertAriaLabels(KeyMod.Shift | KeyCode.US_BACKTICK, 'Control+Alt+N'); + // [ + assertAriaLabels(KeyCode.US_OPEN_SQUARE_BRACKET, 'Control+Alt+5'); + // { + assertAriaLabels(KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET, 'Control+Alt+8'); + // \ + assertAriaLabels(KeyCode.US_BACKSLASH, 'Control+Shift+Alt+7'); + // | + assertAriaLabels(KeyMod.Shift | KeyCode.US_BACKSLASH, 'Control+Alt+7'); + // ] + assertAriaLabels(KeyCode.US_CLOSE_SQUARE_BRACKET, 'Control+Alt+6'); + // } + assertAriaLabels(KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET, 'Control+Alt+9'); + // ' + assertAriaLabels(KeyCode.US_QUOTE, '\''); + // " + assertAriaLabels(KeyMod.Shift | KeyCode.US_QUOTE, 'Shift+2'); + }); }); @@ -626,4 +725,66 @@ suite('keyboardMapper - LINUX de_ch', () => { // OEM_8 // OEM_102 }); + + + test('resolveKeybinding - aria labels', () => { + function assertAriaLabels(keybinding: number, label: string | string[]): void { + let _labels = (typeof label === 'string' ? [label] : label); + const kb = mapper.resolveKeybinding(createKeybinding(keybinding)); + let actualLabels = kb.map(k => k.getAriaLabel()); + assert.deepEqual(actualLabels, _labels); + } + + assertAriaLabels(KeyMod.CtrlCmd | KeyCode.KEY_1, 'Control+1'); + assertAriaLabels(KeyMod.CtrlCmd | KeyCode.KEY_B, 'Control+B'); + assertAriaLabels(KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_B, 'Control+Shift+B'); + assertAriaLabels(KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_B, 'Control+Shift+Alt+Windows+B'); + assertAriaLabels(KeyMod.CtrlCmd | KeyCode.KEY_Z, 'Control+Z'); + assertAriaLabels(KeyMod.CtrlCmd | KeyCode.KEY_Y, 'Control+Y'); + + // ; + assertAriaLabels(KeyCode.US_SEMICOLON, 'Shift+,'); + // : + assertAriaLabels(KeyMod.Shift | KeyCode.US_SEMICOLON, 'Shift+.'); + // = + assertAriaLabels(KeyCode.US_EQUAL, 'Shift+0'); + // + + assertAriaLabels(KeyMod.Shift | KeyCode.US_EQUAL, 'Shift+1'); + // , + assertAriaLabels(KeyCode.US_COMMA, ','); + // < + assertAriaLabels(KeyMod.Shift | KeyCode.US_COMMA, ['<', 'Control+Shift+Alt+Y']); + // - + assertAriaLabels(KeyCode.US_MINUS, '-'); + // _ + assertAriaLabels(KeyMod.Shift | KeyCode.US_MINUS, 'Shift+-'); + // . + assertAriaLabels(KeyCode.US_DOT, '.'); + // > + assertAriaLabels(KeyMod.Shift | KeyCode.US_DOT, ['Shift+<', 'Control+Shift+Alt+X']); + // / + assertAriaLabels(KeyCode.US_SLASH, 'Shift+7'); + // ? + assertAriaLabels(KeyMod.Shift | KeyCode.US_SLASH, 'Shift+\''); + // ` + assertAriaLabels(KeyCode.US_BACKTICK, 'Shift+^'); + // ~ + assertAriaLabels(KeyMod.Shift | KeyCode.US_BACKTICK, []); + // [ + assertAriaLabels(KeyCode.US_OPEN_SQUARE_BRACKET, 'Control+Alt+ü'); + // { + assertAriaLabels(KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET, 'Control+Alt+ä'); + // \ + assertAriaLabels(KeyCode.US_BACKSLASH, 'Control+Alt+<'); + // | + assertAriaLabels(KeyMod.Shift | KeyCode.US_BACKSLASH, ['Control+Alt+1', 'Control+Alt+7']); + // ] + assertAriaLabels(KeyCode.US_CLOSE_SQUARE_BRACKET, ['Control+Alt+9', 'Control+Alt+¨']); + // } + assertAriaLabels(KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET, ['Control+Alt+0', 'Control+Alt+$']); + // ' + assertAriaLabels(KeyCode.US_QUOTE, '\''); + // " + assertAriaLabels(KeyMod.Shift | KeyCode.US_QUOTE, 'Shift+2'); + }); });