提交 f4b89e0b 编写于 作者: C Christof Marti

Action bar (#49340)

上级 0afdd855
......@@ -297,6 +297,15 @@ suite('window namespace tests', () => {
]);
});
test('showInputBox - value not empty on second try', async function () {
const one = window.showInputBox({ value: 'notempty' });
await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem');
assert.equal(await one, 'notempty');
const two = window.showInputBox({ value: 'notempty' });
await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem');
assert.equal(await two, 'notempty');
});
test('showQuickPick, accept first', async function () {
const pick = window.showQuickPick(['eins', 'zwei', 'drei']);
......
......@@ -9,7 +9,6 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import URI from 'vs/base/common/uri';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { Event } from 'vs/base/common/event';
export interface IQuickPickItem {
......@@ -117,7 +116,7 @@ export interface IQuickPick extends IQuickInput {
buttons: ReadonlyArray<IQuickInputButton>;
readonly onDidTriggerCommand: Event<IQuickInputButton>;
readonly onDidTriggerButton: Event<IQuickInputButton>;
items: ReadonlyArray<IQuickPickItem>;
......@@ -160,7 +159,7 @@ export interface IInputBox extends IQuickInput {
}
export interface IQuickInputButton {
iconPath: string | URI | { light: string | URI; dark: string | URI } | ThemeIcon;
iconPath: { dark: URI; light?: URI; };
tooltip?: string | undefined;
}
......
......@@ -8,17 +8,18 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { asWinJsPromise } from 'vs/base/common/async';
import { IPickOptions, IInputOptions, IQuickInputService, IQuickInput } from 'vs/platform/quickinput/common/quickInput';
import { InputBoxOptions } from 'vscode';
import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, MainContext, IExtHostContext, TransferQuickInput } from '../node/extHost.protocol';
import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, TransferQuickPickItems, MainContext, IExtHostContext, TransferQuickInput, TransferQuickInputButton } from 'vs/workbench/api/node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import URI from 'vs/base/common/uri';
@extHostNamedCustomer(MainContext.MainThreadQuickOpen)
export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
private _proxy: ExtHostQuickOpenShape;
private _quickInputService: IQuickInputService;
private _doSetItems: (items: MyQuickPickItems[]) => any;
private _doSetItems: (items: TransferQuickPickItems[]) => any;
private _doSetError: (error: Error) => any;
private _contents: TPromise<MyQuickPickItems[]>;
private _contents: TPromise<TransferQuickPickItems[]>;
private _token: number = 0;
constructor(
......@@ -35,7 +36,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
$show(options: IPickOptions): TPromise<number | number[]> {
const myToken = ++this._token;
this._contents = new TPromise<MyQuickPickItems[]>((c, e) => {
this._contents = new TPromise<TransferQuickPickItems[]>((c, e) => {
this._doSetItems = (items) => {
if (myToken === this._token) {
c(items);
......@@ -57,7 +58,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
return undefined;
}, undefined, progress => {
if (progress) {
this._proxy.$onItemSelected((<MyQuickPickItems>progress).handle);
this._proxy.$onItemSelected((<TransferQuickPickItems>progress).handle);
}
});
} else {
......@@ -68,13 +69,13 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
return undefined;
}, undefined, progress => {
if (progress) {
this._proxy.$onItemSelected((<MyQuickPickItems>progress).handle);
this._proxy.$onItemSelected((<TransferQuickPickItems>progress).handle);
}
});
}
}
$setItems(items: MyQuickPickItems[]): TPromise<any> {
$setItems(items: TransferQuickPickItems[]): TPromise<any> {
if (this._doSetItems) {
this._doSetItems(items);
}
......@@ -125,10 +126,13 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
this._proxy.$onDidAccept(sessionId);
});
input.onDidChangeActive(items => {
this._proxy.$onDidChangeActive(sessionId, items.map(item => (item as MyQuickPickItems).handle));
this._proxy.$onDidChangeActive(sessionId, items.map(item => (item as TransferQuickPickItems).handle));
});
input.onDidChangeSelection(items => {
this._proxy.$onDidChangeSelection(sessionId, items.map(item => (item as MyQuickPickItems).handle));
this._proxy.$onDidChangeSelection(sessionId, items.map(item => (item as TransferQuickPickItems).handle));
});
input.onDidTriggerButton(button => {
this._proxy.$onDidTriggerButton(sessionId, (button as TransferQuickInputButton).handle);
});
session = input;
} else {
......@@ -150,6 +154,15 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
} else {
session.hide();
}
} else if (param === 'buttons') {
params.buttons.forEach(button => {
const iconPath = button.iconPath;
iconPath.dark = URI.revive(iconPath.dark);
if (iconPath.light) {
iconPath.light = URI.revive(iconPath.light);
}
});
session[param] = params[param];
} else {
session[param] = params[param];
}
......
......@@ -26,7 +26,7 @@ import * as modes from 'vs/editor/common/modes';
import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { IConfig, IAdapterExecutable, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug';
import { IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput';
import { IQuickPickItem, IPickOptions, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { EndOfLine, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
......@@ -336,7 +336,11 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
$sendProcessExit(terminalId: number, exitCode: number): void;
}
export interface MyQuickPickItems extends IQuickPickItem {
export interface TransferQuickPickItems extends IQuickPickItem {
handle: number;
}
export interface TransferQuickInputButton extends IQuickInputButton {
handle: number;
}
......@@ -363,9 +367,9 @@ export interface TransferQuickPick extends BaseTransferQuickInput {
placeholder?: string;
commands?: TransferQuickInputCommand[];
buttons?: TransferQuickInputButton[];
items?: MyQuickPickItems[];
items?: TransferQuickPickItems[];
canSelectMany?: boolean;
......@@ -386,21 +390,16 @@ export interface TransferInputBox extends BaseTransferQuickInput {
password?: boolean;
commands?: TransferQuickInputCommand[];
buttons?: TransferQuickInputButton[];
prompt?: string;
validationMessage?: string;
}
export interface TransferQuickInputCommand {
iconPath: { light: string; dark: string; };
tooltip?: string | undefined;
}
export interface MainThreadQuickOpenShape extends IDisposable {
$show(options: IPickOptions): TPromise<number | number[]>;
$setItems(items: MyQuickPickItems[]): TPromise<any>;
$setItems(items: TransferQuickPickItems[]): TPromise<any>;
$setError(error: Error): TPromise<any>;
$input(options: vscode.InputBoxOptions, validateInput: boolean): TPromise<string>;
$createOrUpdate(params: TransferQuickInput): TPromise<void>;
......@@ -819,6 +818,7 @@ export interface ExtHostQuickOpenShape {
$onDidChangeActive(sessionId: number, handles: number[]): void;
$onDidChangeSelection(sessionId: number, handles: number[]): void;
$onDidAccept(sessionId: number): void;
$onDidTriggerButton(sessionId: number, handle: number): void;
}
export interface ShellLaunchConfigDto {
......
......@@ -4,16 +4,18 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { wireCancellationToken, asWinJsPromise } from 'vs/base/common/async';
import { asWinJsPromise, wireCancellationToken } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { QuickPickOptions, QuickPickItem, InputBoxOptions, WorkspaceFolderPickOptions, WorkspaceFolder, QuickInput, QuickPick, InputBox, QuickInputButton } from 'vscode';
import { MainContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, IMainContext, TransferQuickInput } from './extHost.protocol';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { Emitter } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { assign } from 'vs/base/common/objects';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { InputBox, InputBoxOptions, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode';
import { ExtHostQuickOpenShape, IMainContext, MainContext, MainThreadQuickOpenShape, TransferQuickPickItems, TransferQuickInput, TransferQuickInputButton } from './extHost.protocol';
import URI from 'vs/base/common/uri';
import { ThemeIcon } from 'vs/workbench/api/node/extHostTypes';
export type Item = string | QuickPickItem;
......@@ -59,7 +61,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
return itemsPromise.then(items => {
let pickItems: MyQuickPickItems[] = [];
let pickItems: TransferQuickPickItems[] = [];
for (let handle = 0; handle < items.length; handle++) {
let item = items[handle];
......@@ -182,6 +184,13 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
session._fireDidChangeSelection(handles);
}
}
$onDidTriggerButton(sessionId: number, handle: number): void {
const session = this._sessions.get(sessionId);
if (session) {
session._fireDidTriggerButton(handle);
}
}
}
class ExtHostQuickInput implements QuickInput {
......@@ -193,13 +202,17 @@ class ExtHostQuickInput implements QuickInput {
private _enabled = true;
private _busy = false;
private _ignoreFocusOut = true;
private _buttons: QuickInputButton[] = [];
private _handlesToButtons = new Map<number, QuickInputButton>();
private _onDidTriggerButtonEmitter = new Emitter<QuickInputButton>();
private _onDidHideEmitter = new Emitter<void>();
private _updateTimeout: number;
private _pendingUpdate: TransferQuickInput = { id: this._id };
private _disposed = false;
protected _disposables: IDisposable[] = [
this._onDidHideEmitter
this._onDidTriggerButtonEmitter,
this._onDidHideEmitter,
];
constructor(protected _proxy: MainThreadQuickOpenShape, protected _extensionId: string, private _onDidDispose: () => void) {
......@@ -232,6 +245,27 @@ class ExtHostQuickInput implements QuickInput {
this.update({ ignoreFocusOut });
}
get buttons() {
return this._buttons;
}
set buttons(buttons: QuickInputButton[]) {
this._buttons = buttons;
this._handlesToButtons.clear();
buttons.forEach((button, i) => {
this._handlesToButtons.set(i, button);
});
this.update({
buttons: buttons.map<TransferQuickInputButton>((button, i) => ({
iconPath: getIconUris(button.iconPath),
toolTip: button.tooltip,
handle: i,
}))
});
}
onDidTriggerButton = this._onDidTriggerButtonEmitter.event;
show(): void {
this._visible = true;
this.update({ visible: true });
......@@ -244,6 +278,11 @@ class ExtHostQuickInput implements QuickInput {
onDidHide = this._onDidHideEmitter.event;
_fireDidTriggerButton(handle: number) {
const button = this._handlesToButtons.get(handle);
this._onDidTriggerButtonEmitter.fire(button);
}
public dispose(): void {
if (this._disposed) {
return;
......@@ -289,14 +328,42 @@ class ExtHostQuickInput implements QuickInput {
}
}
function getIconUris(iconPath: QuickInputButton['iconPath']) {
const light = getLightIconUri(iconPath);
return { dark: getDarkIconUri(iconPath) || light, light };
}
function getLightIconUri(iconPath: QuickInputButton['iconPath']) {
if (iconPath && !(iconPath instanceof ThemeIcon)) {
if (typeof iconPath === 'string'
|| iconPath instanceof URI) {
return getIconUri(iconPath);
}
return getIconUri(iconPath['light']);
}
return undefined;
}
function getDarkIconUri(iconPath: QuickInputButton['iconPath']) {
if (iconPath && !(iconPath instanceof ThemeIcon) && iconPath['dark']) {
return getIconUri(iconPath['dark']);
}
return undefined;
}
function getIconUri(iconPath: string | URI) {
if (iconPath instanceof URI) {
return iconPath;
}
return URI.file(iconPath);
}
class ExtHostQuickPick extends ExtHostQuickInput implements QuickPick {
private _value = '';
private _placeholder: string;
private _onDidChangeValueEmitter = new Emitter<string>();
private _onDidAcceptEmitter = new Emitter<void>();
private _commands: QuickInputButton[] = [];
private _onDidTriggerButtonEmitter = new Emitter<QuickInputButton>();
private _items: QuickPickItem[] = [];
private _handlesToItems = new Map<number, QuickPickItem>();
private _canSelectMany = false;
......@@ -312,7 +379,6 @@ class ExtHostQuickPick extends ExtHostQuickInput implements QuickPick {
this._disposables.push(
this._onDidChangeValueEmitter,
this._onDidAcceptEmitter,
this._onDidTriggerButtonEmitter,
this._onDidChangeActiveEmitter,
this._onDidChangeSelectionEmitter,
);
......@@ -341,17 +407,6 @@ class ExtHostQuickPick extends ExtHostQuickInput implements QuickPick {
onDidAccept = this._onDidAcceptEmitter.event;
get buttons() {
return this._commands;
}
set buttons(commands: QuickInputButton[]) {
this._commands = commands;
this.update({ commands });
}
onDidTriggerButton = this._onDidTriggerButtonEmitter.event;
get items() {
return this._items;
}
......@@ -438,15 +493,12 @@ class ExtHostInputBox extends ExtHostQuickInput implements InputBox {
private _validationMessage: string;
private _onDidChangeValueEmitter = new Emitter<string>();
private _onDidAcceptEmitter = new Emitter<string>();
private _commands: QuickInputButton[] = [];
private _onDidTriggerButtonEmitter = new Emitter<QuickInputButton>();
constructor(proxy: MainThreadQuickOpenShape, extensionId: string, onDispose: () => void) {
super(proxy, extensionId, onDispose);
this._disposables.push(
this._onDidChangeValueEmitter,
this._onDidAcceptEmitter,
this._onDidTriggerButtonEmitter,
);
this.update({ type: 'inputBox' });
}
......@@ -499,15 +551,4 @@ class ExtHostInputBox extends ExtHostQuickInput implements InputBox {
onDidChangeValue = this._onDidChangeValueEmitter.event;
onDidAccept = this._onDidAcceptEmitter.event;
get buttons() {
return this._commands;
}
set buttons(commands: QuickInputButton[]) {
this._commands = commands;
this.update({ commands });
}
onDidTriggerButton = this._onDidTriggerButtonEmitter.event;
}
\ No newline at end of file
......@@ -56,6 +56,18 @@
line-height: initial;
}
.quick-input-action-bar {
display: flex;
}
.quick-input-action-bar .action-label.icon {
margin: 0 0 0 5px;
width: 16px;
height: 100%;
background-position: center;
background-repeat: no-repeat;
}
.quick-input-message {
margin: 0px 11px;
}
......
......@@ -36,6 +36,10 @@ import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorG
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ICommandAndKeybindingRule, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen';
import { ActionBar } 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';
const $ = dom.$;
......@@ -44,6 +48,7 @@ interface QuickInputUI {
checkAll: HTMLInputElement;
inputBox: QuickInputBox;
count: CountBadge;
actionBar: ActionBar;
message: HTMLElement;
progressBar: ProgressBar;
list: QuickInputList;
......@@ -69,15 +74,20 @@ class QuickInput implements IQuickInput {
private _enabled = true;
private _busy = false;
private _ignoreFocusOut = false;
private _buttons: IQuickInputButton[] = [];
private buttonsUpdated = false;
private onDidTriggerButtonEmitter = new Emitter<IQuickInputButton>();
private onDidHideEmitter = new Emitter<void>();
protected visibleDisposables: IDisposable[] = [];
protected disposables: IDisposable[] = [];
protected disposables: IDisposable[] = [
this.onDidTriggerButtonEmitter,
this.onDidHideEmitter,
];
private busyDelay: TPromise<void>;
constructor(protected ui: QuickInputUI) {
this.disposables.push(this.onDidHideEmitter);
}
get enabled() {
......@@ -107,6 +117,18 @@ class QuickInput implements IQuickInput {
this.update();
}
get buttons() {
return this._buttons;
}
set buttons(buttons: IQuickInputButton[]) {
this._buttons = buttons;
this.buttonsUpdated = true;
this.update();
}
onDidTriggerButton = this.onDidTriggerButtonEmitter.event;
show(): void {
if (this.visible) {
return;
......@@ -146,6 +168,15 @@ class QuickInput implements IQuickInput {
this.busyDelay.cancel();
this.busyDelay = null;
}
if (this.buttonsUpdated) {
this.buttonsUpdated = false;
this.ui.actionBar.clear();
this.ui.actionBar.push(this.buttons.map((button, index) => {
const action = new Action(`id-${index}`, '', getIconClass(button.iconPath), true, () => this.onDidTriggerButtonEmitter.fire(button));
action.tooltip = button.tooltip;
return action;
}), { icon: true, label: false });
}
}
public dispose(): void {
......@@ -160,8 +191,6 @@ class QuickPick extends QuickInput implements IQuickPick {
private _placeholder = '';
private onDidChangeValueEmitter = new Emitter<string>();
private onDidAcceptEmitter = new Emitter<string>();
private _commands: IQuickInputButton[] = [];
private onDidTriggerCommandEmitter = new Emitter<IQuickInputButton>();
private _items: IQuickPickItem[] = [];
private itemsUpdated = false;
private _canSelectMany = false;
......@@ -178,7 +207,6 @@ class QuickPick extends QuickInput implements IQuickPick {
this.disposables.push(
this.onDidChangeValueEmitter,
this.onDidAcceptEmitter,
this.onDidTriggerCommandEmitter,
this.onDidChangeActiveEmitter,
this.onDidChangeSelectionEmitter,
);
......@@ -206,17 +234,6 @@ class QuickPick extends QuickInput implements IQuickPick {
onDidAccept = this.onDidAcceptEmitter.event;
get buttons() {
return this._commands;
}
set buttons(commands: IQuickInputButton[]) {
this._commands = commands;
this.update(); // TODO
}
onDidTriggerCommand = this.onDidTriggerCommandEmitter.event;
get items() {
return this._items;
}
......@@ -268,7 +285,6 @@ class QuickPick extends QuickInput implements IQuickPick {
show() {
if (!this.visible) {
// TODO: this.onDidTriggerCommandEmitter,
this.visibleDisposables.push(
this.ui.inputBox.onDidChange(value => {
if (value === this.value) {
......@@ -425,15 +441,12 @@ class InputBox extends QuickInput implements IInputBox {
private _validationMessage = '';
private onDidValueChangeEmitter = new Emitter<string>();
private onDidAcceptEmitter = new Emitter<string>();
private _commands: IQuickInputButton[] = [];
private onDidTriggerCommandEmitter = new Emitter<IQuickInputButton>();
constructor(ui: QuickInputUI) {
super(ui);
this.disposables.push(
this.onDidValueChangeEmitter,
this.onDidAcceptEmitter,
this.onDidTriggerCommandEmitter,
);
}
......@@ -495,20 +508,8 @@ class InputBox extends QuickInput implements IInputBox {
onDidAccept = this.onDidAcceptEmitter.event;
get buttons() {
return this._commands;
}
set buttons(commands: IQuickInputButton[]) {
this._commands = commands;
this.update(); // TODO
}
onDidTriggerButton = this.onDidTriggerCommandEmitter.event;
show() {
if (!this.visible) {
// TODO: this.onDidTriggerCommandEmitter,
this.visibleDisposables.push(
this.ui.inputBox.onDidChange(value => {
if (value === this.value) {
......@@ -648,6 +649,10 @@ export class QuickInputService extends Component implements IQuickInputService {
this.onDidAcceptEmitter.fire(); // TODO: make single-select QuickPick exclusively use Accept?
}));
const actionBar = new ActionBar(headerContainer);
actionBar.domNode.classList.add('quick-input-action-bar');
this.toUnbind.push(actionBar);
const message = dom.append(container, $('.quick-input-message'));
const progressBar = new ProgressBar(container);
......@@ -719,6 +724,7 @@ export class QuickInputService extends Component implements IQuickInputService {
inputBox,
count,
message,
actionBar,
progressBar,
list,
onDidAccept: this.onDidAcceptEmitter.event,
......@@ -868,6 +874,7 @@ export class QuickInputService extends Component implements IQuickInputService {
this.ui.inputBox.password = false;
this.ui.inputBox.showDecoration(Severity.Ignore);
this.ui.count.setCount(0);
this.ui.actionBar.clear();
this.ui.message.textContent = '';
this.ui.progressBar.stop();
this.ui.list.setElements([]);
......@@ -979,6 +986,25 @@ 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: KeybindingsRegistry.WEIGHT.workbenchContrib(),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册