From 8f5b2f5dab44b7d5743b7dbc6b0a46653924aa03 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 16 Mar 2017 23:27:53 +0100 Subject: [PATCH] #18095 Keybindings editor - Define keybinding action with keyboard shortcut - Search keybindings action with keyboard shortcut --- .../preferences/browser/keybindingsEditor.ts | 99 ++++++++++++------- .../browser/media/keybindingsEditor.css | 2 +- .../browser/preferences.contribution.ts | 12 ++- .../preferences/browser/preferencesActions.ts | 50 +++++++++- .../parts/preferences/common/preferences.ts | 19 +++- 5 files changed, 138 insertions(+), 44 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index e430e831792..a0b2f7a2439 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -17,10 +17,10 @@ import { EditorInput } from 'vs/workbench/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { KeybindingsEditorModel, IKeybindingItemEntry, IKeybindingItem, IListEntry, KEYBINDING_ENTRY_TEMPLATE_ID, KEYBINDING_HEADER_TEMPLATE_ID } from 'vs/workbench/parts/preferences/common/keybindingsEditorModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindingService, IKeybindingItem2, KeybindingSource } from 'vs/platform/keybinding/common/keybinding'; +import { IKeybindingService, KeybindingSource } from 'vs/platform/keybinding/common/keybinding'; import { SearchWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { DefineKeybindingWidget } from 'vs/workbench/parts/preferences/browser/keybindingWidgets'; -import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; +import { IPreferencesService, IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, KEYBINDINGS_EDITOR_ID, CONTEXT_KEYBINDINGS_EDITOR } from 'vs/workbench/parts/preferences/common/preferences'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { renderHtml } from 'vs/base/browser/htmlContentRenderer'; import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; @@ -28,15 +28,10 @@ import { IListService } from 'vs/platform/list/browser/listService'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IDelegate, IRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; let $ = DOM.$; -export interface IKeybindingsEditor { - - defineKeybinding(keybindingItem: IKeybindingItem); - -} - export class KeybindingsEditorInput extends EditorInput { public static ID: string = 'worknench.input.keybindings'; @@ -66,17 +61,21 @@ export class KeybindingsEditorInput extends EditorInput { export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor { - public static ID: string = 'workbench.editor.keybindings'; + public static ID: string = KEYBINDINGS_EDITOR_ID; + private headerContainer: HTMLElement; private searchWidget: SearchWidget; - private defineKeybindingWidget: DefineKeybindingWidget; + private overlayContainer: HTMLElement; - private headerContainer: HTMLElement; - private dimension: Dimension; + private defineKeybindingWidget: DefineKeybindingWidget; - private delayedFiltering: Delayer; private keybindingsListContainer: HTMLElement; private keybindingsList: List; + private dimension: Dimension; + private delayedFiltering: Delayer; + private keybindingsEditorContextKey: IContextKey; + private keybindingFocusContextKey: IContextKey; + constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @@ -85,11 +84,15 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor @IPreferencesService private preferencesService: IPreferencesService, @IKeybindingEditingService private keybindingEditingService: IKeybindingEditingService, @IListService private listService: IListService, + @IContextKeyService private contextKeyService: IContextKeyService, @IInstantiationService private instantiationService: IInstantiationService ) { super(KeybindingsEditor.ID, telemetryService, themeService); this.delayedFiltering = new Delayer(300); this._register(keybindingsService.onDidUpdateKeybindings(() => this.render())); + + this.keybindingsEditorContextKey = CONTEXT_KEYBINDINGS_EDITOR.bindTo(this.contextKeyService); + this.keybindingFocusContextKey = CONTEXT_KEYBINDING_FOCUS.bindTo(this.contextKeyService); } createEditor(parent: Builder): void { @@ -100,6 +103,10 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor this.createOverlayContainer(keybindingsEditorElement); this.createHeader(keybindingsEditorElement); this.createBody(keybindingsEditorElement); + + const focusTracker = this._register(DOM.trackFocus(parentElement)); + this._register(focusTracker.addFocusListener(() => this.keybindingsEditorContextKey.set(true))); + this._register(focusTracker.addBlurListener(() => this.keybindingsEditorContextKey.reset())); } setInput(input: KeybindingsEditorInput): TPromise { @@ -115,6 +122,8 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor clearInput(): void { super.clearInput(); this.searchWidget.clear(); + this.keybindingsEditorContextKey.reset(); + this.keybindingFocusContextKey.reset(); } layout(dimension: Dimension): void { @@ -134,6 +143,28 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor this.searchWidget.focus(); } + get activeKeybindingEntry(): IKeybindingItemEntry { + const focusedElement = this.keybindingsList.getFocusedElements()[0]; + return focusedElement && focusedElement.templateId === KEYBINDING_ENTRY_TEMPLATE_ID ? focusedElement : null; + } + + defineKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise { + this.overlayContainer.style.display = 'block'; + return this.defineKeybindingWidget.define().then(key => { + if (key) { + return this.keybindingEditingService.editKeybinding(key, keybindingEntry.keybindingItem); + } + return null; + }).then(() => { + this.overlayContainer.style.display = 'none'; + this.focus(); + }); + } + + search(filter: string): void { + this.searchWidget.focus(); + } + private createOverlayContainer(parent: HTMLElement): void { this.overlayContainer = DOM.append(parent, $('.overlay-container')); this.overlayContainer.style.position = 'absolute'; @@ -163,12 +194,14 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor } private createList(parent: HTMLElement): void { - const delegate = new Delegate(); this.keybindingsListContainer = DOM.append(parent, $('.keybindings-list-container')); - this.keybindingsList = this._register(new List(this.keybindingsListContainer, delegate, [new KeybindingHeaderRenderer(), new KeybindingItemRenderer(this)], { identityProvider: e => e.id })); + + this.keybindingsList = this._register(new List(this.keybindingsListContainer, new Delegate(), [new KeybindingHeaderRenderer(), new KeybindingItemRenderer(this)], { identityProvider: e => e.id })); this._register(this.keybindingsList.onContextMenu(e => this.onContextMenu(e))); this._register(this.keybindingsList.onFocusChange(e => this.onFocusChange(e))); this._register(this.keybindingsList.onDOMFocus(() => this.keybindingsList.focusNext())); + this._register(this.keybindingsList.onDOMBlur(() => this.keybindingFocusContextKey.reset())); + this._register(this.listService.register(this.keybindingsList)); } @@ -196,8 +229,17 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor } private onFocusChange(e: IListEvent): void { - if (e.elements[0] && e.elements[0].templateId === KEYBINDING_HEADER_TEMPLATE_ID) { + this.keybindingFocusContextKey.reset(); + const element = e.elements[0]; + if (!element) { + return; + } + if (element.templateId === KEYBINDING_HEADER_TEMPLATE_ID) { this.keybindingsList.focusNext(); + return; + } + if (element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) { + this.keybindingFocusContextKey.set(true); } } @@ -209,19 +251,6 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor run: () => this.keybindingEditingService.removeKeybinding(keybinding) }; } - - defineKeybinding(keybindingItem: IKeybindingItem2): void { - this.overlayContainer.style.display = 'block'; - this.defineKeybindingWidget.define().then(key => { - this.overlayContainer.style.display = 'none'; - if (key) { - this.keybindingEditingService.editKeybinding(key, keybindingItem); - } - }, () => { - this.overlayContainer.style.display = 'none'; - this.focus(); - }); - } } class Delegate implements IDelegate { @@ -327,30 +356,30 @@ class ActionsColumn extends Column { this.actionBar.clear(); const actions = []; if (keybindingItemEntry.keybindingItem.keybinding) { - actions.push(this.createEditAction(keybindingItemEntry.keybindingItem)); + actions.push(this.createEditAction(keybindingItemEntry)); } else { - actions.push(this.createAddAction(keybindingItemEntry.keybindingItem)); + actions.push(this.createAddAction(keybindingItemEntry)); } this.actionBar.push(actions, { icon: true }); } - private createEditAction(keybinding: IKeybindingItem): IAction { + private createEditAction(keybindingItemEntry: IKeybindingItemEntry): IAction { return { class: 'edit', enabled: true, id: 'editKeybinding', tooltip: localize('change', "Change Keybinding"), - run: () => this.keybindingsEditor.defineKeybinding(keybinding) + run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry) }; } - private createAddAction(keybinding: IKeybindingItem): IAction { + private createAddAction(keybindingItemEntry: IKeybindingItemEntry): IAction { return { class: 'add', enabled: true, id: 'addKeybinding', tooltip: localize('add', "Add Keybinding"), - run: () => this.keybindingsEditor.defineKeybinding(keybinding) + run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry) }; } } diff --git a/src/vs/workbench/parts/preferences/browser/media/keybindingsEditor.css b/src/vs/workbench/parts/preferences/browser/media/keybindingsEditor.css index 44662a72453..92fb3186c5c 100644 --- a/src/vs/workbench/parts/preferences/browser/media/keybindingsEditor.css +++ b/src/vs/workbench/parts/preferences/browser/media/keybindingsEditor.css @@ -69,7 +69,7 @@ } .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.keybindings-list-header, -.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.even { +.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.even:not(.focused) { background-color: rgba(130, 130, 130, 0.04); } diff --git a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts index 5cb6a24f29b..66f702dfc42 100644 --- a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts @@ -16,12 +16,13 @@ import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { DefaultPreferencesEditorInput, PreferencesEditor, PreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; import { KeybindingsEditor, KeybindingsEditorInput } from 'vs/workbench/parts/preferences/browser/keybindingsEditor'; -import { OpenGlobalSettingsAction, OpenGlobalKeybindingsAction, OpenWorkspaceSettingsAction, ConfigureLanguageBasedSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; -import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; +import { OpenGlobalSettingsAction, OpenGlobalKeybindingsAction, OpenWorkspaceSettingsAction, ConfigureLanguageBasedSettingsAction, DefineKeybindingAction, SearchKeybindingAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; +import { IPreferencesService, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR } from 'vs/workbench/parts/preferences/common/preferences'; import { PreferencesService } from 'vs/workbench/parts/preferences/browser/preferencesService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { PreferencesContentProvider } from 'vs/workbench/parts/preferences/common/preferencesContentProvider'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; registerSingleton(IPreferencesService, PreferencesService); @@ -163,5 +164,10 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalSettingsActi registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceSettingsAction, OpenWorkspaceSettingsAction.ID, OpenWorkspaceSettingsAction.LABEL), 'Preferences: Open Workspace Settings', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsAction, OpenGlobalKeybindingsAction.ID, OpenGlobalKeybindingsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_S) }), 'Preferences: Open Keyboard Shortcuts', category); registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureLanguageBasedSettingsAction, ConfigureLanguageBasedSettingsAction.ID, ConfigureLanguageBasedSettingsAction.LABEL), 'Preferences: Configure Language Specific Settings', category); - +registry.registerWorkbenchAction(new SyncActionDescriptor(SearchKeybindingAction, SearchKeybindingAction.ID, SearchKeybindingAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_F, +}, CONTEXT_KEYBINDINGS_EDITOR), ''); +registry.registerWorkbenchAction(new SyncActionDescriptor(DefineKeybindingAction, DefineKeybindingAction.ID, DefineKeybindingAction.LABEL, { + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K), +}, ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS)), ''); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(PreferencesContentProvider); \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts index a094c5a779e..07e7721e0de 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts @@ -10,7 +10,8 @@ import URI from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; -import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; +import { IPreferencesService, IKeybindingsEditor, KEYBINDINGS_EDITOR_ID } from 'vs/workbench/parts/preferences/common/preferences'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; export class OpenGlobalSettingsAction extends Action { @@ -113,4 +114,51 @@ export class ConfigureLanguageBasedSettingsAction extends Action { }); } +} + +export class DefineKeybindingAction extends Action { + + public static ID = 'keybindings.editor.define'; + public static LABEL = nls.localize('keybindings.editor.define', "Define Keybinding"); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + const activeEditor = this.editorService.getActiveEditor(); + if (activeEditor.getId() === KEYBINDINGS_EDITOR_ID) { + const keybindingsEditor = activeEditor as IKeybindingsEditor; + if (keybindingsEditor.activeKeybindingEntry) { + return keybindingsEditor.defineKeybinding(keybindingsEditor.activeKeybindingEntry); + } + } + return TPromise.as(null); + } +} + +export class SearchKeybindingAction extends Action { + + public static ID = 'keybindings.editor.search'; + public static LABEL = nls.localize('keybindings.editor.search', "Search Keybindings"); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + const activeEditor = this.editorService.getActiveEditor(); + if (activeEditor.getId() === KEYBINDINGS_EDITOR_ID) { + (activeEditor as IKeybindingsEditor).search(''); + } + return TPromise.as(null); + } } \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index 81aa489e60b..8de7e93213d 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -10,6 +10,7 @@ import { IRange } from 'vs/editor/common/editorCommon'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IEditor } from 'vs/platform/editor/common/editor'; +import { IKeybindingItemEntry } from 'vs/workbench/parts/preferences/common/keybindingsEditorModel'; export interface ISettingsGroup { id: string; @@ -80,8 +81,18 @@ export interface IPreferencesService { configureSettingsForLanguage(language: string): void; } -export const CONTEXT_SETTINGS_EDITOR = new RawContextKey('settingsEditor', false); -export const CONTEXT_KEYBINDINGS_EDITOR = new RawContextKey('inKeybindingsEditor', false); -export const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; -export const ACTION_DEFINE_KEYBINDING = 'kebindings.action.define'; \ No newline at end of file +export interface IKeybindingsEditor extends IEditor { + + activeKeybindingEntry: IKeybindingItemEntry; + + search(filter: string): void; + defineKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; +} + +export const KEYBINDINGS_EDITOR_ID = 'workbench.editor.keybindings'; +export const CONTEXT_SETTINGS_EDITOR = new RawContextKey('inSettingsEditor', false); +export const CONTEXT_KEYBINDINGS_EDITOR = new RawContextKey('inKeybindings', false); +export const CONTEXT_KEYBINDING_FOCUS = new RawContextKey('keybindingFocus', false); + +export const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; \ No newline at end of file -- GitLab