mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from master
This commit is contained in:
@@ -3,11 +3,9 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./quickInput';
|
||||
import { Component } from 'vs/workbench/common/component';
|
||||
import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox, IQuickPickItemButtonEvent, QuickPickInput, IQuickPickSeparator, IKeyMods } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -15,7 +13,6 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { QuickInputList } from './quickInputList';
|
||||
import { QuickInputBox } from './quickInputBox';
|
||||
@@ -38,14 +35,20 @@ import { ICommandAndKeybindingRule, KeybindingWeight } from 'vs/platform/keybind
|
||||
import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen';
|
||||
import { ActionBar, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils';
|
||||
import { AccessibilitySupport } from 'vs/base/common/platform';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
type Writeable<T> = { -readonly [P in keyof T]: T[P] };
|
||||
|
||||
const backButton = {
|
||||
iconPath: {
|
||||
dark: URI.parse(require.toUrl('vs/workbench/browser/parts/quickinput/media/dark/arrow-left.svg')),
|
||||
@@ -70,9 +73,13 @@ interface QuickInputUI {
|
||||
onDidAccept: Event<void>;
|
||||
onDidTriggerButton: Event<IQuickInputButton>;
|
||||
ignoreFocusOut: boolean;
|
||||
keyMods: Writeable<IKeyMods>;
|
||||
isScreenReaderOptimized(): boolean;
|
||||
show(controller: QuickInput): void;
|
||||
setVisibilities(visibilities: Visibilities): void;
|
||||
setComboboxAccessibility(enabled: boolean): void;
|
||||
setEnabled(enabled: boolean): void;
|
||||
setContextKey(contextKey?: string): void;
|
||||
hide(): void;
|
||||
}
|
||||
|
||||
@@ -94,6 +101,7 @@ class QuickInput implements IQuickInput {
|
||||
private _totalSteps: number;
|
||||
protected visible = false;
|
||||
private _enabled = true;
|
||||
private _contextKey: string;
|
||||
private _busy = false;
|
||||
private _ignoreFocusOut = false;
|
||||
private _buttons: IQuickInputButton[] = [];
|
||||
@@ -148,6 +156,15 @@ class QuickInput implements IQuickInput {
|
||||
this.update();
|
||||
}
|
||||
|
||||
get contextKey() {
|
||||
return this._contextKey;
|
||||
}
|
||||
|
||||
set contextKey(contextKey: string) {
|
||||
this._contextKey = contextKey;
|
||||
this.update();
|
||||
}
|
||||
|
||||
get busy() {
|
||||
return this._busy;
|
||||
}
|
||||
@@ -182,7 +199,7 @@ class QuickInput implements IQuickInput {
|
||||
if (this.visible) {
|
||||
return;
|
||||
}
|
||||
this.disposables.push(
|
||||
this.visibleDisposables.push(
|
||||
this.ui.onDidTriggerButton(button => {
|
||||
if (this.buttons.indexOf(button) !== -1) {
|
||||
this.onDidTriggerButtonEmitter.fire(button);
|
||||
@@ -235,20 +252,21 @@ class QuickInput implements IQuickInput {
|
||||
this.ui.leftActionBar.clear();
|
||||
const leftButtons = this.buttons.filter(button => button === backButton);
|
||||
this.ui.leftActionBar.push(leftButtons.map((button, index) => {
|
||||
const action = new Action(`id-${index}`, '', getIconClass(button.iconPath), true, () => this.onDidTriggerButtonEmitter.fire(button));
|
||||
const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => this.onDidTriggerButtonEmitter.fire(button));
|
||||
action.tooltip = button.tooltip;
|
||||
return action;
|
||||
}), { icon: true, label: false });
|
||||
this.ui.rightActionBar.clear();
|
||||
const rightButtons = this.buttons.filter(button => button !== backButton);
|
||||
this.ui.rightActionBar.push(rightButtons.map((button, index) => {
|
||||
const action = new Action(`id-${index}`, '', getIconClass(button.iconPath), true, () => this.onDidTriggerButtonEmitter.fire(button));
|
||||
const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => this.onDidTriggerButtonEmitter.fire(button));
|
||||
action.tooltip = button.tooltip;
|
||||
return action;
|
||||
}), { icon: true, label: false });
|
||||
}
|
||||
this.ui.ignoreFocusOut = this.ignoreFocusOut;
|
||||
this.ui.setEnabled(this.enabled);
|
||||
this.ui.setContextKey(this.contextKey);
|
||||
}
|
||||
|
||||
private getTitle() {
|
||||
@@ -282,11 +300,13 @@ class QuickInput implements IQuickInput {
|
||||
|
||||
class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPick<T> {
|
||||
|
||||
private static INPUT_BOX_ARIA_LABEL = localize('quickInputBox.ariaLabel', "Type to narrow down results.");
|
||||
|
||||
private _value = '';
|
||||
private _placeholder;
|
||||
private onDidChangeValueEmitter = new Emitter<string>();
|
||||
private onDidAcceptEmitter = new Emitter<string>();
|
||||
private _items: T[] = [];
|
||||
private _items: (T | IQuickPickSeparator)[] = [];
|
||||
private itemsUpdated = false;
|
||||
private _canSelectMany = false;
|
||||
private _matchOnDescription = false;
|
||||
@@ -299,7 +319,9 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
private selectedItemsUpdated = false;
|
||||
private selectedItemsToConfirm: T[] = [];
|
||||
private onDidChangeSelectionEmitter = new Emitter<T[]>();
|
||||
private quickNavigate = false;
|
||||
private onDidTriggerItemButtonEmitter = new Emitter<IQuickPickItemButtonEvent<T>>();
|
||||
|
||||
quickNavigate: IQuickNavigateConfiguration;
|
||||
|
||||
constructor(ui: QuickInputUI) {
|
||||
super(ui);
|
||||
@@ -308,6 +330,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
this.onDidAcceptEmitter,
|
||||
this.onDidChangeActiveEmitter,
|
||||
this.onDidChangeSelectionEmitter,
|
||||
this.onDidTriggerItemButtonEmitter,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -337,7 +360,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
return this._items;
|
||||
}
|
||||
|
||||
set items(items: T[]) {
|
||||
set items(items: (T | IQuickPickSeparator)[]) {
|
||||
this._items = items;
|
||||
this.itemsUpdated = true;
|
||||
this.update();
|
||||
@@ -392,8 +415,14 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
this.update();
|
||||
}
|
||||
|
||||
get keyMods() {
|
||||
return this.ui.keyMods;
|
||||
}
|
||||
|
||||
onDidChangeSelection = this.onDidChangeSelectionEmitter.event;
|
||||
|
||||
onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event;
|
||||
|
||||
show() {
|
||||
if (!this.visible) {
|
||||
this.visibleDisposables.push(
|
||||
@@ -403,7 +432,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
}
|
||||
this._value = value;
|
||||
this.ui.list.filter(this.ui.inputBox.value);
|
||||
if (!this.canSelectMany) {
|
||||
if (!this.ui.isScreenReaderOptimized() && !this.canSelectMany) {
|
||||
this.ui.list.focus('First');
|
||||
}
|
||||
this.onDidChangeValueEmitter.fire(value);
|
||||
@@ -467,6 +496,9 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
}),
|
||||
this.ui.list.onDidChangeSelection(selectedItems => {
|
||||
if (this.canSelectMany) {
|
||||
if (selectedItems.length) {
|
||||
this.ui.list.setSelectedElements([]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.selectedItemsToConfirm !== this._selectedItems && equals(selectedItems, this._selectedItems, (a, b) => a === b)) {
|
||||
@@ -474,7 +506,9 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
}
|
||||
this._selectedItems = selectedItems as T[];
|
||||
this.onDidChangeSelectionEmitter.fire(selectedItems as T[]);
|
||||
this.onDidAcceptEmitter.fire();
|
||||
if (selectedItems.length) {
|
||||
this.onDidAcceptEmitter.fire();
|
||||
}
|
||||
}),
|
||||
this.ui.list.onChangedCheckedElements(checkedItems => {
|
||||
if (!this.canSelectMany) {
|
||||
@@ -486,77 +520,24 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
this._selectedItems = checkedItems as T[];
|
||||
this.onDidChangeSelectionEmitter.fire(checkedItems as T[]);
|
||||
}),
|
||||
this.ui.list.onButtonTriggered(event => this.onDidTriggerItemButtonEmitter.fire(event as IQuickPickItemButtonEvent<T>)),
|
||||
this.registerQuickNavigation()
|
||||
);
|
||||
}
|
||||
super.show();
|
||||
super.show(); // TODO: Why have show() bubble up while update() trickles down? (Could move setComboboxAccessibility() here.)
|
||||
}
|
||||
|
||||
protected update() {
|
||||
super.update();
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
if (this.ui.inputBox.value !== this.value) {
|
||||
this.ui.inputBox.value = this.value;
|
||||
}
|
||||
if (this.ui.inputBox.placeholder !== (this.placeholder || '')) {
|
||||
this.ui.inputBox.placeholder = (this.placeholder || '');
|
||||
}
|
||||
if (this.itemsUpdated) {
|
||||
this.itemsUpdated = false;
|
||||
this.ui.list.setElements(this.items);
|
||||
this.ui.list.filter(this.ui.inputBox.value);
|
||||
this.ui.checkAll.checked = this.ui.list.getAllVisibleChecked();
|
||||
this.ui.visibleCount.setCount(this.ui.list.getVisibleCount());
|
||||
this.ui.count.setCount(this.ui.list.getCheckedCount());
|
||||
if (!this.canSelectMany) {
|
||||
this.ui.list.focus('First');
|
||||
private registerQuickNavigation() {
|
||||
return dom.addDisposableListener(this.ui.container, dom.EventType.KEY_UP, e => {
|
||||
if (this.canSelectMany || !this.quickNavigate) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.ui.container.classList.contains('show-checkboxes') !== this.canSelectMany) {
|
||||
if (this.canSelectMany) {
|
||||
this.ui.list.clearFocus();
|
||||
} else {
|
||||
this.ui.list.focus('First');
|
||||
}
|
||||
}
|
||||
if (this.activeItemsUpdated) {
|
||||
this.activeItemsUpdated = false;
|
||||
this.activeItemsToConfirm = this._activeItems;
|
||||
this.ui.list.setFocusedElements(this.activeItems);
|
||||
if (this.activeItemsToConfirm === this._activeItems) {
|
||||
this.activeItemsToConfirm = null;
|
||||
}
|
||||
}
|
||||
if (this.selectedItemsUpdated) {
|
||||
this.selectedItemsUpdated = false;
|
||||
this.selectedItemsToConfirm = this._selectedItems;
|
||||
if (this.canSelectMany) {
|
||||
this.ui.list.setCheckedElements(this.selectedItems);
|
||||
} else {
|
||||
this.ui.list.setSelectedElements(this.selectedItems);
|
||||
}
|
||||
if (this.selectedItemsToConfirm === this._selectedItems) {
|
||||
this.selectedItemsToConfirm = null;
|
||||
}
|
||||
}
|
||||
this.ui.list.matchOnDescription = this.matchOnDescription;
|
||||
this.ui.list.matchOnDetail = this.matchOnDetail;
|
||||
this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, checkAll: true, inputBox: true, visibleCount: true, count: true, ok: true, list: true } : { title: !!this.title || !!this.step, inputBox: true, visibleCount: true, list: true });
|
||||
}
|
||||
|
||||
configureQuickNavigate(quickNavigate: IQuickNavigateConfiguration) {
|
||||
if (this.canSelectMany || this.quickNavigate) {
|
||||
return;
|
||||
}
|
||||
this.quickNavigate = true;
|
||||
|
||||
this.disposables.push(dom.addDisposableListener(this.ui.container, dom.EventType.KEY_UP, (e: KeyboardEvent) => {
|
||||
const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e as KeyboardEvent);
|
||||
const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e);
|
||||
const keyCode = keyboardEvent.keyCode;
|
||||
|
||||
// Select element when keys are pressed that signal it
|
||||
const quickNavKeys = quickNavigate.keybindings;
|
||||
const quickNavKeys = this.quickNavigate.keybindings;
|
||||
const wasTriggerKeyPressed = keyCode === KeyCode.Enter || quickNavKeys.some(k => {
|
||||
const [firstPart, chordPart] = k.getParts();
|
||||
if (chordPart) {
|
||||
@@ -591,7 +572,63 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
this.onDidChangeSelectionEmitter.fire(this.selectedItems);
|
||||
this.onDidAcceptEmitter.fire();
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
protected update() {
|
||||
super.update();
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
if (this.ui.inputBox.value !== this.value) {
|
||||
this.ui.inputBox.value = this.value;
|
||||
}
|
||||
if (this.ui.inputBox.placeholder !== (this.placeholder || '')) {
|
||||
this.ui.inputBox.placeholder = (this.placeholder || '');
|
||||
}
|
||||
if (this.itemsUpdated) {
|
||||
this.itemsUpdated = false;
|
||||
this.ui.list.setElements(this.items);
|
||||
this.ui.list.filter(this.ui.inputBox.value);
|
||||
this.ui.checkAll.checked = this.ui.list.getAllVisibleChecked();
|
||||
this.ui.visibleCount.setCount(this.ui.list.getVisibleCount());
|
||||
this.ui.count.setCount(this.ui.list.getCheckedCount());
|
||||
if (!this.ui.isScreenReaderOptimized() && !this.canSelectMany) {
|
||||
this.ui.list.focus('First');
|
||||
}
|
||||
}
|
||||
if (this.ui.container.classList.contains('show-checkboxes') !== !!this.canSelectMany) {
|
||||
if (this.canSelectMany) {
|
||||
this.ui.list.clearFocus();
|
||||
} else if (!this.ui.isScreenReaderOptimized()) {
|
||||
this.ui.list.focus('First');
|
||||
}
|
||||
}
|
||||
if (this.activeItemsUpdated) {
|
||||
this.activeItemsUpdated = false;
|
||||
this.activeItemsToConfirm = this._activeItems;
|
||||
this.ui.list.setFocusedElements(this.activeItems);
|
||||
if (this.activeItemsToConfirm === this._activeItems) {
|
||||
this.activeItemsToConfirm = null;
|
||||
}
|
||||
}
|
||||
if (this.selectedItemsUpdated) {
|
||||
this.selectedItemsUpdated = false;
|
||||
this.selectedItemsToConfirm = this._selectedItems;
|
||||
if (this.canSelectMany) {
|
||||
this.ui.list.setCheckedElements(this.selectedItems);
|
||||
} else {
|
||||
this.ui.list.setSelectedElements(this.selectedItems);
|
||||
}
|
||||
if (this.selectedItemsToConfirm === this._selectedItems) {
|
||||
this.selectedItemsToConfirm = null;
|
||||
}
|
||||
}
|
||||
this.ui.list.matchOnDescription = this.matchOnDescription;
|
||||
this.ui.list.matchOnDetail = this.matchOnDetail;
|
||||
this.ui.setComboboxAccessibility(true);
|
||||
this.ui.inputBox.setAttribute('aria-label', QuickPick.INPUT_BOX_ARIA_LABEL);
|
||||
this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, checkAll: true, inputBox: true, visibleCount: true, count: true, ok: true, list: true } : { title: !!this.title || !!this.step, inputBox: true, visibleCount: true, list: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,6 +725,7 @@ class InputBox extends QuickInput implements IInputBox {
|
||||
}),
|
||||
this.ui.onDidAccept(() => this.onDidAcceptEmitter.fire()),
|
||||
);
|
||||
this.valueSelectionUpdated = true;
|
||||
}
|
||||
super.show();
|
||||
}
|
||||
@@ -729,6 +767,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
private static readonly ID = 'workbench.component.quickinput';
|
||||
private static readonly MAX_WIDTH = 600; // Max total width of quick open widget
|
||||
|
||||
private idPrefix = 'quickInput_'; // Constant since there is still only one.
|
||||
private layoutDimensions: dom.Dimension;
|
||||
private titleBar: HTMLElement;
|
||||
private filterContainer: HTMLElement;
|
||||
@@ -737,11 +776,14 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
private okContainer: HTMLElement;
|
||||
private ok: Button;
|
||||
private ui: QuickInputUI;
|
||||
private comboboxAccessibility = false;
|
||||
private enabled = true;
|
||||
private inQuickOpenWidgets: Record<string, boolean> = {};
|
||||
private inQuickOpenContext: IContextKey<boolean>;
|
||||
private contexts: { [id: string]: IContextKey<boolean>; } = Object.create(null);
|
||||
private onDidAcceptEmitter = this._register(new Emitter<void>());
|
||||
private onDidTriggerButtonEmitter = this._register(new Emitter<IQuickInputButton>());
|
||||
private keyMods: Writeable<IKeyMods> = { ctrlCmd: false, alt: false };
|
||||
|
||||
private controller: QuickInput;
|
||||
|
||||
@@ -753,13 +795,15 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
@IQuickOpenService private quickOpenService: IQuickOpenService,
|
||||
@IEditorGroupsService private editorGroupService: IEditorGroupsService,
|
||||
@IKeybindingService private keybindingService: IKeybindingService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(QuickInputService.ID, themeService);
|
||||
super(QuickInputService.ID, themeService, storageService);
|
||||
this.inQuickOpenContext = new RawContextKey<boolean>('inQuickOpen', false).bindTo(contextKeyService);
|
||||
this._register(this.quickOpenService.onShow(() => this.inQuickOpen('quickOpen', true)));
|
||||
this._register(this.quickOpenService.onHide(() => this.inQuickOpen('quickOpen', false)));
|
||||
this.registerKeyModsListeners();
|
||||
}
|
||||
|
||||
private inQuickOpen(widget: 'quickInput' | 'quickOpen', open: boolean) {
|
||||
@@ -779,13 +823,71 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
}
|
||||
}
|
||||
|
||||
private setContextKey(id?: string) {
|
||||
let key: IContextKey<boolean>;
|
||||
if (id) {
|
||||
key = this.contexts[id];
|
||||
if (!key) {
|
||||
key = new RawContextKey<boolean>(id, false)
|
||||
.bindTo(this.contextKeyService);
|
||||
this.contexts[id] = key;
|
||||
}
|
||||
}
|
||||
|
||||
if (key && key.get()) {
|
||||
return; // already active context
|
||||
}
|
||||
|
||||
this.resetContextKeys();
|
||||
|
||||
if (key) {
|
||||
key.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
private resetContextKeys() {
|
||||
for (const key in this.contexts) {
|
||||
if (this.contexts[key].get()) {
|
||||
this.contexts[key].reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private registerKeyModsListeners() {
|
||||
const workbench = this.partService.getWorkbenchElement();
|
||||
this._register(dom.addDisposableListener(workbench, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
switch (event.keyCode) {
|
||||
case KeyCode.Ctrl:
|
||||
case KeyCode.Meta:
|
||||
this.keyMods.ctrlCmd = true;
|
||||
break;
|
||||
case KeyCode.Alt:
|
||||
this.keyMods.alt = true;
|
||||
break;
|
||||
}
|
||||
}));
|
||||
this._register(dom.addDisposableListener(workbench, dom.EventType.KEY_UP, (e: KeyboardEvent) => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
switch (event.keyCode) {
|
||||
case KeyCode.Ctrl:
|
||||
case KeyCode.Meta:
|
||||
this.keyMods.ctrlCmd = false;
|
||||
break;
|
||||
case KeyCode.Alt:
|
||||
this.keyMods.alt = false;
|
||||
break;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private create() {
|
||||
if (this.ui) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workbench = document.getElementById(this.partService.getWorkbenchElementId());
|
||||
const container = dom.append(workbench, $('.quick-input-widget'));
|
||||
const workbench = this.partService.getWorkbenchElement();
|
||||
const container = dom.append(workbench, $('.quick-input-widget.show-file-icons'));
|
||||
container.tabIndex = -1;
|
||||
container.style.display = 'none';
|
||||
|
||||
@@ -816,9 +918,11 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
this.filterContainer = dom.append(headerContainer, $('.quick-input-filter'));
|
||||
|
||||
const inputBox = this._register(new QuickInputBox(this.filterContainer));
|
||||
inputBox.setAttribute('aria-describedby', `${this.idPrefix}message`);
|
||||
|
||||
this.visibleCountContainer = dom.append(this.filterContainer, $('.quick-input-visible-count'));
|
||||
this.visibleCountContainer.setAttribute('aria-live', 'polite');
|
||||
this.visibleCountContainer.setAttribute('aria-atomic', 'true');
|
||||
const visibleCount = new CountBadge(this.visibleCountContainer, { countFormat: localize({ key: 'quickInput.visibleCount', comment: ['This tells the user how many items are shown in a list of items to select from. The items can be anything. Currently not visible, but read by screen readers.'] }, "{0} Results") });
|
||||
|
||||
this.countContainer = dom.append(this.filterContainer, $('.quick-input-count'));
|
||||
@@ -834,13 +938,13 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
this.onDidAcceptEmitter.fire();
|
||||
}));
|
||||
|
||||
const message = dom.append(container, $('.quick-input-message'));
|
||||
const message = dom.append(container, $(`#${this.idPrefix}message.quick-input-message`));
|
||||
|
||||
const progressBar = new ProgressBar(container);
|
||||
dom.addClass(progressBar.getContainer(), 'quick-input-progress');
|
||||
this._register(attachProgressBarStyler(progressBar, this.themeService));
|
||||
|
||||
const list = this._register(this.instantiationService.createInstance(QuickInputList, container));
|
||||
const list = this._register(this.instantiationService.createInstance(QuickInputList, container, this.idPrefix + 'list'));
|
||||
this._register(list.onChangedAllVisibleChecked(checked => {
|
||||
checkAll.checked = checked;
|
||||
}));
|
||||
@@ -859,6 +963,11 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
}
|
||||
}, 0);
|
||||
}));
|
||||
this._register(list.onDidChangeFocus(() => {
|
||||
if (this.comboboxAccessibility) {
|
||||
this.ui.inputBox.setAttribute('aria-activedescendant', this.ui.list.getActiveDescendant());
|
||||
}
|
||||
}));
|
||||
|
||||
const focusTracker = dom.trackFocus(container);
|
||||
this._register(focusTracker);
|
||||
@@ -919,21 +1028,33 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
onDidAccept: this.onDidAcceptEmitter.event,
|
||||
onDidTriggerButton: this.onDidTriggerButtonEmitter.event,
|
||||
ignoreFocusOut: false,
|
||||
keyMods: this.keyMods,
|
||||
isScreenReaderOptimized: () => this.isScreenReaderOptimized(),
|
||||
show: controller => this.show(controller),
|
||||
hide: () => this.hide(),
|
||||
setVisibilities: visibilities => this.setVisibilities(visibilities),
|
||||
setComboboxAccessibility: enabled => this.setComboboxAccessibility(enabled),
|
||||
setEnabled: enabled => this.setEnabled(enabled),
|
||||
setContextKey: contextKey => this.setContextKey(contextKey),
|
||||
};
|
||||
this.updateStyles();
|
||||
}
|
||||
|
||||
pick<T extends IQuickPickItem, O extends IPickOptions<T>>(picks: TPromise<T[]>, options: O = <O>{}, token: CancellationToken = CancellationToken.None): TPromise<O extends { canPickMany: true } ? T[] : T> {
|
||||
return new TPromise<O extends { canPickMany: true } ? T[] : T>((resolve, reject) => {
|
||||
pick<T extends IQuickPickItem, O extends IPickOptions<T>>(picks: Promise<QuickPickInput<T>[]> | QuickPickInput<T>[], options: O = <O>{}, token: CancellationToken = CancellationToken.None): Promise<O extends { canPickMany: true } ? T[] : T> {
|
||||
return new Promise<O extends { canPickMany: true } ? T[] : T>((doResolve, reject) => {
|
||||
let resolve = (result: any) => {
|
||||
resolve = doResolve;
|
||||
if (options.onKeyMods) {
|
||||
options.onKeyMods(input.keyMods);
|
||||
}
|
||||
doResolve(result);
|
||||
};
|
||||
if (token.isCancellationRequested) {
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
const input = this.createQuickPick<T>();
|
||||
let activeItem: T;
|
||||
const disposables = [
|
||||
input,
|
||||
input.onDidAccept(() => {
|
||||
@@ -963,6 +1084,22 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
}
|
||||
}
|
||||
}),
|
||||
input.onDidTriggerItemButton(event => options.onDidTriggerItemButton && options.onDidTriggerItemButton({
|
||||
...event,
|
||||
removeItem: () => {
|
||||
const index = input.items.indexOf(event.item);
|
||||
if (index !== -1) {
|
||||
const items = input.items.slice();
|
||||
items.splice(index, 1);
|
||||
input.items = items;
|
||||
}
|
||||
}
|
||||
})),
|
||||
input.onDidChangeValue(value => {
|
||||
if (activeItem && !value && (input.activeItems.length !== 1 || input.activeItems[0] !== activeItem)) {
|
||||
input.activeItems = [activeItem];
|
||||
}
|
||||
}),
|
||||
token.onCancellationRequested(() => {
|
||||
input.hide();
|
||||
}),
|
||||
@@ -976,38 +1113,45 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
input.ignoreFocusOut = options.ignoreFocusLost;
|
||||
input.matchOnDescription = options.matchOnDescription;
|
||||
input.matchOnDetail = options.matchOnDetail;
|
||||
input.quickNavigate = options.quickNavigate;
|
||||
input.contextKey = options.contextKey;
|
||||
input.busy = true;
|
||||
picks.then(items => {
|
||||
input.busy = false;
|
||||
input.items = items;
|
||||
if (input.canSelectMany) {
|
||||
input.selectedItems = items.filter(item => item.picked);
|
||||
}
|
||||
});
|
||||
Promise.all([picks, options.activeItem])
|
||||
.then(([items, _activeItem]) => {
|
||||
activeItem = _activeItem;
|
||||
input.busy = false;
|
||||
input.items = items;
|
||||
if (input.canSelectMany) {
|
||||
input.selectedItems = items.filter(item => item.type !== 'separator' && item.picked) as T[];
|
||||
}
|
||||
if (activeItem) {
|
||||
input.activeItems = [activeItem];
|
||||
}
|
||||
});
|
||||
input.show();
|
||||
picks.then(null, err => {
|
||||
Promise.resolve(picks).then(null, err => {
|
||||
reject(err);
|
||||
input.hide();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): TPromise<string> {
|
||||
return new TPromise<string>((resolve, reject) => {
|
||||
input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
if (token.isCancellationRequested) {
|
||||
resolve(undefined);
|
||||
return;
|
||||
}
|
||||
const input = this.createInputBox();
|
||||
const validateInput = options.validateInput || (() => TPromise.as(undefined));
|
||||
const validateInput = options.validateInput || (() => <Thenable<undefined>>Promise.resolve(undefined));
|
||||
const onDidValueChange = debounceEvent(input.onDidChangeValue, (last, cur) => cur, 100);
|
||||
let validationValue = options.value || '';
|
||||
let validation = TPromise.wrap(validateInput(validationValue));
|
||||
let validation = Promise.resolve(validateInput(validationValue));
|
||||
const disposables = [
|
||||
input,
|
||||
onDidValueChange(value => {
|
||||
if (value !== validationValue) {
|
||||
validation = TPromise.wrap(validateInput(value));
|
||||
validation = Promise.resolve(validateInput(value));
|
||||
validationValue = value;
|
||||
}
|
||||
validation.then(result => {
|
||||
@@ -1019,7 +1163,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
input.onDidAccept(() => {
|
||||
const value = input.value;
|
||||
if (value !== validationValue) {
|
||||
validation = TPromise.wrap(validateInput(value));
|
||||
validation = Promise.resolve(validateInput(value));
|
||||
validationValue = value;
|
||||
}
|
||||
validation.then(result => {
|
||||
@@ -1087,11 +1231,14 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
this.ui.list.matchOnDescription = false;
|
||||
this.ui.list.matchOnDetail = false;
|
||||
this.ui.ignoreFocusOut = false;
|
||||
this.setComboboxAccessibility(false);
|
||||
this.ui.inputBox.removeAttribute('aria-label');
|
||||
|
||||
const keybinding = this.keybindingService.lookupKeybinding(BackAction.ID);
|
||||
backButton.tooltip = keybinding ? localize('quickInput.backWithKeybinding', "Back ({0})", keybinding.getLabel()) : localize('quickInput.back', "Back");
|
||||
|
||||
this.inQuickOpen('quickInput', true);
|
||||
this.resetContextKeys();
|
||||
|
||||
this.ui.container.style.display = '';
|
||||
this.updateLayout();
|
||||
@@ -1111,6 +1258,29 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
this.updateLayout(); // TODO
|
||||
}
|
||||
|
||||
private setComboboxAccessibility(enabled: boolean) {
|
||||
if (enabled !== this.comboboxAccessibility) {
|
||||
this.comboboxAccessibility = enabled;
|
||||
if (this.comboboxAccessibility) {
|
||||
this.ui.inputBox.setAttribute('role', 'combobox');
|
||||
this.ui.inputBox.setAttribute('aria-haspopup', 'true');
|
||||
this.ui.inputBox.setAttribute('aria-autocomplete', 'list');
|
||||
this.ui.inputBox.setAttribute('aria-activedescendant', this.ui.list.getActiveDescendant());
|
||||
} else {
|
||||
this.ui.inputBox.removeAttribute('role');
|
||||
this.ui.inputBox.removeAttribute('aria-haspopup');
|
||||
this.ui.inputBox.removeAttribute('aria-autocomplete');
|
||||
this.ui.inputBox.removeAttribute('aria-activedescendant');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private isScreenReaderOptimized() {
|
||||
const detected = browser.getAccessibilitySupport() === AccessibilitySupport.Enabled;
|
||||
const config = this.configurationService.getValue<IEditorOptions>('editor').accessibilitySupport;
|
||||
return config === 'on' || (config === 'auto' && detected);
|
||||
}
|
||||
|
||||
private setEnabled(enabled: boolean) {
|
||||
if (enabled !== this.enabled) {
|
||||
this.enabled = enabled;
|
||||
@@ -1132,6 +1302,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
if (controller) {
|
||||
this.controller = null;
|
||||
this.inQuickOpen('quickInput', false);
|
||||
this.resetContextKeys();
|
||||
this.ui.container.style.display = 'none';
|
||||
if (!focusLost) {
|
||||
this.editorGroupService.activeGroup.focus();
|
||||
@@ -1156,24 +1327,24 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
if (this.isDisplayed() && this.ui.list.isDisplayed()) {
|
||||
this.ui.list.focus(next ? 'Next' : 'Previous');
|
||||
if (quickNavigate && this.controller instanceof QuickPick) {
|
||||
this.controller.configureQuickNavigate(quickNavigate);
|
||||
this.controller.quickNavigate = quickNavigate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
accept() {
|
||||
this.onDidAcceptEmitter.fire();
|
||||
return TPromise.as(undefined);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
back() {
|
||||
this.onDidTriggerButtonEmitter.fire(this.backButton);
|
||||
return TPromise.as(undefined);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.hide();
|
||||
return TPromise.as(undefined);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
layout(dimension: dom.Dimension): void {
|
||||
@@ -1201,16 +1372,16 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
if (this.ui) {
|
||||
// TODO
|
||||
const titleColor = { dark: 'rgba(255, 255, 255, 0.105)', light: 'rgba(0,0,0,.06)', hc: 'black' }[theme.type];
|
||||
this.titleBar.style.backgroundColor = titleColor ? titleColor.toString() : undefined;
|
||||
this.titleBar.style.backgroundColor = titleColor ? titleColor.toString() : null;
|
||||
this.ui.inputBox.style(theme);
|
||||
const sideBarBackground = theme.getColor(SIDE_BAR_BACKGROUND);
|
||||
this.ui.container.style.backgroundColor = sideBarBackground ? sideBarBackground.toString() : undefined;
|
||||
this.ui.container.style.backgroundColor = sideBarBackground ? sideBarBackground.toString() : null;
|
||||
const sideBarForeground = theme.getColor(SIDE_BAR_FOREGROUND);
|
||||
this.ui.container.style.color = sideBarForeground ? sideBarForeground.toString() : undefined;
|
||||
this.ui.container.style.color = sideBarForeground ? sideBarForeground.toString() : null;
|
||||
const contrastBorderColor = theme.getColor(contrastBorder);
|
||||
this.ui.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : undefined;
|
||||
this.ui.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : null;
|
||||
const widgetShadowColor = theme.getColor(widgetShadow);
|
||||
this.ui.container.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : undefined;
|
||||
this.ui.container.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1219,25 +1390,6 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
}
|
||||
}
|
||||
|
||||
const iconPathToClass = {};
|
||||
const iconClassGenerator = new IdGenerator('quick-input-button-icon-');
|
||||
|
||||
function getIconClass(iconPath: { dark: URI; light?: URI; }) {
|
||||
let iconClass: string;
|
||||
|
||||
const key = iconPath.dark.toString();
|
||||
if (iconPathToClass[key]) {
|
||||
iconClass = iconPathToClass[key];
|
||||
} else {
|
||||
iconClass = iconClassGenerator.nextId();
|
||||
dom.createCSSRule(`.${iconClass}`, `background-image: url("${(iconPath.light || iconPath.dark).toString()}")`);
|
||||
dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: url("${iconPath.dark.toString()}")`);
|
||||
iconPathToClass[key] = iconClass;
|
||||
}
|
||||
|
||||
return iconClass;
|
||||
}
|
||||
|
||||
export const QuickPickManyToggle: ICommandAndKeybindingRule = {
|
||||
id: 'workbench.action.quickPickManyToggle',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
@@ -1258,8 +1410,8 @@ export class BackAction extends Action {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
public run(): Promise<any> {
|
||||
this.quickInputService.back();
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user