keyboardLayoutPicker.ts 8.2 KB
Newer Older
P
Peng Lyu 已提交
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor } from 'vs/platform/statusbar/common/statusbar';
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
9
import { IKeymapService, areKeyboardLayoutsEqual, parseKeyboardLayoutDescription, getKeyboardLayoutId, IKeyboardLayoutInfo } from 'vs/workbench/services/keybinding/common/keymapInfo';
P
Peng Lyu 已提交
10 11 12 13 14 15 16 17 18 19
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { KEYBOARD_LAYOUT_OPEN_PICKER } from 'vs/workbench/contrib/preferences/common/preferences';
import { Action } from 'vs/base/common/actions';
import { isWeb, isMacintosh, isWindows } from 'vs/base/common/platform';
import { QuickPickInput, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
P
Peng Lyu 已提交
20 21 22 23 24
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { VSBuffer } from 'vs/base/common/buffer';
import { IEditor } from 'vs/workbench/common/editor';
P
Peng Lyu 已提交
25 26 27 28 29 30 31 32 33 34 35

export class KeyboardLayoutPickerContribution extends Disposable implements IWorkbenchContribution {
	private readonly pickerElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());

	constructor(
		@IKeymapService private readonly keymapService: IKeymapService,
		@IStatusbarService private readonly statusbarService: IStatusbarService,
	) {
		super();

		let layout = this.keymapService.getCurrentKeyboardLayout();
36
		if (layout) {
37
			let layoutInfo = parseKeyboardLayoutDescription(layout);
38 39
			this.pickerElement.value = this.statusbarService.addEntry(
				{
P
Peng Lyu 已提交
40
					text: `Layout: ${layoutInfo.label}`,
41 42 43 44 45 46 47 48
					// tooltip: nls.localize('keyboard.layout.tooltip', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\"."),
					command: KEYBOARD_LAYOUT_OPEN_PICKER
				},
				'status.editor.screenReaderMode',
				nls.localize('status.editor.screenReaderMode', "Screen Reader Mode"),
				StatusbarAlignment.RIGHT
			);
		}
P
Peng Lyu 已提交
49 50

		this._register(keymapService.onDidChangeKeyboardMapper(() => {
51
			let layout = this.keymapService.getCurrentKeyboardLayout();
52
			let layoutInfo = parseKeyboardLayoutDescription(layout);
P
Peng Lyu 已提交
53

54
			if (this.pickerElement.value) {
P
Peng Lyu 已提交
55
				this.pickerElement.value.update({
P
Peng Lyu 已提交
56
					text: `Layout: ${layoutInfo.label}`,
P
Peng Lyu 已提交
57
					command: KEYBOARD_LAYOUT_OPEN_PICKER
P
Peng Lyu 已提交
58
				});
59 60 61 62 63 64 65
			} else {
				this.pickerElement.value = this.statusbarService.addEntry(
					{
						text: `Layout: ${layoutInfo}`,
						// tooltip: nls.localize('keyboard.layout.tooltip', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\"."),
						command: KEYBOARD_LAYOUT_OPEN_PICKER
					},
66 67
					'status.workbench.keyboardLayout',
					nls.localize('status.workbench.keyboardLayout', "Current keyboard layout"),
68 69
					StatusbarAlignment.RIGHT
				);
P
Peng Lyu 已提交
70 71 72 73 74 75 76 77
			}
		}));
	}
}

const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchContributionsRegistry.registerWorkbenchContribution(KeyboardLayoutPickerContribution, LifecyclePhase.Starting);

78 79 80
interface LayoutQuickPickItem extends IQuickPickItem {
	layout: IKeyboardLayoutInfo;
}
P
Peng Lyu 已提交
81 82 83 84 85

export class KeyboardLayoutPickerAction extends Action {
	static readonly ID = KEYBOARD_LAYOUT_OPEN_PICKER;
	static readonly LABEL = nls.localize('keyboard.chooseLayout', "Change keyboard layout");

P
Peng Lyu 已提交
86 87
	private static DEFAULT_CONTENT: string = [
		`// ${nls.localize('displayLanguage', 'Defines the keyboard layout used in VS Code in the browser environment.')}`,
P
Peng Lyu 已提交
88
		`// ${nls.localize('doc', 'Open VS Code and run "Developer: Inspect Key Mappings (JSON)" from Command Palette.')}`,
P
Peng Lyu 已提交
89 90 91 92 93
		``,
		`// Once you have the keyboard layout info, please paste it below.`,
		'\n'
	].join('\n');

P
Peng Lyu 已提交
94 95 96
	constructor(
		actionId: string,
		actionLabel: string,
P
Peng Lyu 已提交
97
		@IFileService private readonly fileService: IFileService,
P
Peng Lyu 已提交
98 99
		@IQuickInputService private readonly quickInputService: IQuickInputService,
		@IKeymapService private readonly keymapService: IKeymapService,
P
Peng Lyu 已提交
100 101 102
		@IConfigurationService private readonly configurationService: IConfigurationService,
		@IEnvironmentService private readonly environmentService: IEnvironmentService,
		@IEditorService private readonly editorService: IEditorService
P
Peng Lyu 已提交
103 104 105 106 107 108 109 110
	) {
		super(actionId, actionLabel);

		this.enabled = isWeb;
	}

	async run(): Promise<void> {
		let layouts = this.keymapService.getAllKeyboardLayouts();
P
Peng Lyu 已提交
111 112 113
		let currentLayout = this.keymapService.getCurrentKeyboardLayout();
		let layoutConfig = this.configurationService.getValue('keyboard.layout');
		let isAutoDetect = layoutConfig === 'autodetect';
P
Peng Lyu 已提交
114 115

		const picks: QuickPickInput[] = layouts.map(layout => {
P
Peng Lyu 已提交
116
			const picked = !isAutoDetect && areKeyboardLayoutsEqual(currentLayout, layout);
117
			const layoutInfo = parseKeyboardLayoutDescription(layout);
P
Peng Lyu 已提交
118
			return {
119
				layout: layout,
P
Peng Lyu 已提交
120
				label: [layoutInfo.label, (layout && layout.isUserKeyboardLayout) ? '(User configured layout)' : ''].join(' '),
P
Peng Lyu 已提交
121
				id: (<any>layout).text || (<any>layout).lang || (<any>layout).layout,
P
Peng Lyu 已提交
122
				description: layoutInfo.description + (picked ? ' (Current layout)' : ''),
P
Peng Lyu 已提交
123
				picked: !isAutoDetect && areKeyboardLayoutsEqual(currentLayout, layout)
P
Peng Lyu 已提交
124
			};
P
Peng Lyu 已提交
125 126
		}).sort((a: IQuickPickItem, b: IQuickPickItem) => {
			return a.label < b.label ? -1 : (a.label > b.label ? 1 : 0);
P
Peng Lyu 已提交
127 128 129 130 131 132 133
		});

		if (picks.length > 0) {
			const platform = isMacintosh ? 'Mac' : isWindows ? 'Win' : 'Linux';
			picks.unshift({ type: 'separator', label: nls.localize('layoutPicks', "Keyboard Layouts ({0})", platform) });
		}

P
Peng Lyu 已提交
134
		let configureKeyboardLayout: IQuickPickItem = { label: nls.localize('configureKeyboardLayout', "Configure Keyboard Layout") };
P
Peng Lyu 已提交
135 136 137 138 139

		picks.unshift(configureKeyboardLayout);

		// Offer to "Auto Detect"
		const autoDetectMode: IQuickPickItem = {
P
Peng Lyu 已提交
140
			label: nls.localize('autoDetect', "Auto Detect"),
141
			description: isAutoDetect ? `Current: ${parseKeyboardLayoutDescription(currentLayout).label}` : undefined,
P
Peng Lyu 已提交
142
			picked: isAutoDetect ? true : undefined
P
Peng Lyu 已提交
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
		};

		picks.unshift(autoDetectMode);

		const pick = await this.quickInputService.pick(picks, { placeHolder: nls.localize('pickKeyboardLayout', "Select Keyboard Layout"), matchOnDescription: true });
		if (!pick) {
			return;
		}

		if (pick === autoDetectMode) {
			// set keymap service to auto mode
			this.configurationService.updateValue('keyboard.layout', 'autodetect');
			return;
		}

		if (pick === configureKeyboardLayout) {
P
Peng Lyu 已提交
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
			const file = this.environmentService.keyboardLayoutResource;

			await this.fileService.resolve(file).then(undefined, (error) => {
				return this.fileService.createFile(file, VSBuffer.fromString(KeyboardLayoutPickerAction.DEFAULT_CONTENT));
			}).then((stat): Promise<IEditor | null> | null => {
				if (!stat) {
					return null;
				}
				return this.editorService.openEditor({
					resource: stat.resource,
					mode: 'jsonc'
				});
			}, (error) => {
				throw new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", file.toString(), error));
			});

			return Promise.resolve();
P
Peng Lyu 已提交
176 177
		}

178
		this.configurationService.updateValue('keyboard.layout', getKeyboardLayoutId((<LayoutQuickPickItem>pick).layout));
P
Peng Lyu 已提交
179 180 181 182
	}
}

const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
P
Peng Lyu 已提交
183
registry.registerWorkbenchAction(new SyncActionDescriptor(KeyboardLayoutPickerAction, KeyboardLayoutPickerAction.ID, KeyboardLayoutPickerAction.LABEL, {}), 'Change Language Mode');