提交 6cba5616 编写于 作者: C Christof Marti

Writeable active and selected items (#49340, fixes vscode-azure-account#67)

上级 05b5db7a
......@@ -9,6 +9,13 @@ import * as assert from 'assert';
import { window, commands } from 'vscode';
import { closeAllEditors } from '../utils';
interface QuickPickExpected {
events: string[];
activeItems: string[][];
selectionItems: string[][];
acceptedItems: string[][];
}
suite('window namespace tests', function () {
suite('QuickInput tests', function () {
......@@ -20,59 +27,87 @@ suite('window namespace tests', function () {
_done(err);
};
const expectedEvents = ['active', 'active', 'selection', 'accept', 'hide'];
const expectedActiveItems = [['eins'], ['zwei']];
const expectedSelectionItems = [['zwei']];
const quickPick = createQuickPick({
events: ['active', 'active', 'selection', 'accept', 'hide'],
activeItems: [['eins'], ['zwei']],
selectionItems: [['zwei']],
acceptedItems: [['zwei']],
}, done);
quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label }));
quickPick.show();
const quickPick = window.createQuickPick();
quickPick.onDidChangeActive(items => {
try {
assert.equal('active', expectedEvents.shift());
const expected = expectedActiveItems.shift();
assert.deepEqual(items.map(item => item.label), expected);
assert.deepEqual(quickPick.activeItems.map(item => item.label), expected);
} catch (err) {
done(err);
}
});
quickPick.onDidChangeSelection(items => {
try {
assert.equal('selection', expectedEvents.shift());
const expected = expectedSelectionItems.shift();
assert.deepEqual(items.map(item => item.label), expected);
assert.deepEqual(quickPick.selectedItems.map(item => item.label), expected);
} catch (err) {
done(err);
}
});
quickPick.onDidAccept(() => {
try {
assert.equal('accept', expectedEvents.shift());
const expected = ['zwei'];
assert.deepEqual(quickPick.activeItems.map(item => item.label), expected);
assert.deepEqual(quickPick.selectedItems.map(item => item.label), expected);
quickPick.dispose();
} catch (err) {
done(err);
}
});
quickPick.onDidHide(() => {
try {
assert.equal('hide', expectedEvents.shift());
done();
} catch (err) {
done(err);
}
});
(async () => {
await commands.executeCommand('workbench.action.quickOpenSelectNext');
await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem');
})()
.catch(err => done(err));
});
test('createQuickPick, focus second', function (_done) {
let done = (err?: any) => {
done = () => {};
_done(err);
};
const quickPick = createQuickPick({
events: ['active', 'selection', 'accept', 'hide'],
activeItems: [['zwei']],
selectionItems: [['zwei']],
acceptedItems: [['zwei']],
}, done);
quickPick.items = ['eins', 'zwei', 'drei'].map(label => ({ label }));
quickPick.activeItems = [quickPick.items[1]];
quickPick.show();
(async () => {
await commands.executeCommand('workbench.action.quickOpenSelectNext');
await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem');
})()
.catch(err => done(err));
});
});
});
function createQuickPick(expected: QuickPickExpected, done: (err?: any) => void) {
const quickPick = window.createQuickPick();
quickPick.onDidChangeActive(items => {
try {
assert.equal('active', expected.events.shift());
const expectedItems = expected.activeItems.shift();
assert.deepEqual(items.map(item => item.label), expectedItems);
assert.deepEqual(quickPick.activeItems.map(item => item.label), expectedItems);
} catch (err) {
done(err);
}
});
quickPick.onDidChangeSelection(items => {
try {
assert.equal('selection', expected.events.shift());
const expectedItems = expected.selectionItems.shift();
assert.deepEqual(items.map(item => item.label), expectedItems);
assert.deepEqual(quickPick.selectedItems.map(item => item.label), expectedItems);
} catch (err) {
done(err);
}
});
quickPick.onDidAccept(() => {
try {
assert.equal('accept', expected.events.shift());
const expectedItems = expected.acceptedItems.shift();
assert.deepEqual(quickPick.activeItems.map(item => item.label), expectedItems);
assert.deepEqual(quickPick.selectedItems.map(item => item.label), expectedItems);
quickPick.dispose();
} catch (err) {
done(err);
}
});
quickPick.onDidHide(() => {
try {
assert.equal('hide', expected.events.shift());
done();
} catch (err) {
done(err);
}
});
return quickPick;
}
\ No newline at end of file
......@@ -440,6 +440,18 @@ suite('window namespace tests', () => {
assert.deepStrictEqual(await picks, ['eins', 'zwei']);
});
test('showQuickPick, keep selection (Microsoft/vscode-azure-account#67)', async function () {
const picks = window.showQuickPick([
{ label: 'eins' },
{ label: 'zwei', picked: true },
{ label: 'drei', picked: true }
], {
canPickMany: true
});
await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem');
assert.deepStrictEqual((await picks)!.map(pick => pick.label), ['zwei', 'drei']);
});
test('showQuickPick, undefined on cancel', function () {
const source = new CancellationTokenSource();
const p = window.showQuickPick(['eins', 'zwei', 'drei'], undefined, source.token);
......
......@@ -131,11 +131,11 @@ export interface IQuickPick extends IQuickInput {
matchOnDetail: boolean;
readonly activeItems: ReadonlyArray<IQuickPickItem>;
activeItems: ReadonlyArray<IQuickPickItem>;
readonly onDidChangeActive: Event<IQuickPickItem[]>;
readonly selectedItems: ReadonlyArray<IQuickPickItem>;
selectedItems: ReadonlyArray<IQuickPickItem>;
readonly onDidChangeSelection: Event<IQuickPickItem[]>;
}
......
......@@ -581,11 +581,11 @@ declare module 'vscode' {
matchOnDetail: boolean;
readonly activeItems: ReadonlyArray<QuickPickItem>;
activeItems: ReadonlyArray<QuickPickItem>;
readonly onDidChangeActive: Event<QuickPickItem[]>;
readonly selectedItems: ReadonlyArray<QuickPickItem>;
selectedItems: ReadonlyArray<QuickPickItem>;
readonly onDidChangeSelection: Event<QuickPickItem[]>;
}
......
......@@ -12,6 +12,11 @@ import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, Transf
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import URI from 'vs/base/common/uri';
interface QuickInputSession {
input: IQuickInput;
handlesToItems: Map<number, TransferQuickPickItems>;
}
@extHostNamedCustomer(MainContext.MainThreadQuickOpen)
export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
......@@ -114,7 +119,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
// ---- QuickInput
private sessions = new Map<number, IQuickInput>();
private sessions = new Map<number, QuickInputSession>();
$createOrUpdate(params: TransferQuickInput): TPromise<void> {
const sessionId = params.id;
......@@ -140,7 +145,10 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
input.onDidHide(() => {
this._proxy.$onDidHide(sessionId);
});
session = input;
session = {
input,
handlesToItems: new Map()
};
} else {
const input = this._quickInputService.createInputBox();
input.onDidAccept(() => {
......@@ -155,22 +163,36 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
input.onDidHide(() => {
this._proxy.$onDidHide(sessionId);
});
session = input;
session = {
input,
handlesToItems: new Map()
};
}
this.sessions.set(sessionId, session);
}
const { input, handlesToItems } = session;
for (const param in params) {
if (param === 'id' || param === 'type') {
continue;
}
if (param === 'visible') {
if (params.visible) {
session.show();
input.show();
} else {
session.hide();
input.hide();
}
} else if (param === 'items') {
handlesToItems.clear();
params[param].forEach(item => {
handlesToItems.set(item.handle, item);
});
input[param] = params[param];
} else if (param === 'activeItems' || param === 'selectedItems') {
input[param] = params[param]
.filter(handle => handlesToItems.has(handle))
.map(handle => handlesToItems.get(handle));
} else if (param === 'buttons') {
session[param] = params.buttons.map(button => {
input[param] = params.buttons.map(button => {
if (button.handle === -1) {
return this._quickInputService.backButton;
}
......@@ -185,7 +207,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
};
});
} else {
session[param] = params[param];
input[param] = params[param];
}
}
return TPromise.as(undefined);
......@@ -194,7 +216,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
$dispose(sessionId: number): TPromise<void> {
const session = this.sessions.get(sessionId);
if (session) {
session.dispose();
session.input.dispose();
this.sessions.delete(sessionId);
}
return TPromise.as(undefined);
......
......@@ -380,6 +380,10 @@ export interface TransferQuickPick extends BaseTransferQuickInput {
items?: TransferQuickPickItems[];
activeItems?: number[];
selectedItems?: number[];
canSelectMany?: boolean;
ignoreFocusOut?: boolean;
......
......@@ -455,6 +455,7 @@ class ExtHostQuickPick extends ExtHostQuickInput implements QuickPick {
private _items: QuickPickItem[] = [];
private _handlesToItems = new Map<number, QuickPickItem>();
private _itemsToHandles = new Map<QuickPickItem, number>();
private _canSelectMany = false;
private _matchOnDescription = true;
private _matchOnDetail = true;
......@@ -479,8 +480,10 @@ class ExtHostQuickPick extends ExtHostQuickInput implements QuickPick {
set items(items: QuickPickItem[]) {
this._items = items;
this._handlesToItems.clear();
this._itemsToHandles.clear();
items.forEach((item, i) => {
this._handlesToItems.set(i, item);
this._itemsToHandles.set(item, i);
});
this.update({
items: items.map((item, i) => ({
......@@ -524,12 +527,22 @@ class ExtHostQuickPick extends ExtHostQuickInput implements QuickPick {
return this._activeItems;
}
set activeItems(activeItems: QuickPickItem[]) {
this._activeItems = activeItems.filter(item => this._itemsToHandles.has(item));
this.update({ activeItems: this._activeItems.map(item => this._itemsToHandles.get(item)) });
}
onDidChangeActive = this._onDidChangeActiveEmitter.event;
get selectedItems() {
return this._selectedItems;
}
set selectedItems(selectedItems: QuickPickItem[]) {
this._selectedItems = selectedItems.filter(item => this._itemsToHandles.has(item));
this.update({ selectedItems: this._selectedItems.map(item => this._itemsToHandles.get(item)) });
}
onDidChangeSelection = this._onDidChangeSelectionEmitter.event;
_fireDidChangeActive(handles: number[]) {
......
......@@ -266,8 +266,10 @@ class QuickPick extends QuickInput implements IQuickPick {
private _matchOnDescription = true;
private _matchOnDetail = true;
private _activeItems: IQuickPickItem[] = [];
private activeItemsUpdated = false;
private onDidChangeActiveEmitter = new Emitter<IQuickPickItem[]>();
private _selectedItems: IQuickPickItem[] = [];
private selectedItemsUpdated = false;
private onDidChangeSelectionEmitter = new Emitter<IQuickPickItem[]>();
private quickNavigate = false;
......@@ -344,12 +346,24 @@ class QuickPick extends QuickInput implements IQuickPick {
return this._activeItems;
}
set activeItems(activeItems: IQuickPickItem[]) {
this._activeItems = activeItems;
this.activeItemsUpdated = true;
this.update();
}
onDidChangeActive = this.onDidChangeActiveEmitter.event;
get selectedItems() {
return this._selectedItems;
}
set selectedItems(selectedItems: IQuickPickItem[]) {
this._selectedItems = selectedItems;
this.selectedItemsUpdated = true;
this.update();
}
onDidChangeSelection = this.onDidChangeSelectionEmitter.event;
show() {
......@@ -390,6 +404,9 @@ class QuickPick extends QuickInput implements IQuickPick {
this.onDidAcceptEmitter.fire();
}),
this.ui.list.onDidChangeFocus(focusedItems => {
if (this.activeItemsUpdated) {
return; // Expect another event.
}
// Drop initial event.
if (!focusedItems.length && !this._activeItems.length) {
return;
......@@ -433,6 +450,7 @@ class QuickPick extends QuickInput implements IQuickPick {
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();
......@@ -440,7 +458,6 @@ class QuickPick extends QuickInput implements IQuickPick {
if (!this.canSelectMany) {
this.ui.list.focus('First');
}
this.itemsUpdated = false;
}
if (this.ui.container.classList.contains('show-checkboxes') !== this.canSelectMany) {
if (this.canSelectMany) {
......@@ -449,6 +466,18 @@ class QuickPick extends QuickInput implements IQuickPick {
this.ui.list.focus('First');
}
}
if (this.activeItemsUpdated) {
this.activeItemsUpdated = false;
this.ui.list.setFocusedElements(this.activeItems);
}
if (this.selectedItemsUpdated) {
this.selectedItemsUpdated = false;
if (this.canSelectMany) {
this.ui.list.setCheckedElements(this.selectedItems);
} else {
this.ui.list.setSelectedElements(this.selectedItems);
}
}
this.ui.ignoreFocusOut = this.ignoreFocusOut;
this.ui.list.matchOnDescription = this.matchOnDescription;
this.ui.list.matchOnDetail = this.matchOnDetail;
......@@ -878,6 +907,9 @@ export class QuickInputService extends Component implements IQuickInputService {
picks.then(items => {
input.busy = false;
input.items = items;
if (input.canSelectMany) {
input.selectedItems = items.filter(item => item.picked);
}
});
input.show();
picks.then(null, err => {
......
......@@ -149,6 +149,7 @@ export class QuickInputList {
private container: HTMLElement;
private list: WorkbenchList<ListElement>;
private elements: ListElement[] = [];
private elementsToIndexes = new Map<IQuickPickItem, number>();
matchOnDescription = false;
matchOnDetail = false;
private _onChangedAllVisibleChecked = new Emitter<boolean>();
......@@ -269,9 +270,14 @@ export class QuickInputList {
this.elements = elements.map((item, index) => new ListElement({
index,
item,
checked: !!item.picked
checked: false
}));
this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents())));
this.elementsToIndexes = this.elements.reduce((map, element, index) => {
map.set(element.item, index);
return map;
}, new Map<IQuickPickItem, number>());
this.list.splice(0, this.list.length, this.elements);
this.list.setFocus([]);
}
......@@ -281,16 +287,44 @@ export class QuickInputList {
.map(e => e.item);
}
setFocusedElements(items: IQuickPickItem[]) {
this.list.setFocus(items
.filter(item => this.elementsToIndexes.has(item))
.map(item => this.elementsToIndexes.get(item)));
}
getSelectedElements() {
return this.list.getSelectedElements()
.map(e => e.item);
}
setSelectedElements(items: IQuickPickItem[]) {
this.list.setSelection(items
.filter(item => this.elementsToIndexes.has(item))
.map(item => this.elementsToIndexes.get(item)));
}
getCheckedElements() {
return this.elements.filter(e => e.checked)
.map(e => e.item);
}
setCheckedElements(items: IQuickPickItem[]) {
try {
this._fireCheckedEvents = false;
const checked = new Set();
for (const item of items) {
checked.add(item);
}
for (const element of this.elements) {
element.checked = checked.has(element.item);
}
} finally {
this._fireCheckedEvents = true;
this.fireCheckedEvents();
}
}
set enabled(value: boolean) {
this.list.getHTMLElement().style.pointerEvents = value ? null : 'none';
}
......@@ -368,6 +402,10 @@ export class QuickInputList {
return compareEntries(a, b, normalizedSearchValue);
});
this.elementsToIndexes = shownElements.reduce((map, element, index) => {
map.set(element.item, index);
return map;
}, new Map<IQuickPickItem, number>());
this.list.splice(0, this.list.length, shownElements);
this.list.setFocus([]);
this.list.layout();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册