提交 f055e93f 编写于 作者: A Alex Dima

Add WindowsKeyboardMapper.resolveKeybinding & tests

上级 f51ed6a6
......@@ -5,9 +5,12 @@
'use strict';
import { KeyCode, KeyCodeUtils } from 'vs/base/common/keyCodes';
import { KeyCode, KeyCodeUtils, ResolvedKeybinding, Keybinding, SimpleKeybinding, KeybindingType, USER_SETTINGS } from 'vs/base/common/keyCodes';
import { KeyboardEventCode, KeyboardEventCodeUtils, IMMUTABLE_CODE_TO_KEY_CODE } from 'vs/workbench/services/keybinding/common/keyboardEventCode';
import { CharCode } from 'vs/base/common/charCode';
import { UILabelProvider, AriaLabelProvider, ElectronAcceleratorLabelProvider, UserSettingsLabelProvider } from 'vs/platform/keybinding/common/keybindingLabels';
import { OperatingSystem } from 'vs/base/common/platform';
import { IHTMLContentElement } from 'vs/base/common/htmlContent';
export interface IKeyMapping {
vkey: string;
......@@ -39,21 +42,153 @@ interface IHardwareCodeMapping {
withShiftAltGr: number;
}
export class WindowsNativeResolvedKeybinding extends ResolvedKeybinding {
private readonly _mapper: WindowsKeyboardMapper;
private readonly _firstPart: SimpleKeybinding;
private readonly _chordPart: SimpleKeybinding;
constructor(mapper: WindowsKeyboardMapper, firstPart: SimpleKeybinding, chordPart: SimpleKeybinding) {
super();
this._mapper = mapper;
this._firstPart = firstPart;
this._chordPart = chordPart;
}
public getLabel(): string {
let firstPart = this._firstPart ? this._mapper.getUILabelForKeyCode(this._firstPart.keyCode) : null;
let chordPart = this._chordPart ? this._mapper.getUILabelForKeyCode(this._chordPart.keyCode) : null;
return UILabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, OperatingSystem.Windows);
}
public getAriaLabel(): string {
let firstPart = this._firstPart ? this._mapper.getAriaLabelForKeyCode(this._firstPart.keyCode) : null;
let chordPart = this._chordPart ? this._mapper.getAriaLabelForKeyCode(this._chordPart.keyCode) : null;
return AriaLabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, OperatingSystem.Windows);
}
public getHTMLLabel(): IHTMLContentElement[] {
let firstPart = this._firstPart ? this._mapper.getUILabelForKeyCode(this._firstPart.keyCode) : null;
let chordPart = this._chordPart ? this._mapper.getUILabelForKeyCode(this._chordPart.keyCode) : null;
return UILabelProvider.toHTMLLabel(this._firstPart, firstPart, this._chordPart, chordPart, OperatingSystem.Windows);
}
private _keyCodeToElectronAccelerator(keyCode: KeyCode): string {
if (keyCode >= KeyCode.NUMPAD_0 && keyCode <= KeyCode.NUMPAD_DIVIDE) {
// Electron cannot handle numpad keys
return null;
}
switch (keyCode) {
case KeyCode.UpArrow:
return 'Up';
case KeyCode.DownArrow:
return 'Down';
case KeyCode.LeftArrow:
return 'Left';
case KeyCode.RightArrow:
return 'Right';
}
// electron menus always do the correct rendering on Windows
return KeyCodeUtils.toString(keyCode);
}
public getElectronAccelerator(): string {
if (this._chordPart !== null) {
// Electron cannot handle chords
return null;
}
let firstPart = this._firstPart ? this._keyCodeToElectronAccelerator(this._firstPart.keyCode) : null;
return ElectronAcceleratorLabelProvider.toLabel(this._firstPart, firstPart, null, null, OperatingSystem.Windows);
}
public getUserSettingsLabel(): string {
let firstPart = this._firstPart ? USER_SETTINGS.fromKeyCode(this._firstPart.keyCode) : null;
let chordPart = this._chordPart ? USER_SETTINGS.fromKeyCode(this._chordPart.keyCode) : null;
let result = UserSettingsLabelProvider.toLabel(this._firstPart, firstPart, this._chordPart, chordPart, OperatingSystem.Windows);
return result.toLowerCase();
}
public isChord(): boolean {
return (this._chordPart ? true : false);
}
public hasCtrlModifier(): boolean {
if (this._chordPart) {
return false;
}
return this._firstPart.ctrlKey;
}
public hasShiftModifier(): boolean {
if (this._chordPart) {
return false;
}
return this._firstPart.shiftKey;
}
public hasAltModifier(): boolean {
if (this._chordPart) {
return false;
}
return this._firstPart.altKey;
}
public hasMetaModifier(): boolean {
if (this._chordPart) {
return false;
}
return this._firstPart.metaKey;
}
public getDispatchParts(): [string, string] {
let firstPart = this._firstPart ? this._getDispatchStr(this._firstPart) : null;
let chordPart = this._chordPart ? this._getDispatchStr(this._chordPart) : null;
return [firstPart, chordPart];
}
private _getDispatchStr(keybinding: SimpleKeybinding): string {
if (keybinding.isModifierKey()) {
return null;
}
let result = '';
if (keybinding.ctrlKey) {
result += 'ctrl+';
}
if (keybinding.shiftKey) {
result += 'shift+';
}
if (keybinding.altKey) {
result += 'alt+';
}
if (keybinding.metaKey) {
result += 'meta+';
}
result += KeyCodeUtils.toString(keybinding.keyCode);
return result;
}
}
export class WindowsKeyboardMapper {
private readonly _codeInfo: IHardwareCodeMapping[];
private readonly _hwToKb: KeyCode[];
private readonly _hwToLabel: string[] = [];
private readonly _kbToLabel: string[] = [];
constructor(rawMappings: IKeyboardMapping) {
this._hwToKb = [];
this._hwToLabel = [];
this._kbToLabel = [];
this._kbToLabel[KeyCode.Unknown] = KeyCodeUtils.toString(KeyCode.Unknown);
for (let code = KeyboardEventCode.None; code < KeyboardEventCode.MAX_VALUE; code++) {
const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[code];
if (immutableKeyCode !== -1) {
this._hwToKb[code] = immutableKeyCode;
this._hwToLabel[code] = KeyCodeUtils.toString(immutableKeyCode);
this._kbToLabel[immutableKeyCode] = KeyCodeUtils.toString(immutableKeyCode);
}
}
......@@ -86,12 +221,14 @@ export class WindowsKeyboardMapper {
};
this._codeInfo[code] = mapping;
if (value >= CharCode.a && value <= CharCode.z) {
this._hwToLabel[code] = String.fromCharCode(CharCode.A + (value - CharCode.a));
} else if (value) {
this._hwToLabel[code] = String.fromCharCode(value);
} else {
this._hwToLabel[code] = null;
if (keyCode !== KeyCode.Unknown) {
if (value >= CharCode.a && value <= CharCode.z) {
this._kbToLabel[keyCode] = String.fromCharCode(CharCode.A + (value - CharCode.a));
} else if (value) {
this._kbToLabel[keyCode] = String.fromCharCode(value);
} else {
this._kbToLabel[keyCode] = null;
}
}
this._hwToKb[code] = keyCode;
}
......@@ -116,7 +253,9 @@ export class WindowsKeyboardMapper {
const mapping = this._codeInfo[code];
const strCode = KeyboardEventCodeUtils.toString(code);
const uiLabel = this._hwToLabel[code];
const keyCode = this._hwToKb[code];
const strKeyCode = KeyCodeUtils.toString(keyCode);
const uiLabel = this._kbToLabel[keyCode];
let mods = [0b000, 0b010, 0b101, 0b111];
for (let modIndex = 0; modIndex < mods.length; modIndex++) {
......@@ -149,8 +288,7 @@ export class WindowsKeyboardMapper {
}
}
const keyCode = this._hwToKb[code];
const strKb = `${ctrlKey ? 'Ctrl+' : ''}${shiftKey ? 'Shift+' : ''}${altKey ? 'Alt+' : ''}${KeyCodeUtils.toString(keyCode)}`;
const strKb = `${ctrlKey ? 'Ctrl+' : ''}${shiftKey ? 'Shift+' : ''}${altKey ? 'Alt+' : ''}${strKeyCode}`;
result.push(`| ${this._leftPad(strHw, 30)} | ${strKey} | ${this._leftPad(strKb, 25)} | ${this._leftPad(uiHwLabel, 25)} |`);
}
......@@ -174,6 +312,26 @@ export class WindowsKeyboardMapper {
}
return char.charCodeAt(0);
}
public getUILabelForKeyCode(keyCode: KeyCode): string {
return this._getLabelForKeyCode(keyCode);
}
public getAriaLabelForKeyCode(keyCode: KeyCode): string {
return this._getLabelForKeyCode(keyCode);
}
private _getLabelForKeyCode(keyCode: KeyCode): string {
return this._kbToLabel[keyCode] || KeyCodeUtils.toString(KeyCode.Unknown);
}
public resolveKeybinding(keybinding: Keybinding): WindowsNativeResolvedKeybinding[] {
if (keybinding.type === KeybindingType.Chord) {
return [new WindowsNativeResolvedKeybinding(this, keybinding.firstPart, keybinding.chordPart)];
} else {
return [new WindowsNativeResolvedKeybinding(this, keybinding, null)];
}
}
}
......
......@@ -10,6 +10,8 @@ import { OperatingSystem } from 'vs/base/common/platform';
import { readFile, writeFile } from 'vs/base/node/pfs';
import { TPromise } from 'vs/base/common/winjs.base';
import { WindowsKeyboardMapper, IKeyboardMapping } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper';
import { createKeybinding, KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
import { IHTMLContentElement } from 'vs/base/common/htmlContent';
function createKeyboardMapper(file: string, OS: OperatingSystem): TPromise<WindowsKeyboardMapper> {
return readFile(require.toUrl(`vs/workbench/services/keybinding/test/${file}.js`)).then((buff) => {
......@@ -41,6 +43,72 @@ function assertMapping(mapper: WindowsKeyboardMapper, file: string, done: (err?:
}, done);
}
function _htmlPieces(pieces: string[]): IHTMLContentElement[] {
let children: IHTMLContentElement[] = [];
for (let i = 0, len = pieces.length; i < len; i++) {
if (i !== 0) {
children.push({ tagName: 'span', text: '+' });
}
children.push({ tagName: 'span', className: 'monaco-kbkey', text: pieces[i] });
}
return children;
}
function simpleHTMLLabel(pieces: string[]): IHTMLContentElement {
return {
tagName: 'span',
className: 'monaco-kb',
children: _htmlPieces(pieces)
};
}
function chordHTMLLabel(firstPart: string[], chordPart: string[]): IHTMLContentElement {
return {
tagName: 'span',
className: 'monaco-kb',
children: [].concat(
_htmlPieces(firstPart),
[{ tagName: 'span', text: ' ' }],
_htmlPieces(chordPart)
)
};
}
interface IResolvedKeybindingExpected {
label: string;
ariaLabel: string;
HTMLLabel: IHTMLContentElement[];
electronAccelerator: string;
userSettingsLabel: string;
isChord: boolean;
hasCtrlModifier: boolean;
hasShiftModifier: boolean;
hasAltModifier: boolean;
hasMetaModifier: boolean;
dispatchParts: [string, string];
}
function _assertResolvedKeybinding(mapper: WindowsKeyboardMapper, k: number, expected: IResolvedKeybindingExpected): void {
let kbs = mapper.resolveKeybinding(createKeybinding(k, OperatingSystem.Windows));
assert.equal(kbs.length, 1);
let kb = kbs[0];
let actual: IResolvedKeybindingExpected = {
label: kb.getLabel(),
ariaLabel: kb.getAriaLabel(),
HTMLLabel: kb.getHTMLLabel(),
electronAccelerator: kb.getElectronAccelerator(),
userSettingsLabel: kb.getUserSettingsLabel(),
isChord: kb.isChord(),
hasCtrlModifier: kb.hasCtrlModifier(),
hasShiftModifier: kb.hasShiftModifier(),
hasAltModifier: kb.hasAltModifier(),
hasMetaModifier: kb.hasMetaModifier(),
dispatchParts: kb.getDispatchParts(),
};
assert.deepEqual(actual, expected);
}
suite('keyboardMapper - WINDOWS de_ch', () => {
let mapper: WindowsKeyboardMapper;
......@@ -56,45 +124,105 @@ suite('keyboardMapper - WINDOWS de_ch', () => {
assertMapping(mapper, 'win_de_ch.txt', done);
});
// test('resolveKeybinding', () => {
// 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' },
// ]
// }]]
// );
// });
function assertResolveKeybinding(kb: number, expected: IResolvedKeybindingExpected): void {
_assertResolvedKeybinding(mapper, kb, expected);
}
});
test('resolveKeybinding Ctrl+A', () => {
assertResolveKeybinding(
KeyMod.CtrlCmd | KeyCode.KEY_A,
{
label: 'Ctrl+A',
ariaLabel: 'Control+A',
HTMLLabel: [simpleHTMLLabel(['Ctrl', 'A'])],
electronAccelerator: 'Ctrl+A',
userSettingsLabel: 'ctrl+a',
isChord: false,
hasCtrlModifier: true,
hasShiftModifier: false,
hasAltModifier: false,
hasMetaModifier: false,
dispatchParts: ['ctrl+A', null],
}
);
});
test('resolveKeybinding Ctrl+Z', () => {
assertResolveKeybinding(
KeyMod.CtrlCmd | KeyCode.KEY_Z,
{
label: 'Ctrl+Z',
ariaLabel: 'Control+Z',
HTMLLabel: [simpleHTMLLabel(['Ctrl', 'Z'])],
electronAccelerator: 'Ctrl+Z',
userSettingsLabel: 'ctrl+z',
isChord: false,
hasCtrlModifier: true,
hasShiftModifier: false,
hasAltModifier: false,
hasMetaModifier: false,
dispatchParts: ['ctrl+Z', null],
}
);
});
test('resolveKeybinding Ctrl+]', () => {
assertResolveKeybinding(
KeyMod.CtrlCmd | KeyCode.US_CLOSE_SQUARE_BRACKET,
{
label: 'Ctrl+^',
ariaLabel: 'Control+^',
HTMLLabel: [simpleHTMLLabel(['Ctrl', '^'])],
electronAccelerator: 'Ctrl+]',
userSettingsLabel: 'ctrl+]',
isChord: false,
hasCtrlModifier: true,
hasShiftModifier: false,
hasAltModifier: false,
hasMetaModifier: false,
dispatchParts: ['ctrl+]', null],
}
);
});
test('resolveKeybinding Ctrl+/', () => {
assertResolveKeybinding(
KeyMod.CtrlCmd | KeyCode.US_SLASH,
{
label: 'Ctrl+§',
ariaLabel: 'Control+§',
HTMLLabel: [simpleHTMLLabel(['Ctrl', '§'])],
electronAccelerator: 'Ctrl+/',
userSettingsLabel: 'ctrl+/',
isChord: false,
hasCtrlModifier: true,
hasShiftModifier: false,
hasAltModifier: false,
hasMetaModifier: false,
dispatchParts: ['ctrl+/', null],
}
);
});
test('resolveKeybinding Ctrl+K Ctrl+\\', () => {
assertResolveKeybinding(
KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_BACKSLASH),
{
label: 'Ctrl+K Ctrl+ä',
ariaLabel: 'Control+K Control+ä',
HTMLLabel: [chordHTMLLabel(['Ctrl', 'K'], ['Ctrl', 'ä'])],
electronAccelerator: null,
userSettingsLabel: 'ctrl+k ctrl+\\',
isChord: true,
hasCtrlModifier: false,
hasShiftModifier: false,
hasAltModifier: false,
hasMetaModifier: false,
dispatchParts: ['ctrl+K', 'ctrl+\\'],
}
);
});
});
suite('keyboardMapper - WINDOWS en_us', () => {
......@@ -110,4 +238,27 @@ suite('keyboardMapper - WINDOWS en_us', () => {
test('mapping', (done) => {
assertMapping(mapper, 'win_en_us.txt', done);
});
});
\ No newline at end of file
function assertResolveKeybinding(kb: number, expected: IResolvedKeybindingExpected): void {
_assertResolvedKeybinding(mapper, kb, expected);
}
test('resolveKeybinding Ctrl+K Ctrl+\\', () => {
assertResolveKeybinding(
KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_BACKSLASH),
{
label: 'Ctrl+K Ctrl+\\',
ariaLabel: 'Control+K Control+\\',
HTMLLabel: [chordHTMLLabel(['Ctrl', 'K'], ['Ctrl', '\\'])],
electronAccelerator: null,
userSettingsLabel: 'ctrl+k ctrl+\\',
isChord: true,
hasCtrlModifier: false,
hasShiftModifier: false,
hasAltModifier: false,
hasMetaModifier: false,
dispatchParts: ['ctrl+K', 'ctrl+\\'],
}
);
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册