diff --git a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts index 959efeff31d498904b6823d96d0ffbaba53a55f8..c2e42dc13b65c968b25f3bdcc294d944f4aac6b0 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts @@ -17,6 +17,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { private _proxy: ExtHostQuickOpenShape; private _quickOpenService: IQuickOpenService; + private _quickInputService: IQuickInputService; private _doSetItems: (items: MyQuickPickItems[]) => any; private _doSetError: (error: Error) => any; private _contents: TPromise; @@ -25,10 +26,11 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { constructor( extHostContext: IExtHostContext, @IQuickOpenService quickOpenService: IQuickOpenService, - @IQuickInputService private _quickInputService: IQuickInputService + @IQuickInputService quickInputService: IQuickInputService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostQuickOpen); this._quickOpenService = quickOpenService; + this._quickInputService = quickInputService; } public dispose(): void { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.css b/src/vs/workbench/browser/parts/quickinput/quickInput.css index 3e0cdbea6a15578f83e388ffcd60d8531830da3f..a51f0f1d7d11f708b1829d0b63c6ac8999ac2ac4 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.css +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.css @@ -22,7 +22,7 @@ } .quick-input-box .monaco-inputbox > .wrapper > .input { - padding-left: 14px; + padding-left: 15px; } .quick-input-action { @@ -37,4 +37,47 @@ .quick-input-checkbox-list { margin-left: 3px; + line-height: 22px; +} + +.quick-input-checkbox-list .quick-input-checkbox-list-entry { + overflow: hidden; + display: flex; + height: 100%; +} + +.quick-input-checkbox-list .quick-input-checkbox-list-label { + overflow: hidden; + display: flex; + height: 100%; + flex: 1; +} + +.quick-input-checkbox-list .quick-input-checkbox-list-checkbox { + align-self: center; + /* margin-top: 5px; */ +} + +.quick-input-checkbox-list .quick-input-checkbox-list-rows { + overflow: hidden; + text-overflow: ellipsis; + display: flex; + flex-direction: column; + height: 100%; + flex: 1; + margin-left: 1px; +} + +.quick-input-checkbox-list .quick-input-checkbox-list-rows > .quick-input-checkbox-list-row { + display: flex; + align-items: center; +} + +.quick-input-checkbox-list .quick-input-checkbox-list-rows .monaco-highlighted-label span { + opacity: 1; +} + +.quick-input-checkbox-list .quick-input-checkbox-list-label-meta { + opacity: 0.7; + line-height: normal; } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index 56a0961c76235942422814a5fd5975d133759e0f..6475c430a329f8318c885b22fba04179315490e2 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -23,6 +23,8 @@ import { QuickInputBox } from './quickInputBox'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { CLOSE_ON_FOCUS_LOST_CONFIG } from 'vs/workbench/browser/quickopen'; const $ = dom.$; @@ -42,6 +44,7 @@ export class QuickInputService extends Component implements IQuickInputService { private resolve: (value?: object[] | Thenable) => void; constructor( + @IConfigurationService private configurationService: IConfigurationService, @IInstantiationService private instantiationService: IInstantiationService, @IPartService private partService: IPartService, @IThemeService themeService: IThemeService @@ -63,7 +66,7 @@ export class QuickInputService extends Component implements IQuickInputService { this.inputBox = new QuickInputBox(headerContainer); this.toUnbind.push(this.inputBox); this.inputBox.style(this.themeService.getTheme()); - this.inputBox.onInput(value => { + this.inputBox.onDidChange(value => { this.checkboxList.filter(value); }); this.toUnbind.push(this.inputBox.onKeyDown(event => { @@ -101,7 +104,9 @@ export class QuickInputService extends Component implements IQuickInputService { return; } } - this.close(false); + if (this.configurationService.getValue(CLOSE_ON_FOCUS_LOST_CONFIG)) { + this.close(false); + } })); this.toUnbind.push(dom.addDisposableListener(this.container, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { const event = new StandardKeyboardEvent(e); @@ -130,9 +135,10 @@ export class QuickInputService extends Component implements IQuickInputService { async pick(picks: TPromise, options?: IPickOptions, token?: CancellationToken): TPromise { this.create(); if (this.resolve) { - this.resolve(undefined); + this.resolve(); } + this.inputBox.setValue(''); this.inputBox.setPlaceholder(options.placeHolder || ''); // TODO: Progress indication. this.checkboxList.setElements(await picks); diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts b/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts index 0e7c127d58c7c7f3954df8d6c3178d080a59db6d..2c780a001a21bce481e870093b0a1f1662fe7653 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputBox.ts @@ -46,10 +46,14 @@ export class QuickInputBox { }); } - onInput(handler: (event: string) => void): IDisposable { + onDidChange(handler: (event: string) => void): IDisposable { return this.inputBox.onDidChange(handler); } + setValue(value: string) { + this.inputBox.value = value; + } + setPlaceholder(placeholder: string) { this.inputBox.setPlaceHolder(placeholder); } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputCheckboxList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputCheckboxList.ts index a50fd1b84b50fc95d036e36d59430c86b9d0c3ed..06b3f268b78b52b11e743b4f3e3935e83be2d311 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputCheckboxList.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputCheckboxList.ts @@ -19,20 +19,20 @@ import { Emitter } from 'vs/base/common/event'; import { assign } from 'vs/base/common/objects'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; const $ = dom.$; interface ISelectableElement { index: number; - item: object; - label: string; + item: IPickOpenEntry; selected: boolean; } class SelectableElement implements ISelectableElement { index: number; - item: object; - label: string; + item: IPickOpenEntry; shouldAlwaysShow = false; hidden = false; private _onSelected = new Emitter(); @@ -57,10 +57,10 @@ class SelectableElement implements ISelectableElement { } interface ISelectedElementTemplateData { - element: HTMLElement; - name: HTMLElement; checkbox: HTMLInputElement; - context: SelectableElement; + label: IconLabel; + detail: HighlightedLabel; + element: SelectableElement; toDisposeElement: IDisposable[]; toDisposeTemplate: IDisposable[]; } @@ -75,30 +75,50 @@ class SelectedElementRenderer implements IRenderer$('input'); + const entry = dom.append(container, $('.quick-input-checkbox-list-entry')); + const label = dom.append(entry, $('label.quick-input-checkbox-list-label')); + + // Entry + data.checkbox = dom.append(label, $('input.quick-input-checkbox-list-checkbox')); data.checkbox.type = 'checkbox'; data.toDisposeElement = []; data.toDisposeTemplate = []; data.toDisposeTemplate.push(dom.addStandardDisposableListener(data.checkbox, dom.EventType.CHANGE, e => { - data.context.selected = data.checkbox.checked; + data.element.selected = data.checkbox.checked; })); - dom.append(data.element, data.checkbox); + const rows = dom.append(label, $('.quick-input-checkbox-list-rows')); + const row1 = dom.append(rows, $('.quick-input-checkbox-list-row')); + const row2 = dom.append(rows, $('.quick-input-checkbox-list-row')); + + // Label + data.label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true }); - data.name = dom.append(data.element, $('span.label')); + // Detail + const detailContainer = dom.append(row2, $('.quick-input-checkbox-list-label-meta')); + data.detail = new HighlightedLabel(detailContainer); return data; } renderElement(element: SelectableElement, index: number, data: ISelectedElementTemplateData): void { dispose(data.toDisposeElement); - data.context = element; - data.name.textContent = element.label; - data.element.title = data.name.textContent; + data.element = element; data.checkbox.checked = element.selected; data.toDisposeElement.push(element.onSelected(selected => data.checkbox.checked = selected)); + + const { labelHighlights, descriptionHighlights, detailHighlights } = element; + + // Label + const options: IIconLabelValueOptions = Object.create(null); + options.matches = labelHighlights || []; + options.descriptionTitle = element.item.description; + options.descriptionMatches = descriptionHighlights || []; + data.label.setValue(element.item.label, element.item.description, options); + + // Meta + data.detail.set(element.item.detail, detailHighlights); } disposeTemplate(data: ISelectedElementTemplateData): void { @@ -109,7 +129,7 @@ class SelectedElementRenderer implements IRenderer { getHeight(element: SelectableElement): number { - return 22; + return element.item.detail ? 44 : 22; } getTemplateId(element: SelectableElement): string { @@ -147,10 +167,10 @@ export class QuickInputCheckboxList { this.elements = elements.map((item, index) => new SelectableElement({ index, item, - label: item.label, selected: !!item.selected })); this.list.splice(0, this.list.length, this.elements); + this.list.focusFirst(); } getSelectedElements() { @@ -182,9 +202,9 @@ export class QuickInputCheckboxList { // Filter by value (since we support octicons, use octicon aware fuzzy matching) else { this.elements.forEach(element => { - const labelHighlights = matchesFuzzyOcticonAware(query, parseOcticons(element.label)); - const descriptionHighlights = undefined; // TODO matchesFuzzyOcticonAware(query, parseOcticons(element.description)); - const detailHighlights = undefined; // TODO matchesFuzzyOcticonAware(query, parseOcticons(element.detail)); + const labelHighlights = matchesFuzzyOcticonAware(query, parseOcticons(element.item.label)); + const descriptionHighlights = matchesFuzzyOcticonAware(query, parseOcticons(element.item.description || '')); + const detailHighlights = matchesFuzzyOcticonAware(query, parseOcticons(element.item.detail || '')); if (element.shouldAlwaysShow || labelHighlights || descriptionHighlights || detailHighlights) { element.labelHighlights = labelHighlights; @@ -211,9 +231,7 @@ export class QuickInputCheckboxList { this.list.splice(0, this.list.length, this.elements.filter(element => !element.hidden)); this.list.layout(); - if (query) { - this.list.focusFirst(); - } + this.list.focusFirst(); } toggleCheckbox() { @@ -240,5 +258,5 @@ function compareEntries(elementA: SelectableElement, elementB: SelectableElement return 1; } - return compareAnything(elementA.label, elementB.label, lookFor); + return compareAnything(elementA.item.label, elementB.item.label, lookFor); } \ No newline at end of file