Skip to content

Commit

Permalink
Input box and checkbox list (#45589)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrmarti committed Mar 26, 2018
1 parent ea1e57b commit 7cd8795
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 82 deletions.
4 changes: 4 additions & 0 deletions src/vs/workbench/browser/parts/quickinput/quickInput.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
margin-left: -300px;
}

.quick-input-box {
margin: 6px;
}

.quick-input-actions {
padding: 3px;
}
Expand Down
103 changes: 21 additions & 82 deletions src/vs/workbench/browser/parts/quickinput/quickInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,81 +10,19 @@ import { Component } from 'vs/workbench/common/component';
import { IQuickInputService } from 'vs/platform/quickInput/common/quickInput';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { Dimension } from 'vs/base/browser/builder';
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import * as dom from 'vs/base/browser/dom';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { WorkbenchList } from 'vs/platform/list/browser/listService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService';
import { buttonBackground, buttonForeground, contrastBorder, buttonHoverBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme';
import { IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen';
import { TPromise } from 'vs/base/common/winjs.base';
import { CancellationToken } from 'vs/base/common/cancellation';
import { QuickInputCheckboxList } from './quickInputCheckboxList';
import { QuickInputBox } from './quickInputBox';

const $ = dom.$;

export interface ISelectedElement {
item: object;
label: string;
selected: boolean;
}

interface ISelectedElementTemplateData {
element: HTMLElement;
name: HTMLElement;
checkbox: HTMLInputElement;
context: ISelectedElement;
toDispose: IDisposable[];
}

class SelectedElementRenderer implements IRenderer<ISelectedElement, ISelectedElementTemplateData> {

static readonly ID = 'selectedelement';

get templateId() {
return SelectedElementRenderer.ID;
}

renderTemplate(container: HTMLElement): ISelectedElementTemplateData {
const data: ISelectedElementTemplateData = Object.create(null);
data.element = dom.append(container, $('.selected_element'));

data.checkbox = <HTMLInputElement>$('input');
data.checkbox.type = 'checkbox';
data.toDispose = [];
data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => data.context.selected = !data.context.selected));

dom.append(data.element, data.checkbox);

data.name = dom.append(data.element, $('span.label'));

return data;
}

renderElement(element: ISelectedElement, index: number, data: ISelectedElementTemplateData): void {
data.context = element;
data.name.textContent = element.label;
data.element.title = data.name.textContent;
data.checkbox.checked = element.selected;
}

disposeTemplate(templateData: ISelectedElementTemplateData): void {
dispose(templateData.toDispose);
}
}

class SelectedElementDelegate implements IDelegate<ISelectedElement> {

getHeight(element: ISelectedElement): number {
return 22;
}

getTemplateId(element: ISelectedElement): string {
return SelectedElementRenderer.ID;
}
}

export class QuickInputService extends Component implements IQuickInputService {

public _serviceBrand: any;
Expand All @@ -95,9 +33,9 @@ export class QuickInputService extends Component implements IQuickInputService {

private layoutDimensions: Dimension;
private container: HTMLElement;
private list: WorkbenchList<ISelectedElement>;
private inputBox: QuickInputBox;
private checkboxList: QuickInputCheckboxList;

private elements: ISelectedElement[] = [];
private resolve: (value?: object[] | Thenable<object[]>) => void;

constructor(
Expand All @@ -117,12 +55,13 @@ export class QuickInputService extends Component implements IQuickInputService {
this.container = dom.append(workbench, $('.quick-input-widget'));
this.container.style.display = 'none';

const listContainer = dom.append(this.container, $('.quick-input-list'));
const delegate = new SelectedElementDelegate();
this.list = this.instantiationService.createInstance(WorkbenchList, listContainer, delegate, [new SelectedElementRenderer()], {
identityProvider: element => element.label,
multipleSelectionSupport: false
}) as WorkbenchList<ISelectedElement>;
this.inputBox = new QuickInputBox(this.container);
this.inputBox.style(this.themeService.getTheme());
this.inputBox.onInput(value => {
this.checkboxList.filter(value);
});

this.checkboxList = this.instantiationService.createInstance(QuickInputCheckboxList, this.container);

const buttonContainer = dom.append(this.container, $('.quick-input-actions'));
const cancel = dom.append(buttonContainer, $('button'));
Expand All @@ -144,7 +83,7 @@ export class QuickInputService extends Component implements IQuickInputService {

private close(ok: boolean) {
if (ok) {
this.resolve(this.elements.filter(e => e.selected).map(e => e.item));
this.resolve(this.checkboxList.getSelectedElements());
} else {
this.resolve();
}
Expand All @@ -154,18 +93,13 @@ export class QuickInputService extends Component implements IQuickInputService {
async pick<T extends IPickOpenEntry>(picks: TPromise<T[]>, options?: IPickOptions, token?: CancellationToken): TPromise<T[]> {
this.create();

this.inputBox.setPlaceholder(options.placeHolder || '');
// TODO: Progress indication.
this.elements = (await picks).map(item => ({
item,
label: item.label,
selected: !!item.selected
}));
this.list.splice(0, this.list.length, this.elements);
this.checkboxList.setElements(await picks);

this.container.style.display = null;
this.updateLayout();
this.list.focusFirst();
this.list.domFocus();
this.inputBox.setFocus();

return new TPromise<T[]>(resolve => this.resolve = resolve);
}
Expand All @@ -185,9 +119,14 @@ export class QuickInputService extends Component implements IQuickInputService {
style.width = width + 'px';
style.marginLeft = '-' + (width / 2) + 'px';

this.list.layout();
this.inputBox.layout();
this.checkboxList.layout();
}
}

protected updateStyles() {
this.inputBox.style(this.themeService.getTheme());
}
}

registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
Expand Down
63 changes: 63 additions & 0 deletions src/vs/workbench/browser/parts/quickinput/quickInputBox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*---------------------------------------------------------------------------------------------
* 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 'vs/css!./quickInput';
import * as dom from 'vs/base/browser/dom';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import * as nls from 'vs/nls';
import { inputBackground, inputForeground, inputBorder } from 'vs/platform/theme/common/colorRegistry';
import { ITheme } from 'vs/platform/theme/common/themeService';
import { IDisposable } from 'vs/base/common/lifecycle';

const $ = dom.$;

const DEFAULT_INPUT_ARIA_LABEL = nls.localize('quickInputBoxAriaLabel', "Type to narrow down results.");

export class QuickInputBox {

public container: HTMLElement;
private inputBox: InputBox;

constructor(
private parent: HTMLElement
) {
this.container = dom.append(this.parent, $('.quick-input-box'));
this.inputBox = new InputBox(this.container, null, {
ariaLabel: DEFAULT_INPUT_ARIA_LABEL
});

// ARIA
const inputElement = this.inputBox.inputElement;
inputElement.setAttribute('role', 'combobox');
inputElement.setAttribute('aria-haspopup', 'false');
inputElement.setAttribute('aria-autocomplete', 'list');
}

onInput(handler: (event: string) => void): IDisposable {
return this.inputBox.onDidChange(handler);
}

setPlaceholder(placeholder: string) {
this.inputBox.setPlaceHolder(placeholder);
}

setFocus(): void {
this.inputBox.focus();
}

layout(): void {
this.inputBox.layout();
}

style(theme: ITheme) {
this.inputBox.style({
inputForeground: theme.getColor(inputForeground),
inputBackground: theme.getColor(inputBackground),
inputBorder: theme.getColor(inputBorder)
});
}
}
Loading

0 comments on commit 7cd8795

Please sign in to comment.