/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInputButton, IInputBox, QuickPickInput, IKeyMods } from 'vs/platform/quickinput/common/quickInput'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder, badgeBackground, badgeForeground, contrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, progressBarBackground, widgetShadow, listFocusForeground, listFocusBackground, activeContrastBorder, pickerGroupBorder, pickerGroupForeground, quickInputForeground, quickInputBackground, quickInputTitleBackground } from 'vs/platform/theme/common/colorRegistry'; import { CancellationToken } from 'vs/base/common/cancellation'; import { computeStyles } from 'vs/platform/theme/common/styler'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { QuickInputController, IQuickInputStyles, IQuickInputOptions } from 'vs/base/parts/quickinput/browser/quickInput'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { List, IListOptions } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; import { IQuickAccessController } from 'vs/platform/quickinput/common/quickAccess'; import { QuickAccessController } from 'vs/platform/quickinput/browser/quickAccess'; export interface IQuickInputControllerHost extends ILayoutService { } export class QuickInputService extends Themable implements IQuickInputService { _serviceBrand: undefined; get backButton(): IQuickInputButton { return this.controller.backButton; } get onShow() { return this.controller.onShow; } get onHide() { return this.controller.onHide; } private _controller: QuickInputController | undefined; private get controller(): QuickInputController { if (!this._controller) { this._controller = this._register(this.createController()); } return this._controller; } private _quickAccess: IQuickAccessController | undefined; get quickAccess(): IQuickAccessController { if (!this._quickAccess) { this._quickAccess = this._register(this.instantiationService.createInstance(QuickAccessController)); } return this._quickAccess; } private readonly contexts = new Map>(); constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IThemeService themeService: IThemeService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @ILayoutService protected readonly layoutService: ILayoutService ) { super(themeService); } protected createController(host: IQuickInputControllerHost = this.layoutService, options?: Partial): QuickInputController { const defaultOptions: IQuickInputOptions = { idPrefix: 'quickInput_', // Constant since there is still only one. container: host.container, ignoreFocusOut: () => false, isScreenReaderOptimized: () => this.accessibilityService.isScreenReaderOptimized(), backKeybindingLabel: () => undefined, setContextKey: (id?: string) => this.setContextKey(id), returnFocus: () => host.focus(), createList: ( user: string, container: HTMLElement, delegate: IListVirtualDelegate, renderers: IListRenderer[], options: IListOptions, ) => this.instantiationService.createInstance(WorkbenchList, user, container, delegate, renderers, options) as List, styles: this.computeStyles() }; const controller = this._register(new QuickInputController({ ...defaultOptions, ...options })); controller.layout(host.dimension, host.offset?.top ?? 0); // Layout changes this._register(host.onLayout(dimension => controller.layout(dimension, host.offset?.top ?? 0))); // Context keys this._register(controller.onShow(() => this.resetContextKeys())); this._register(controller.onHide(() => this.resetContextKeys())); return controller; } private setContextKey(id?: string) { let key: IContextKey | undefined; if (id) { key = this.contexts.get(id); if (!key) { key = new RawContextKey(id, false) .bindTo(this.contextKeyService); this.contexts.set(id, key); } } if (key && key.get()) { return; // already active context } this.resetContextKeys(); if (key) { key.set(true); } } private resetContextKeys() { this.contexts.forEach(context => { if (context.get()) { context.reset(); } }); } pick>(picks: Promise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): Promise { return this.controller.pick(picks, options, token); } input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise { return this.controller.input(options, token); } createQuickPick(): IQuickPick { return this.controller.createQuickPick(); } createInputBox(): IInputBox { return this.controller.createInputBox(); } focus() { this.controller.focus(); } toggle() { this.controller.toggle(); } navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration) { this.controller.navigate(next, quickNavigate); } accept(keyMods?: IKeyMods) { return this.controller.accept(keyMods); } back() { return this.controller.back(); } cancel() { return this.controller.cancel(); } hide(focusLost?: boolean): void { return this.controller.hide(focusLost); } protected updateStyles() { this.controller.applyStyles(this.computeStyles()); } private computeStyles(): IQuickInputStyles { return { widget: { ...computeStyles(this.theme, { quickInputBackground, quickInputForeground, quickInputTitleBackground, contrastBorder, widgetShadow }), }, inputBox: computeStyles(this.theme, { inputForeground, inputBackground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder }), countBadge: computeStyles(this.theme, { badgeBackground, badgeForeground, badgeBorder: contrastBorder }), button: computeStyles(this.theme, { buttonForeground, buttonBackground, buttonHoverBackground, buttonBorder: contrastBorder }), progressBar: computeStyles(this.theme, { progressBarBackground }), list: computeStyles(this.theme, { listBackground: quickInputBackground, // Look like focused when inactive. listInactiveFocusForeground: listFocusForeground, listInactiveFocusBackground: listFocusBackground, listFocusOutline: activeContrastBorder, listInactiveFocusOutline: activeContrastBorder, pickerGroupBorder, pickerGroupForeground }) }; } }