findInput.ts 12.4 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  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!./findInput';

A
Alex Dima 已提交
9 10
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
11
import { IMessage as InputBoxMessage, IInputValidator, InputBox, IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox';
J
Johannes Rieken 已提交
12 13 14 15
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { Widget } from 'vs/base/browser/ui/widget';
import Event, { Emitter } from 'vs/base/common/event';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
16
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
J
Johannes Rieken 已提交
17
import { KeyCode } from 'vs/base/common/keyCodes';
18
import { CaseSensitiveCheckbox, WholeWordsCheckbox, RegexCheckbox } from 'vs/base/browser/ui/findinput/findInputCheckboxes';
19 20
import { Color } from 'vs/base/common/color';
import { ICheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox';
E
Erich Gamma 已提交
21

22
export interface IFindInputOptions extends IFindInputStyles {
M
Matt Bierner 已提交
23 24 25 26 27 28 29 30
	readonly placeholder?: string;
	readonly width?: number;
	readonly validation?: IInputValidator;
	readonly label: string;

	readonly appendCaseSensitiveLabel?: string;
	readonly appendWholeWordsLabel?: string;
	readonly appendRegexLabel?: string;
E
Erich Gamma 已提交
31 32
}

33
export interface IFindInputStyles extends IInputBoxStyles {
34
	inputActiveOptionBorder?: Color;
35 36
}

37 38
const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input");

A
Alex Dima 已提交
39
export class FindInput extends Widget {
E
Erich Gamma 已提交
40

M
Matt Bierner 已提交
41
	static readonly OPTION_CHANGE: string = 'optionChange';
E
Erich Gamma 已提交
42

A
Alex Dima 已提交
43
	private contextViewProvider: IContextViewProvider;
J
Johannes Rieken 已提交
44 45 46 47
	private width: number;
	private placeholder: string;
	private validation: IInputValidator;
	private label: string;
48

49
	private inputActiveOptionBorder: Color;
50 51
	private inputBackground: Color;
	private inputForeground: Color;
52 53
	private inputBorder: Color;

B
Benjamin Pasero 已提交
54 55 56 57 58 59
	private inputValidationInfoBorder: Color;
	private inputValidationInfoBackground: Color;
	private inputValidationWarningBorder: Color;
	private inputValidationWarningBackground: Color;
	private inputValidationErrorBorder: Color;
	private inputValidationErrorBackground: Color;
J
Johannes Rieken 已提交
60

61 62 63
	private regex: RegexCheckbox;
	private wholeWords: WholeWordsCheckbox;
	private caseSensitive: CaseSensitiveCheckbox;
E
Erich Gamma 已提交
64
	public domNode: HTMLElement;
J
Johannes Rieken 已提交
65
	public inputBox: InputBox;
E
Erich Gamma 已提交
66

67 68
	private readonly _onDidOptionChange = this._register(new Emitter<boolean>());
	public readonly onDidOptionChange: Event<boolean /* via keyboard */> = this._onDidOptionChange.event;
A
Alex Dima 已提交
69

70 71
	private readonly _onKeyDown = this._register(new Emitter<IKeyboardEvent>());
	public readonly onKeyDown: Event<IKeyboardEvent> = this._onKeyDown.event;
A
Alex Dima 已提交
72

73 74
	private readonly _onMouseDown = this._register(new Emitter<IMouseEvent>());
	public readonly onMouseDown: Event<IMouseEvent> = this._onMouseDown.event;
75

76 77
	private readonly _onInput = this._register(new Emitter<void>());
	public readonly onInput: Event<void> = this._onInput.event;
78

79 80
	private readonly _onKeyUp = this._register(new Emitter<IKeyboardEvent>());
	public readonly onKeyUp: Event<IKeyboardEvent> = this._onKeyUp.event;
A
Alex Dima 已提交
81

A
Cleanup  
Alex Dima 已提交
82
	private _onCaseSensitiveKeyDown = this._register(new Emitter<IKeyboardEvent>());
83
	public readonly onCaseSensitiveKeyDown: Event<IKeyboardEvent> = this._onCaseSensitiveKeyDown.event;
84

J
Johannes Rieken 已提交
85
	constructor(parent: HTMLElement, contextViewProvider: IContextViewProvider, options?: IFindInputOptions) {
A
Alex Dima 已提交
86
		super();
E
Erich Gamma 已提交
87 88 89 90
		this.contextViewProvider = contextViewProvider;
		this.width = options.width || 100;
		this.placeholder = options.placeholder || '';
		this.validation = options.validation;
91
		this.label = options.label || NLS_DEFAULT_LABEL;
92

93
		this.inputActiveOptionBorder = options.inputActiveOptionBorder;
94 95
		this.inputBackground = options.inputBackground;
		this.inputForeground = options.inputForeground;
96 97
		this.inputBorder = options.inputBorder;

B
Benjamin Pasero 已提交
98 99 100 101 102 103
		this.inputValidationInfoBorder = options.inputValidationInfoBorder;
		this.inputValidationInfoBackground = options.inputValidationInfoBackground;
		this.inputValidationWarningBorder = options.inputValidationWarningBorder;
		this.inputValidationWarningBackground = options.inputValidationWarningBackground;
		this.inputValidationErrorBorder = options.inputValidationErrorBorder;
		this.inputValidationErrorBackground = options.inputValidationErrorBackground;
E
Erich Gamma 已提交
104 105 106 107 108 109 110

		this.regex = null;
		this.wholeWords = null;
		this.caseSensitive = null;
		this.domNode = null;
		this.inputBox = null;

111
		this.buildDomNode(options.appendCaseSensitiveLabel || '', options.appendWholeWordsLabel || '', options.appendRegexLabel || '');
E
Erich Gamma 已提交
112

J
Johannes Rieken 已提交
113
		if (Boolean(parent)) {
E
Erich Gamma 已提交
114 115 116
			parent.appendChild(this.domNode);
		}

A
Alex Dima 已提交
117 118
		this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown.fire(e));
		this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp.fire(e));
119
		this.oninput(this.inputBox.inputElement, (e) => this._onInput.fire());
120
		this.onmousedown(this.inputBox.inputElement, (e) => this._onMouseDown.fire(e));
E
Erich Gamma 已提交
121 122 123
	}

	public enable(): void {
A
Alex Dima 已提交
124
		dom.removeClass(this.domNode, 'disabled');
E
Erich Gamma 已提交
125 126 127 128 129 130 131
		this.inputBox.enable();
		this.regex.enable();
		this.wholeWords.enable();
		this.caseSensitive.enable();
	}

	public disable(): void {
A
Alex Dima 已提交
132
		dom.addClass(this.domNode, 'disabled');
E
Erich Gamma 已提交
133 134 135 136 137 138
		this.inputBox.disable();
		this.regex.disable();
		this.wholeWords.disable();
		this.caseSensitive.disable();
	}

J
Johannes Rieken 已提交
139
	public setEnabled(enabled: boolean): void {
140 141 142 143 144 145 146
		if (enabled) {
			this.enable();
		} else {
			this.disable();
		}
	}

E
Erich Gamma 已提交
147 148 149 150 151 152
	public clear(): void {
		this.clearValidation();
		this.setValue('');
		this.focus();
	}

J
Johannes Rieken 已提交
153
	public setWidth(newWidth: number): void {
E
Erich Gamma 已提交
154 155 156 157 158 159 160 161 162 163
		this.width = newWidth;
		this.domNode.style.width = this.width + 'px';
		this.contextViewProvider.layout();
		this.setInputWidth();
	}

	public getValue(): string {
		return this.inputBox.value;
	}

J
Johannes Rieken 已提交
164
	public setValue(value: string): void {
E
Erich Gamma 已提交
165 166 167 168 169
		if (this.inputBox.value !== value) {
			this.inputBox.value = value;
		}
	}

170
	public style(styles: IFindInputStyles): void {
171
		this.inputActiveOptionBorder = styles.inputActiveOptionBorder;
172 173
		this.inputBackground = styles.inputBackground;
		this.inputForeground = styles.inputForeground;
174 175
		this.inputBorder = styles.inputBorder;

B
Benjamin Pasero 已提交
176 177 178 179 180 181
		this.inputValidationInfoBackground = styles.inputValidationInfoBackground;
		this.inputValidationInfoBorder = styles.inputValidationInfoBorder;
		this.inputValidationWarningBackground = styles.inputValidationWarningBackground;
		this.inputValidationWarningBorder = styles.inputValidationWarningBorder;
		this.inputValidationErrorBackground = styles.inputValidationErrorBackground;
		this.inputValidationErrorBorder = styles.inputValidationErrorBorder;
182

183
		this.applyStyles();
184 185
	}

186
	protected applyStyles(): void {
187
		if (this.domNode) {
188
			const checkBoxStyles: ICheckboxStyles = {
189
				inputActiveOptionBorder: this.inputActiveOptionBorder,
190 191 192 193 194 195
			};
			this.regex.style(checkBoxStyles);
			this.wholeWords.style(checkBoxStyles);
			this.caseSensitive.style(checkBoxStyles);

			const inputBoxStyles: IInputBoxStyles = {
196
				inputBackground: this.inputBackground,
197 198
				inputForeground: this.inputForeground,
				inputBorder: this.inputBorder,
B
Benjamin Pasero 已提交
199 200 201 202 203 204
				inputValidationInfoBackground: this.inputValidationInfoBackground,
				inputValidationInfoBorder: this.inputValidationInfoBorder,
				inputValidationWarningBackground: this.inputValidationWarningBackground,
				inputValidationWarningBorder: this.inputValidationWarningBorder,
				inputValidationErrorBackground: this.inputValidationErrorBackground,
				inputValidationErrorBorder: this.inputValidationErrorBorder
205
			};
206
			this.inputBox.style(inputBoxStyles);
207 208 209
		}
	}

E
Erich Gamma 已提交
210 211 212 213 214 215 216 217
	public select(): void {
		this.inputBox.select();
	}

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

J
Johannes Rieken 已提交
218
	public getCaseSensitive(): boolean {
A
Alex Dima 已提交
219
		return this.caseSensitive.checked;
E
Erich Gamma 已提交
220 221
	}

J
Johannes Rieken 已提交
222
	public setCaseSensitive(value: boolean): void {
A
Alex Dima 已提交
223
		this.caseSensitive.checked = value;
E
Erich Gamma 已提交
224 225 226
		this.setInputWidth();
	}

J
Johannes Rieken 已提交
227
	public getWholeWords(): boolean {
A
Alex Dima 已提交
228
		return this.wholeWords.checked;
E
Erich Gamma 已提交
229 230
	}

J
Johannes Rieken 已提交
231
	public setWholeWords(value: boolean): void {
A
Alex Dima 已提交
232
		this.wholeWords.checked = value;
E
Erich Gamma 已提交
233 234 235
		this.setInputWidth();
	}

J
Johannes Rieken 已提交
236
	public getRegex(): boolean {
A
Alex Dima 已提交
237
		return this.regex.checked;
E
Erich Gamma 已提交
238 239
	}

J
Johannes Rieken 已提交
240
	public setRegex(value: boolean): void {
A
Alex Dima 已提交
241
		this.regex.checked = value;
E
Erich Gamma 已提交
242
		this.setInputWidth();
243
		this.validate();
E
Erich Gamma 已提交
244 245 246 247 248 249
	}

	public focusOnCaseSensitive(): void {
		this.caseSensitive.focus();
	}

250 251 252 253 254 255 256
	private _lastHighlightFindOptions: number = 0;
	public highlightFindOptions(): void {
		dom.removeClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions));
		this._lastHighlightFindOptions = 1 - this._lastHighlightFindOptions;
		dom.addClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions));
	}

E
Erich Gamma 已提交
257
	private setInputWidth(): void {
A
Alex Dima 已提交
258
		let w = this.width - this.caseSensitive.width() - this.wholeWords.width() - this.regex.width();
E
Erich Gamma 已提交
259 260 261
		this.inputBox.width = w;
	}

J
Johannes Rieken 已提交
262
	private buildDomNode(appendCaseSensitiveLabel: string, appendWholeWordsLabel: string, appendRegexLabel: string): void {
E
Erich Gamma 已提交
263 264
		this.domNode = document.createElement('div');
		this.domNode.style.width = this.width + 'px';
A
Alex Dima 已提交
265
		dom.addClass(this.domNode, 'monaco-findInput');
E
Erich Gamma 已提交
266

A
Alex Dima 已提交
267
		this.inputBox = this._register(new InputBox(this.domNode, this.contextViewProvider, {
E
Erich Gamma 已提交
268 269 270
			placeholder: this.placeholder || '',
			ariaLabel: this.label || '',
			validationOptions: {
B
Benjamin Pasero 已提交
271
				validation: this.validation || null
272 273
			},
			inputBackground: this.inputBackground,
274 275
			inputForeground: this.inputForeground,
			inputBorder: this.inputBorder,
B
Benjamin Pasero 已提交
276 277 278 279 280 281
			inputValidationInfoBackground: this.inputValidationInfoBackground,
			inputValidationInfoBorder: this.inputValidationInfoBorder,
			inputValidationWarningBackground: this.inputValidationWarningBackground,
			inputValidationWarningBorder: this.inputValidationWarningBorder,
			inputValidationErrorBackground: this.inputValidationErrorBackground,
			inputValidationErrorBorder: this.inputValidationErrorBorder
A
Alex Dima 已提交
282
		}));
E
Erich Gamma 已提交
283

284 285
		this.regex = this._register(new RegexCheckbox({
			appendTitle: appendRegexLabel,
A
Alex Dima 已提交
286
			isChecked: false,
287 288 289 290 291
			onChange: (viaKeyboard) => {
				this._onDidOptionChange.fire(viaKeyboard);
				if (!viaKeyboard) {
					this.inputBox.focus();
				}
A
Alex Dima 已提交
292 293
				this.setInputWidth();
				this.validate();
294
			},
295
			inputActiveOptionBorder: this.inputActiveOptionBorder
A
Alex Dima 已提交
296
		}));
297 298
		this.wholeWords = this._register(new WholeWordsCheckbox({
			appendTitle: appendWholeWordsLabel,
A
Alex Dima 已提交
299
			isChecked: false,
300 301 302 303 304
			onChange: (viaKeyboard) => {
				this._onDidOptionChange.fire(viaKeyboard);
				if (!viaKeyboard) {
					this.inputBox.focus();
				}
A
Alex Dima 已提交
305 306
				this.setInputWidth();
				this.validate();
307
			},
308
			inputActiveOptionBorder: this.inputActiveOptionBorder
A
Alex Dima 已提交
309
		}));
310 311
		this.caseSensitive = this._register(new CaseSensitiveCheckbox({
			appendTitle: appendCaseSensitiveLabel,
A
Alex Dima 已提交
312
			isChecked: false,
313 314 315 316 317
			onChange: (viaKeyboard) => {
				this._onDidOptionChange.fire(viaKeyboard);
				if (!viaKeyboard) {
					this.inputBox.focus();
				}
A
Alex Dima 已提交
318 319
				this.setInputWidth();
				this.validate();
320 321 322
			},
			onKeyDown: (e) => {
				this._onCaseSensitiveKeyDown.fire(e);
323
			},
324
			inputActiveOptionBorder: this.inputActiveOptionBorder
A
Alex Dima 已提交
325
		}));
A
Alex Dima 已提交
326

327 328
		// Arrow-Key support to navigate between options
		let indexes = [this.caseSensitive.domNode, this.wholeWords.domNode, this.regex.domNode];
A
Cleanup  
Alex Dima 已提交
329
		this.onkeydown(this.domNode, (event: IKeyboardEvent) => {
A
Alexandru Dima 已提交
330
			if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) {
331 332 333
				let index = indexes.indexOf(<HTMLElement>document.activeElement);
				if (index >= 0) {
					let newIndex: number;
A
Alexandru Dima 已提交
334
					if (event.equals(KeyCode.RightArrow)) {
335
						newIndex = (index + 1) % indexes.length;
A
Alexandru Dima 已提交
336
					} else if (event.equals(KeyCode.LeftArrow)) {
337 338 339 340 341 342 343
						if (index === 0) {
							newIndex = indexes.length - 1;
						} else {
							newIndex = index - 1;
						}
					}

A
Alexandru Dima 已提交
344
					if (event.equals(KeyCode.Escape)) {
345 346 347 348 349
						indexes[index].blur();
					} else if (newIndex >= 0) {
						indexes[newIndex].focus();
					}

350
					dom.EventHelper.stop(event, true);
351 352 353 354
				}
			}
		});

E
Erich Gamma 已提交
355 356
		this.setInputWidth();

A
Alex Dima 已提交
357
		let controls = document.createElement('div');
E
Erich Gamma 已提交
358 359 360 361 362 363 364 365 366 367 368 369
		controls.className = 'controls';
		controls.appendChild(this.caseSensitive.domNode);
		controls.appendChild(this.wholeWords.domNode);
		controls.appendChild(this.regex.domNode);

		this.domNode.appendChild(controls);
	}

	public validate(): void {
		this.inputBox.validate();
	}

A
Alex Dima 已提交
370
	public showMessage(message: InputBoxMessage): void {
E
Erich Gamma 已提交
371 372 373 374 375 376 377 378 379 380
		this.inputBox.showMessage(message);
	}

	public clearMessage(): void {
		this.inputBox.hideMessage();
	}

	private clearValidation(): void {
		this.inputBox.hideMessage();
	}
381 382 383 384

	public dispose(): void {
		super.dispose();
	}
E
Erich Gamma 已提交
385
}