quickInputList.ts 20.5 KB
Newer Older
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

B
Benjamin Pasero 已提交
6
import 'vs/css!./media/quickInput';
J
Joao Moreno 已提交
7
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
8
import * as dom from 'vs/base/browser/dom';
9
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
10 11
import { WorkbenchList } from 'vs/platform/list/browser/listService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
C
Christof Marti 已提交
12
import { IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
13 14 15
import { IMatch } from 'vs/base/common/filters';
import { matchesFuzzyOcticonAware, parseOcticons } from 'vs/base/common/octicon';
import { compareAnything } from 'vs/base/common/comparers';
J
Joao Moreno 已提交
16
import { Emitter, Event } from 'vs/base/common/event';
C
Christof Marti 已提交
17 18 19
import { assign } from 'vs/base/common/objects';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
20 21
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
22
import { memoize } from 'vs/base/common/decorators';
23 24
import { range } from 'vs/base/common/arrays';
import * as platform from 'vs/base/common/platform';
25
import { listFocusBackground, pickerGroupBorder, pickerGroupForeground, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
26
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
C
Christof Marti 已提交
27 28 29
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action } from 'vs/base/common/actions';
import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils';
30
import { IListOptions } from 'vs/base/browser/ui/list/listWidget';
31
import { withNullAsUndefined } from 'vs/base/common/types';
32 33 34

const $ = dom.$;

35
interface IListElement {
36 37 38 39 40 41 42 43
	readonly index: number;
	readonly item: IQuickPickItem;
	readonly saneLabel: string;
	readonly saneDescription?: string;
	readonly saneDetail?: string;
	readonly checked: boolean;
	readonly separator?: IQuickPickSeparator;
	readonly fireButtonTriggered: (event: IQuickPickItemButtonEvent<IQuickPickItem>) => void;
C
Christof Marti 已提交
44 45
}

46
class ListElement implements IListElement {
C
Christof Marti 已提交
47
	index: number;
48
	item: IQuickPickItem;
C
Christof Marti 已提交
49 50 51
	saneLabel: string;
	saneDescription?: string;
	saneDetail?: string;
C
Christof Marti 已提交
52
	hidden = false;
53 54
	private _onChecked = new Emitter<boolean>();
	onChecked = this._onChecked.event;
55
	_checked?: boolean;
56
	get checked() {
57
		return !!this._checked;
C
Christof Marti 已提交
58
	}
59 60 61 62
	set checked(value: boolean) {
		if (value !== this._checked) {
			this._checked = value;
			this._onChecked.fire(value);
C
Christof Marti 已提交
63 64
		}
	}
65
	separator?: IQuickPickSeparator;
66 67 68
	labelHighlights?: IMatch[];
	descriptionHighlights?: IMatch[];
	detailHighlights?: IMatch[];
C
Christof Marti 已提交
69
	fireButtonTriggered: (event: IQuickPickItemButtonEvent<IQuickPickItem>) => void;
C
Christof Marti 已提交
70

71
	constructor(init: IListElement) {
C
Christof Marti 已提交
72 73
		assign(this, init);
	}
74 75
}

76
interface IListElementTemplateData {
C
Christof Marti 已提交
77
	entry: HTMLDivElement;
78
	checkbox: HTMLInputElement;
79 80
	label: IconLabel;
	detail: HighlightedLabel;
C
Christof Marti 已提交
81
	separator: HTMLDivElement;
C
Christof Marti 已提交
82
	actionBar: ActionBar;
83
	element: ListElement;
C
Christof Marti 已提交
84 85
	toDisposeElement: IDisposable[];
	toDisposeTemplate: IDisposable[];
86 87
}

J
Joao Moreno 已提交
88
class ListElementRenderer implements IListRenderer<ListElement, IListElementTemplateData> {
89

90
	static readonly ID = 'listelement';
91 92

	get templateId() {
93
		return ListElementRenderer.ID;
94 95
	}

96 97
	renderTemplate(container: HTMLElement): IListElementTemplateData {
		const data: IListElementTemplateData = Object.create(null);
98 99
		data.toDisposeElement = [];
		data.toDisposeTemplate = [];
100

C
Christof Marti 已提交
101
		data.entry = dom.append(container, $('.quick-input-list-entry'));
102

103
		// Checkbox
C
Christof Marti 已提交
104
		const label = dom.append(data.entry, $('label.quick-input-list-label'));
105
		data.checkbox = <HTMLInputElement>dom.append(label, $('input.quick-input-list-checkbox'));
106
		data.checkbox.type = 'checkbox';
C
Christof Marti 已提交
107
		data.toDisposeTemplate.push(dom.addStandardDisposableListener(data.checkbox, dom.EventType.CHANGE, e => {
108
			data.element.checked = data.checkbox.checked;
C
Christof Marti 已提交
109
		}));
110

111
		// Rows
112 113 114
		const rows = dom.append(label, $('.quick-input-list-rows'));
		const row1 = dom.append(rows, $('.quick-input-list-row'));
		const row2 = dom.append(rows, $('.quick-input-list-row'));
115 116

		// Label
117
		data.label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true, supportOcticons: true });
118

119
		// Detail
120
		const detailContainer = dom.append(row2, $('.quick-input-list-label-meta'));
S
Sandeep Somavarapu 已提交
121
		data.detail = new HighlightedLabel(detailContainer, true);
122

C
Christof Marti 已提交
123 124 125
		// Separator
		data.separator = dom.append(data.entry, $('.quick-input-list-separator'));

C
Christof Marti 已提交
126
		// Actions
C
Christof Marti 已提交
127
		data.actionBar = new ActionBar(data.entry);
C
Christof Marti 已提交
128 129 130
		data.actionBar.domNode.classList.add('quick-input-list-entry-action-bar');
		data.toDisposeTemplate.push(data.actionBar);

131 132 133
		return data;
	}

134
	renderElement(element: ListElement, index: number, data: IListElementTemplateData): void {
C
Christof Marti 已提交
135
		data.toDisposeElement = dispose(data.toDisposeElement);
136
		data.element = element;
137 138
		data.checkbox.checked = element.checked;
		data.toDisposeElement.push(element.onChecked(checked => data.checkbox.checked = checked));
139 140 141 142 143 144

		const { labelHighlights, descriptionHighlights, detailHighlights } = element;

		// Label
		const options: IIconLabelValueOptions = Object.create(null);
		options.matches = labelHighlights || [];
C
Christof Marti 已提交
145
		options.descriptionTitle = element.saneDescription;
146
		options.descriptionMatches = descriptionHighlights || [];
C
Christof Marti 已提交
147
		options.extraClasses = element.item.iconClasses;
B
Benjamin Pasero 已提交
148
		data.label.setLabel(element.saneLabel, element.saneDescription, options);
149 150

		// Meta
C
Christof Marti 已提交
151
		data.detail.set(element.saneDetail, detailHighlights);
C
Christof Marti 已提交
152

153
		// ARIA label
C
Christof Marti 已提交
154
		data.entry.setAttribute('aria-label', [element.saneLabel, element.saneDescription, element.saneDetail]
155
			.map(s => s && parseOcticons(s).text)
156 157 158
			.filter(s => !!s)
			.join(', '));

C
Christof Marti 已提交
159 160 161 162 163 164 165
		// Separator
		if (element.separator && element.separator.label) {
			data.separator.textContent = element.separator.label;
			data.separator.style.display = null;
		} else {
			data.separator.style.display = 'none';
		}
C
Christof Marti 已提交
166
		if (element.separator) {
C
Christof Marti 已提交
167 168 169 170 171
			dom.addClass(data.entry, 'quick-input-list-separator-border');
		} else {
			dom.removeClass(data.entry, 'quick-input-list-separator-border');
		}

C
Christof Marti 已提交
172 173
		// Actions
		data.actionBar.clear();
C
Christof Marti 已提交
174 175 176
		const buttons = element.item.buttons;
		if (buttons && buttons.length) {
			data.actionBar.push(buttons.map((button, index) => {
177
				const action = new Action(`id-${index}`, '', button.iconClass || (button.iconPath ? getIconClass(button.iconPath) : undefined), true, () => {
C
Christof Marti 已提交
178 179 180 181
					element.fireButtonTriggered({
						button,
						item: element.item
					});
182
					return Promise.resolve();
C
Christof Marti 已提交
183
				});
184
				action.tooltip = button.tooltip || '';
C
Christof Marti 已提交
185 186
				return action;
			}), { icon: true, label: false });
C
Christof Marti 已提交
187 188 189
			dom.addClass(data.entry, 'has-actions');
		} else {
			dom.removeClass(data.entry, 'has-actions');
C
Christof Marti 已提交
190
		}
191 192
	}

C
Christof Marti 已提交
193 194
	disposeElement(element: ListElement, index: number, data: IListElementTemplateData): void {
		data.toDisposeElement = dispose(data.toDisposeElement);
J
Joao Moreno 已提交
195 196
	}

197
	disposeTemplate(data: IListElementTemplateData): void {
C
Christof Marti 已提交
198 199
		data.toDisposeElement = dispose(data.toDisposeElement);
		data.toDisposeTemplate = dispose(data.toDisposeTemplate);
200 201 202
	}
}

J
Joao Moreno 已提交
203
class ListElementDelegate implements IListVirtualDelegate<ListElement> {
204

205
	getHeight(element: ListElement): number {
C
Christof Marti 已提交
206
		return element.saneDetail ? 44 : 22;
207 208
	}

209 210
	getTemplateId(element: ListElement): string {
		return ListElementRenderer.ID;
211 212 213
	}
}

214
export class QuickInputList {
215

216
	readonly id: string;
C
Christof Marti 已提交
217
	private container: HTMLElement;
218
	private list: WorkbenchList<ListElement>;
219
	private inputElements: Array<IQuickPickItem | IQuickPickSeparator>;
220
	private elements: ListElement[] = [];
221
	private elementsToIndexes = new Map<IQuickPickItem, number>();
222 223
	matchOnDescription = false;
	matchOnDetail = false;
A
Alex Ross 已提交
224
	matchOnLabel = true;
225 226 227 228
	private _onChangedAllVisibleChecked = new Emitter<boolean>();
	onChangedAllVisibleChecked: Event<boolean> = this._onChangedAllVisibleChecked.event;
	private _onChangedCheckedCount = new Emitter<number>();
	onChangedCheckedCount: Event<number> = this._onChangedCheckedCount.event;
229 230
	private _onChangedVisibleCount = new Emitter<number>();
	onChangedVisibleCount: Event<number> = this._onChangedVisibleCount.event;
231 232
	private _onChangedCheckedElements = new Emitter<IQuickPickItem[]>();
	onChangedCheckedElements: Event<IQuickPickItem[]> = this._onChangedCheckedElements.event;
C
Christof Marti 已提交
233 234
	private _onButtonTriggered = new Emitter<IQuickPickItemButtonEvent<IQuickPickItem>>();
	onButtonTriggered = this._onButtonTriggered.event;
235 236
	private _onLeave = new Emitter<void>();
	onLeave: Event<void> = this._onLeave.event;
237
	private _fireCheckedEvents = true;
238 239
	private elementDisposables: IDisposable[] = [];
	private disposables: IDisposable[] = [];
240 241 242

	constructor(
		private parent: HTMLElement,
243
		id: string,
244
		@IInstantiationService private readonly instantiationService: IInstantiationService
245
	) {
246
		this.id = id;
247 248 249
		this.container = dom.append(this.parent, $('.quick-input-list'));
		const delegate = new ListElementDelegate();
		this.list = this.instantiationService.createInstance(WorkbenchList, this.container, delegate, [new ListElementRenderer()], {
250
			identityProvider: { getId: element => element.saneLabel },
C
Christof Marti 已提交
251
			openController: { shouldOpen: () => false }, // Workaround #58124
252
			setRowLineHeight: false,
253 254
			multipleSelectionSupport: false,
			horizontalScrolling: false
255
		} as IListOptions<ListElement>);
256
		this.list.getHTMLElement().id = id;
257 258
		this.disposables.push(this.list);
		this.disposables.push(this.list.onKeyDown(e => {
C
Christof Marti 已提交
259
			const event = new StandardKeyboardEvent(e);
260 261 262 263
			switch (event.keyCode) {
				case KeyCode.Space:
					this.toggleCheckbox();
					break;
264 265 266 267 268
				case KeyCode.KEY_A:
					if (platform.isMacintosh ? e.metaKey : e.ctrlKey) {
						this.list.setFocus(range(this.list.length));
					}
					break;
269
				case KeyCode.UpArrow:
C
Christof Marti 已提交
270
				case KeyCode.PageUp:
271 272 273 274 275 276
					const focus1 = this.list.getFocus();
					if (focus1.length === 1 && focus1[0] === 0) {
						this._onLeave.fire();
					}
					break;
				case KeyCode.DownArrow:
C
Christof Marti 已提交
277
				case KeyCode.PageDown:
278 279
					const focus2 = this.list.getFocus();
					if (focus2.length === 1 && focus2[0] === this.list.length - 1) {
280 281 282
						this._onLeave.fire();
					}
					break;
C
Christof Marti 已提交
283 284
			}
		}));
285
		this.disposables.push(this.list.onMouseDown(e => {
286 287 288 289 290
			if (e.browserEvent.button !== 2) {
				// Works around / fixes #64350.
				e.browserEvent.preventDefault();
			}
		}));
291
		this.disposables.push(dom.addDisposableListener(this.container, dom.EventType.CLICK, e => {
292 293 294 295
			if (e.x || e.y) { // Avoid 'click' triggered by 'space' on checkbox.
				this._onLeave.fire();
			}
		}));
296 297
	}

298
	@memoize
299
	get onDidChangeFocus() {
J
Joao Moreno 已提交
300
		return Event.map(this.list.onFocusChange, e => e.elements.map(e => e.item));
C
Christof Marti 已提交
301 302
	}

303
	@memoize
304
	get onDidChangeSelection() {
J
Joao Moreno 已提交
305
		return Event.map(this.list.onSelectionChange, e => e.elements.map(e => e.item));
306 307
	}

308
	getAllVisibleChecked() {
309
		return this.allVisibleChecked(this.elements, false);
310 311
	}

312
	private allVisibleChecked(elements: ListElement[], whenNoneVisible = true) {
313 314
		for (let i = 0, n = elements.length; i < n; i++) {
			const element = elements[i];
315 316 317 318 319 320
			if (!element.hidden) {
				if (!element.checked) {
					return false;
				} else {
					whenNoneVisible = true;
				}
321 322
			}
		}
323
		return whenNoneVisible;
C
Christof Marti 已提交
324 325
	}

326
	getCheckedCount() {
327 328 329 330 331 332 333 334
		let count = 0;
		const elements = this.elements;
		for (let i = 0, n = elements.length; i < n; i++) {
			if (elements[i].checked) {
				count++;
			}
		}
		return count;
C
Christof Marti 已提交
335 336
	}

337 338 339 340 341 342 343 344 345 346 347
	getVisibleCount() {
		let count = 0;
		const elements = this.elements;
		for (let i = 0, n = elements.length; i < n; i++) {
			if (!elements[i].hidden) {
				count++;
			}
		}
		return count;
	}

348
	setAllVisibleChecked(checked: boolean) {
349 350 351 352 353 354 355 356 357 358 359
		try {
			this._fireCheckedEvents = false;
			this.elements.forEach(element => {
				if (!element.hidden) {
					element.checked = checked;
				}
			});
		} finally {
			this._fireCheckedEvents = true;
			this.fireCheckedEvents();
		}
C
Christof Marti 已提交
360 361
	}

362
	setElements(inputElements: Array<IQuickPickItem | IQuickPickSeparator>): void {
363
		this.elementDisposables = dispose(this.elementDisposables);
C
Christof Marti 已提交
364
		const fireButtonTriggered = (event: IQuickPickItemButtonEvent<IQuickPickItem>) => this.fireButtonTriggered(event);
C
Christof Marti 已提交
365 366 367 368 369 370 371
		this.inputElements = inputElements;
		this.elements = inputElements.reduce((result, item, index) => {
			if (item.type !== 'separator') {
				const previous = index && inputElements[index - 1];
				result.push(new ListElement({
					index,
					item,
C
Christof Marti 已提交
372 373 374
					saneLabel: item.label && item.label.replace(/\r?\n/g, ' '),
					saneDescription: item.description && item.description.replace(/\r?\n/g, ' '),
					saneDetail: item.detail && item.detail.replace(/\r?\n/g, ' '),
C
Christof Marti 已提交
375 376 377 378 379 380 381
					checked: false,
					separator: previous && previous.type === 'separator' ? previous : undefined,
					fireButtonTriggered
				}));
			}
			return result;
		}, [] as ListElement[]);
382
		this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents())));
383 384 385 386 387

		this.elementsToIndexes = this.elements.reduce((map, element, index) => {
			map.set(element.item, index);
			return map;
		}, new Map<IQuickPickItem, number>());
388
		this.list.splice(0, this.list.length); // Clear focus and selection first, sending the events when the list is empty.
389
		this.list.splice(0, this.list.length, this.elements);
390
		this._onChangedVisibleCount.fire(this.elements.length);
391 392
	}

393 394
	getFocusedElements() {
		return this.list.getFocusedElements()
395 396 397
			.map(e => e.item);
	}

398 399 400
	setFocusedElements(items: IQuickPickItem[]) {
		this.list.setFocus(items
			.filter(item => this.elementsToIndexes.has(item))
401
			.map(item => this.elementsToIndexes.get(item)!));
A
Alex Ross 已提交
402 403 404
		if (items.length > 0) {
			this.list.reveal(this.list.getFocus()[0]);
		}
405 406
	}

407 408 409 410
	getActiveDescendant() {
		return this.list.getHTMLElement().getAttribute('aria-activedescendant');
	}

411 412 413 414 415
	getSelectedElements() {
		return this.list.getSelectedElements()
			.map(e => e.item);
	}

416 417 418
	setSelectedElements(items: IQuickPickItem[]) {
		this.list.setSelection(items
			.filter(item => this.elementsToIndexes.has(item))
419
			.map(item => this.elementsToIndexes.get(item)!));
420 421
	}

422 423
	getCheckedElements() {
		return this.elements.filter(e => e.checked)
424 425 426
			.map(e => e.item);
	}

427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
	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();
		}
	}

C
Christof Marti 已提交
443 444 445 446
	set enabled(value: boolean) {
		this.list.getHTMLElement().style.pointerEvents = value ? null : 'none';
	}

447
	focus(what: 'First' | 'Last' | 'Next' | 'Previous' | 'NextPage' | 'PreviousPage'): void {
448 449 450 451
		if (!this.list.length) {
			return;
		}

C
Christof Marti 已提交
452
		if ((what === 'Next' || what === 'NextPage') && this.list.getFocus()[0] === this.list.length - 1) {
453 454
			what = 'First';
		}
C
Christof Marti 已提交
455
		if ((what === 'Previous' || what === 'PreviousPage') && this.list.getFocus()[0] === 0) {
456 457 458
			what = 'Last';
		}

459
		(this.list as any)['focus' + what]();
460
		this.list.reveal(this.list.getFocus()[0]);
461 462
	}

463 464 465 466
	clearFocus() {
		this.list.setFocus([]);
	}

467 468 469 470
	domFocus() {
		this.list.domFocus();
	}

471 472 473 474 475
	layout(): void {
		this.list.layout();
	}

	filter(query: string) {
A
Alex Ross 已提交
476 477 478
		if (!(this.matchOnLabel || this.matchOnDescription || this.matchOnDetail)) {
			return;
		}
479 480 481 482 483 484 485 486 487
		query = query.trim();

		// Reset filtering
		if (!query) {
			this.elements.forEach(element => {
				element.labelHighlights = undefined;
				element.descriptionHighlights = undefined;
				element.detailHighlights = undefined;
				element.hidden = false;
C
Christof Marti 已提交
488 489
				const previous = element.index && this.inputElements[element.index - 1];
				element.separator = previous && previous.type === 'separator' ? previous : undefined;
490 491 492 493
			});
		}

		// Filter by value (since we support octicons, use octicon aware fuzzy matching)
A
Alex Ross 已提交
494
		else {
495
			this.elements.forEach(element => {
496 497 498
				const labelHighlights = this.matchOnLabel ? withNullAsUndefined(matchesFuzzyOcticonAware(query, parseOcticons(element.saneLabel))) : undefined;
				const descriptionHighlights = this.matchOnDescription ? withNullAsUndefined(matchesFuzzyOcticonAware(query, parseOcticons(element.saneDescription || ''))) : undefined;
				const detailHighlights = this.matchOnDetail ? withNullAsUndefined(matchesFuzzyOcticonAware(query, parseOcticons(element.saneDetail || ''))) : undefined;
499

C
Christof Marti 已提交
500
				if (labelHighlights || descriptionHighlights || detailHighlights) {
501 502 503 504 505 506 507 508
					element.labelHighlights = labelHighlights;
					element.descriptionHighlights = descriptionHighlights;
					element.detailHighlights = detailHighlights;
					element.hidden = false;
				} else {
					element.labelHighlights = undefined;
					element.descriptionHighlights = undefined;
					element.detailHighlights = undefined;
C
Christof Marti 已提交
509
					element.hidden = !element.item.alwaysShow;
510
				}
C
Christof Marti 已提交
511
				element.separator = undefined;
512 513 514
			});
		}

515 516
		const shownElements = this.elements.filter(element => !element.hidden);

517
		// Sort by value
C
Christof Marti 已提交
518 519 520 521 522 523
		if (query) {
			const normalizedSearchValue = query.toLowerCase();
			shownElements.sort((a, b) => {
				return compareEntries(a, b, normalizedSearchValue);
			});
		}
524

525 526 527 528
		this.elementsToIndexes = shownElements.reduce((map, element, index) => {
			map.set(element.item, index);
			return map;
		}, new Map<IQuickPickItem, number>());
529
		this.list.splice(0, this.list.length, shownElements);
530
		this.list.setFocus([]);
C
Christof Marti 已提交
531
		this.list.layout();
532

533
		this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked());
534
		this._onChangedVisibleCount.fire(shownElements.length);
535
	}
C
Christof Marti 已提交
536 537

	toggleCheckbox() {
538 539 540 541 542 543 544 545 546 547
		try {
			this._fireCheckedEvents = false;
			const elements = this.list.getFocusedElements();
			const allChecked = this.allVisibleChecked(elements);
			for (const element of elements) {
				element.checked = !allChecked;
			}
		} finally {
			this._fireCheckedEvents = true;
			this.fireCheckedEvents();
C
Christof Marti 已提交
548 549 550
		}
	}

551
	display(display: boolean) {
552
		this.container.style.display = display ? '' : 'none';
553 554
	}

555 556 557 558
	isDisplayed() {
		return this.container.style.display !== 'none';
	}

559 560 561 562 563
	dispose() {
		this.elementDisposables = dispose(this.elementDisposables);
		this.disposables = dispose(this.disposables);
	}

564 565
	private fireCheckedEvents() {
		if (this._fireCheckedEvents) {
566 567 568
			this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked());
			this._onChangedCheckedCount.fire(this.getCheckedCount());
			this._onChangedCheckedElements.fire(this.getCheckedElements());
569 570
		}
	}
C
Christof Marti 已提交
571 572 573 574

	private fireButtonTriggered(event: IQuickPickItemButtonEvent<IQuickPickItem>) {
		this._onButtonTriggered.fire(event);
	}
575 576
}

577
function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: string): number {
578 579 580 581 582 583 584 585 586 587 588

	const labelHighlightsA = elementA.labelHighlights || [];
	const labelHighlightsB = elementB.labelHighlights || [];
	if (labelHighlightsA.length && !labelHighlightsB.length) {
		return -1;
	}

	if (!labelHighlightsA.length && labelHighlightsB.length) {
		return 1;
	}

C
Christof Marti 已提交
589
	return compareAnything(elementA.saneLabel, elementB.saneLabel, lookFor);
590 591 592 593 594 595
}

registerThemingParticipant((theme, collector) => {
	// Override inactive focus background with active focus background for single-pick case.
	const listInactiveFocusBackground = theme.getColor(listFocusBackground);
	if (listInactiveFocusBackground) {
596 597
		collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { background-color:  ${listInactiveFocusBackground}; }`);
		collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused:hover { background-color:  ${listInactiveFocusBackground}; }`);
598
	}
599 600 601 602 603 604 605
	const activeContrast = theme.getColor(activeContrastBorder);
	if (activeContrast) {
		collector.addRule(`.quick-input-list .monaco-list .monaco-list-row.focused { border: 1px dotted ${activeContrast}; }`);
		collector.addRule(`.quick-input-list .monaco-list .monaco-list-row { border: 1px solid transparent; }`);
		collector.addRule(`.quick-input-list .monaco-list .quick-input-list-entry { padding: 0 5px; height: 18px; align-items: center; }`);
		collector.addRule(`.quick-input-list .monaco-list .quick-input-list-entry-action-bar { margin-top: 0; }`);
	}
C
Christof Marti 已提交
606 607 608 609 610 611 612 613
	const pickerGroupBorderColor = theme.getColor(pickerGroupBorder);
	if (pickerGroupBorderColor) {
		collector.addRule(`.quick-input-list .quick-input-list-entry { border-top-color:  ${pickerGroupBorderColor}; }`);
	}
	const pickerGroupForegroundColor = theme.getColor(pickerGroupForeground);
	if (pickerGroupForegroundColor) {
		collector.addRule(`.quick-input-list .quick-input-list-separator { color:  ${pickerGroupForegroundColor}; }`);
	}
614
});