Skip to content

Commit

Permalink
#5042: Implemented dynamic keyboard layout detection
Browse files Browse the repository at this point in the history
Signed-off-by: Miro Spönemann <miro.spoenemann@typefox.io>
  • Loading branch information
spoenemann committed May 2, 2019
1 parent ff9c78d commit dcd3a53
Show file tree
Hide file tree
Showing 5 changed files with 348 additions and 93 deletions.
1 change: 1 addition & 0 deletions packages/core/src/browser/keybinding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ export class KeybindingRegistry {
return;
}

this.keyboardLayoutService.validateKeyCode(keyCode);
this.keySequence.push(keyCode);
const bindings = this.getKeybindingsForKeySequence(this.keySequence);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/********************************************************************************
* Copyright (C) 2019 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { Container, injectable } from 'inversify';
import { IMacKeyboardLayoutInfo } from 'native-keymap';
import * as os from '../../common/os';
import { ILogger, Loggable } from '../../common/logger';
import * as chai from 'chai';
import * as sinon from 'sinon';
import { BrowserKeyboardLayoutProvider, DEFAULT_LAYOUT_DATA } from './browser-keyboard-layout-provider';
import { Key, KeyCode } from './keys';

describe('browser keyboard layout provider', function () {

let stubOSX: sinon.SinonStub;
let stubWindows: sinon.SinonStub;
let stubNavigator: sinon.SinonStub;

const setup = (system: 'mac' | 'win' | 'linux') => {
switch (system) {
case 'mac':
stubOSX = sinon.stub(os, 'isOSX').value(true);
stubWindows = sinon.stub(os, 'isWindows').value(false);
break;
case 'win':
stubOSX = sinon.stub(os, 'isOSX').value(false);
stubWindows = sinon.stub(os, 'isWindows').value(true);
break;
default:
stubOSX = sinon.stub(os, 'isOSX').value(false);
stubWindows = sinon.stub(os, 'isWindows').value(false);
}
// tslint:disable-next-line:no-any
stubNavigator = sinon.stub(global, 'navigator' as any).value({});
const container = new Container();
container.bind(BrowserKeyboardLayoutProvider).toSelf().inSingletonScope();
container.bind(ILogger).to(MockLogger);
const service = container.get(BrowserKeyboardLayoutProvider);
return service;
};

afterEach(() => {
stubOSX.restore();
stubWindows.restore();
stubNavigator.restore();
});

it('detects German Mac layout', async () => {
const service = setup('mac');
let currentLayout = await service.getNativeLayout();
service.onDidChangeNativeLayout(l => {
currentLayout = l;
});

chai.expect(currentLayout).to.equal(DEFAULT_LAYOUT_DATA.raw);
service.validateKeyCode(new KeyCode({ key: Key.SEMICOLON, character: 'ö' }));
chai.expect((currentLayout.info as IMacKeyboardLayoutInfo).id).to.equal('com.apple.keylayout.German');
});

it('detects French Mac layout', async () => {
const service = setup('mac');
let currentLayout = await service.getNativeLayout();
service.onDidChangeNativeLayout(l => {
currentLayout = l;
});

chai.expect(currentLayout).to.equal(DEFAULT_LAYOUT_DATA.raw);
service.validateKeyCode(new KeyCode({ key: Key.SEMICOLON, character: 'm' }));
chai.expect((currentLayout.info as IMacKeyboardLayoutInfo).id).to.equal('com.apple.keylayout.French');
});

it('detects keyboard layout change', async () => {
const service = setup('mac');
let currentLayout = await service.getNativeLayout();
service.onDidChangeNativeLayout(l => {
currentLayout = l;
});

service.validateKeyCode(new KeyCode({ key: Key.QUOTE, character: 'ä' }));
service.validateKeyCode(new KeyCode({ key: Key.SEMICOLON, character: 'ö' }));
service.validateKeyCode(new KeyCode({ key: Key.BRACKET_LEFT, character: 'ü' }));
chai.expect((currentLayout.info as IMacKeyboardLayoutInfo).id).to.equal('com.apple.keylayout.German');
service.validateKeyCode(new KeyCode({ key: Key.SEMICOLON, character: 'm' }));
chai.expect((currentLayout.info as IMacKeyboardLayoutInfo).id).to.equal('com.apple.keylayout.French');
});

});

@injectable()
class MockLogger implements Partial<ILogger> {
trace(loggable: Loggable): Promise<void> {
return Promise.resolve();
}
debug(loggable: Loggable): Promise<void> {
return Promise.resolve();
}
info(loggable: Loggable): Promise<void> {
return Promise.resolve();
}
warn(loggable: Loggable): Promise<void> {
return Promise.resolve();
}
error(loggable: Loggable): Promise<void> {
return Promise.resolve();
}
fatal(loggable: Loggable): Promise<void> {
return Promise.resolve();
}
}
Loading

0 comments on commit dcd3a53

Please sign in to comment.