Skip to content

Commit

Permalink
Multi-select list (#45589)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrmarti committed Mar 26, 2018
1 parent 4ba0ecc commit ea1e57b
Show file tree
Hide file tree
Showing 11 changed files with 331 additions and 14 deletions.
3 changes: 3 additions & 0 deletions src/vs/platform/quickOpen/common/quickOpen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface IPickOpenEntry {
run?: (context: IEntryRunContext) => void;
action?: IAction;
payload?: any;
selected?: boolean;
}

export interface IPickOpenItem {
Expand Down Expand Up @@ -84,6 +85,8 @@ export interface IPickOptions {
* a context key to set when this picker is active
*/
contextKey?: string;

multiSelect?: boolean;
}

export interface IInputOptions {
Expand Down
19 changes: 19 additions & 0 deletions src/vs/platform/quickinput/common/quickInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';

import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { TPromise } from 'vs/base/common/winjs.base';
import { IPickOptions, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
import { CancellationToken } from 'vs/base/common/cancellation';

export const IQuickInputService = createDecorator<IQuickInputService>('quickInputService');

export interface IQuickInputService {

_serviceBrand: any;

pick<T extends IPickOpenEntry>(picks: TPromise<T[]>, options?: IPickOptions, token?: CancellationToken): TPromise<T[]>;
}
12 changes: 12 additions & 0 deletions src/vs/vscode.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1554,12 +1554,22 @@ declare module 'vscode' {
*/
ignoreFocusOut?: boolean;

multiSelect?: boolean;

/**
* An optional function that is invoked whenever an item is selected.
*/
onDidSelectItem?(item: QuickPickItem | string): any;
}

export interface MultiSelectQuickPickItem extends QuickPickItem {
selected?: boolean;
}

export interface MultiSelectQuickPickOptions extends QuickPickOptions {
multiSelect: true;
}

/**
* Options to configure the behaviour of the [workspace folder](#WorkspaceFolder) pick UI.
*/
Expand Down Expand Up @@ -5069,6 +5079,7 @@ declare module 'vscode' {
* @param token A token that can be used to signal cancellation.
* @return A promise that resolves to the selection or `undefined`.
*/
export function showQuickPick(items: string[] | Thenable<string[]>, options: MultiSelectQuickPickOptions, token?: CancellationToken): Thenable<string[] | undefined>;
export function showQuickPick(items: string[] | Thenable<string[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<string | undefined>;

/**
Expand All @@ -5079,6 +5090,7 @@ declare module 'vscode' {
* @param token A token that can be used to signal cancellation.
* @return A promise that resolves to the selected item or `undefined`.
*/
export function showQuickPick<T extends MultiSelectQuickPickItem>(items: T[] | Thenable<T[]>, options: MultiSelectQuickPickOptions, token?: CancellationToken): Thenable<T[] | undefined>;
export function showQuickPick<T extends QuickPickItem>(items: T[] | Thenable<T[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<T | undefined>;

/**
Expand Down
29 changes: 22 additions & 7 deletions src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { asWinJsPromise } from 'vs/base/common/async';
import { IQuickOpenService, IPickOptions, IInputOptions } from 'vs/platform/quickOpen/common/quickOpen';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { InputBoxOptions } from 'vscode';
import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
Expand All @@ -23,7 +24,8 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {

constructor(
extHostContext: IExtHostContext,
@IQuickOpenService quickOpenService: IQuickOpenService
@IQuickOpenService quickOpenService: IQuickOpenService,
@IQuickInputService private _quickInputService: IQuickInputService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostQuickOpen);
this._quickOpenService = quickOpenService;
Expand All @@ -32,7 +34,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
public dispose(): void {
}

$show(options: IPickOptions): TPromise<number> {
$show(options: IPickOptions): TPromise<number | number[]> {

const myToken = ++this._token;

Expand All @@ -50,12 +52,25 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
};
});

return asWinJsPromise(token => this._quickOpenService.pick(this._contents, options, token)).then(item => {
if (item) {
return item.handle;
return asWinJsPromise<number | number[]>(token => {
if (options.multiSelect) {
return this._quickInputService.pick(this._contents, options, token)
.then(items => {
if (items) {
return items.map(item => item.handle);
}
return undefined;
});
} else {
return this._quickOpenService.pick(this._contents, options, token)
.then(item => {
if (item) {
return item.handle;
}
return undefined;
});
}
return undefined;
}, undefined, progress => {
}).then(undefined, undefined, progress => {
if (progress) {
this._proxy.$onItemSelected((<MyQuickPickItems>progress).handle);
}
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/node/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ export function createApiFactory(
showErrorMessage(message, first, ...rest) {
return extHostMessageService.showMessage(extension, Severity.Error, message, first, rest);
},
showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken) {
showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken): any {
return extHostQuickOpen.showQuickPick(items, options, token);
},
showWorkspaceFolderPick(options: vscode.WorkspaceFolderPickOptions) {
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/node/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ export interface MyQuickPickItems extends IPickOpenEntry {
handle: number;
}
export interface MainThreadQuickOpenShape extends IDisposable {
$show(options: IPickOptions): TPromise<number>;
$show(options: IPickOptions): TPromise<number | number[]>;
$setItems(items: MyQuickPickItems[]): TPromise<any>;
$setError(error: Error): TPromise<any>;
$input(options: vscode.InputBoxOptions, validateInput: boolean): TPromise<string>;
Expand Down
17 changes: 12 additions & 5 deletions src/vs/workbench/api/node/extHostQuickOpen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { wireCancellationToken, asWinJsPromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { QuickPickOptions, QuickPickItem, InputBoxOptions, WorkspaceFolderPickOptions, WorkspaceFolder } from 'vscode';
import { QuickPickOptions, QuickPickItem, InputBoxOptions, WorkspaceFolderPickOptions, WorkspaceFolder, MultiSelectQuickPickItem, MultiSelectQuickPickOptions } from 'vscode';
import { MainContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, IMainContext } from './extHost.protocol';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
Expand All @@ -29,9 +29,10 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
this._commands = commands;
}

showQuickPick(itemsOrItemsPromise: MultiSelectQuickPickItem[] | Thenable<MultiSelectQuickPickItem[]>, options: MultiSelectQuickPickOptions, token?: CancellationToken): Thenable<MultiSelectQuickPickItem[] | undefined>;
showQuickPick(itemsOrItemsPromise: string[] | Thenable<string[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<string | undefined>;
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<QuickPickItem | undefined>;
showQuickPick(itemsOrItemsPromise: Item[] | Thenable<Item[]>, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable<Item | undefined> {
showQuickPick(itemsOrItemsPromise: Item[] | Thenable<Item[]>, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable<Item | Item[] | undefined> {

// clear state from last invocation
this._onDidSelectItem = undefined;
Expand All @@ -43,7 +44,8 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
placeHolder: options && options.placeHolder,
matchOnDescription: options && options.matchOnDescription,
matchOnDetail: options && options.matchOnDetail,
ignoreFocusLost: options && options.ignoreFocusOut
ignoreFocusLost: options && options.ignoreFocusOut,
multiSelect: options && options.multiSelect
});

const promise = TPromise.any(<TPromise<number | Item[]>[]>[quickPickWidget, itemsPromise]).then(values => {
Expand All @@ -60,19 +62,22 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
let label: string;
let description: string;
let detail: string;
let selected: boolean;

if (typeof item === 'string') {
label = item;
} else {
label = item.label;
description = item.description;
detail = item.detail;
selected = options && options.multiSelect ? (<MultiSelectQuickPickItem>item).selected : undefined;
}
pickItems.push({
label,
description,
handle,
detail
detail,
selected
});
}

Expand All @@ -89,6 +94,8 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
return quickPickWidget.then(handle => {
if (typeof handle === 'number') {
return items[handle];
} else if (Array.isArray(handle)) {
return handle.map(h => items[h]);
}
return undefined;
});
Expand All @@ -98,7 +105,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
return TPromise.wrapError(err);
});
});
return wireCancellationToken<Item>(token, promise, true);
return wireCancellationToken<Item | Item[]>(token, promise, true);
}

$onItemSelected(handle: number): void {
Expand Down
7 changes: 7 additions & 0 deletions src/vs/workbench/browser/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
import { Part } from 'vs/workbench/browser/part';
import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController';
import { QuickInputService } from 'vs/workbench/browser/parts/quickinput/quickInput';
import { Sash, ISashEvent, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider, Orientation } from 'vs/base/browser/ui/sash/sash';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IPartService, Position, ILayoutOptions, Parts } from 'vs/workbench/services/part/common/partService';
Expand Down Expand Up @@ -67,6 +68,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
private panel: Part;
private statusbar: Part;
private quickopen: QuickOpenController;
private quickInput: QuickInputService;
private notificationsCenter: NotificationsCenter;
private notificationsToasts: NotificationsToasts;
private toUnbind: IDisposable[];
Expand Down Expand Up @@ -97,6 +99,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
statusbar: Part
},
quickopen: QuickOpenController,
quickInput: QuickInputService,
notificationsCenter: NotificationsCenter,
notificationsToasts: NotificationsToasts,
@IStorageService private storageService: IStorageService,
Expand All @@ -116,6 +119,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
this.panel = parts.panel;
this.statusbar = parts.statusbar;
this.quickopen = quickopen;
this.quickInput = quickInput;
this.notificationsCenter = notificationsCenter;
this.notificationsToasts = notificationsToasts;
this.toUnbind = [];
Expand Down Expand Up @@ -651,6 +655,9 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
// Quick open
this.quickopen.layout(this.workbenchSize);

// Quick input
this.quickInput.layout(this.workbenchSize);

// Notifications
this.notificationsCenter.layout(this.workbenchSize);
this.notificationsToasts.layout(this.workbenchSize);
Expand Down
28 changes: 28 additions & 0 deletions src/vs/workbench/browser/parts/quickinput/quickInput.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

.quick-input-widget {
position: absolute;
width: 600px;
z-index: 2000;
padding-bottom: 6px;
left: 50%;
margin-left: -300px;
}

.quick-input-actions {
padding: 3px;
}

.quick-input-actions button {
border: 0;
padding: 0px 4px 1px 4px;
float: right;
margin-left: 4px;
}

.quick-input-actions button:focus {
outline:0;
}
Loading

0 comments on commit ea1e57b

Please sign in to comment.