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

import 'vs/css!./simpleFindWidget';
import * as nls from 'vs/nls';
A
Alex Dima 已提交
8 9
import * as dom from 'vs/base/browser/dom';
import { FindInput } from 'vs/base/browser/ui/findinput/findInput';
10
import { Widget } from 'vs/base/browser/ui/widget';
11
import { Delayer } from 'vs/base/common/async';
12
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
A
Alex Dima 已提交
13 14 15
import { FindReplaceState } from 'vs/editor/contrib/find/findState';
import { SimpleButton } from 'vs/editor/contrib/find/findWidget';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
16
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
A
Alex Dima 已提交
17 18
import { editorWidgetBackground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
19
import { ContextScopedFindInput } from 'vs/platform/widget/browser/contextScopedHistoryWidget';
20 21 22 23 24 25 26 27

const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find");
const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find");
const NLS_PREVIOUS_MATCH_BTN_LABEL = nls.localize('label.previousMatchButton', "Previous match");
const NLS_NEXT_MATCH_BTN_LABEL = nls.localize('label.nextMatchButton', "Next match");
const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close");

export abstract class SimpleFindWidget extends Widget {
M
Matt Bierner 已提交
28 29 30
	private _findInput: FindInput;
	private _domNode: HTMLElement;
	private _innerDomNode: HTMLElement;
31
	private _isVisible: boolean = false;
M
Matt Bierner 已提交
32
	private _focusTracker: dom.IFocusTracker;
33
	private _findInputFocusTracker: dom.IFocusTracker;
M
Matt Bierner 已提交
34
	private _updateHistoryDelayer: Delayer<void>;
35

36
	constructor(
S
#50583  
Sandeep Somavarapu 已提交
37
		@IContextViewService private readonly _contextViewService: IContextViewService,
38 39 40
		@IContextKeyService contextKeyService: IContextKeyService,
		private readonly _state: FindReplaceState = new FindReplaceState(),
		showOptionButtons?: boolean
41 42
	) {
		super();
M
Matt Bierner 已提交
43

S
#50583  
Sandeep Somavarapu 已提交
44
		this._findInput = this._register(new ContextScopedFindInput(null, this._contextViewService, {
45 46
			label: NLS_FIND_INPUT_LABEL,
			placeholder: NLS_FIND_INPUT_PLACEHOLDER,
47
		}, contextKeyService, showOptionButtons));
48

49 50 51
		// Find History with update delayer
		this._updateHistoryDelayer = new Delayer<void>(500);

52 53
		this.oninput(this._findInput.domNode, (e) => {
			this.onInputChanged();
54
			this._delayedUpdateHistory();
55 56
		});

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
		this._findInput.setRegex(!!this._state.isRegex);
		this._findInput.setCaseSensitive(!!this._state.matchCase);
		this._findInput.setWholeWords(!!this._state.wholeWord);

		this._register(this._findInput.onDidOptionChange(() => {
			this._state.change({
				isRegex: this._findInput.getRegex(),
				wholeWord: this._findInput.getWholeWords(),
				matchCase: this._findInput.getCaseSensitive()
			}, true);
		}));

		this._register(this._state.onFindReplaceStateChange(() => {
			this._findInput.setRegex(this._state.isRegex);
			this._findInput.setWholeWords(this._state.wholeWord);
			this._findInput.setCaseSensitive(this._state.matchCase);
		}));

75 76 77 78 79 80 81 82 83 84 85 86 87 88
		this._register(this._findInput.onKeyDown((e) => {
			if (e.equals(KeyCode.Enter)) {
				this.find(false);
				e.preventDefault();
				return;
			}

			if (e.equals(KeyMod.Shift | KeyCode.Enter)) {
				this.find(true);
				e.preventDefault();
				return;
			}
		}));

89
		const prevBtn = new SimpleButton({
90 91 92 93
			label: NLS_PREVIOUS_MATCH_BTN_LABEL,
			className: 'previous',
			onTrigger: () => {
				this.find(true);
M
Matt Bierner 已提交
94
			}
95 96
		});

97
		const nextBtn = new SimpleButton({
98 99 100 101
			label: NLS_NEXT_MATCH_BTN_LABEL,
			className: 'next',
			onTrigger: () => {
				this.find(false);
M
Matt Bierner 已提交
102
			}
103 104
		});

105
		const closeBtn = new SimpleButton({
106 107 108 109
			label: NLS_CLOSE_BTN_LABEL,
			className: 'close-fw',
			onTrigger: () => {
				this.hide();
M
Matt Bierner 已提交
110
			}
111 112
		});

113 114 115 116 117 118 119 120
		this._innerDomNode = document.createElement('div');
		this._innerDomNode.classList.add('simple-find-part');
		this._innerDomNode.appendChild(this._findInput.domNode);
		this._innerDomNode.appendChild(prevBtn.domNode);
		this._innerDomNode.appendChild(nextBtn.domNode);
		this._innerDomNode.appendChild(closeBtn.domNode);

		// _domNode wraps _innerDomNode, ensuring that
121
		this._domNode = document.createElement('div');
122 123
		this._domNode.classList.add('simple-find-part-wrapper');
		this._domNode.appendChild(this._innerDomNode);
124

125
		this.onkeyup(this._innerDomNode, e => {
126 127 128 129 130 131 132
			if (e.equals(KeyCode.Escape)) {
				this.hide();
				e.preventDefault();
				return;
			}
		});

133
		this._focusTracker = this._register(dom.trackFocus(this._innerDomNode));
134 135
		this._register(this._focusTracker.onDidFocus(this.onFocusTrackerFocus.bind(this)));
		this._register(this._focusTracker.onDidBlur(this.onFocusTrackerBlur.bind(this)));
136

137 138 139 140
		this._findInputFocusTracker = this._register(dom.trackFocus(this._findInput.domNode));
		this._register(this._findInputFocusTracker.onDidFocus(this.onFindInputFocusTrackerFocus.bind(this)));
		this._register(this._findInputFocusTracker.onDidBlur(this.onFindInputFocusTrackerBlur.bind(this)));

141
		this._register(dom.addDisposableListener(this._innerDomNode, 'click', (event) => {
142 143 144 145
			event.stopPropagation();
		}));
	}

146 147 148 149 150 151
	protected abstract onInputChanged(): void;
	protected abstract find(previous: boolean): void;
	protected abstract onFocusTrackerFocus(): void;
	protected abstract onFocusTrackerBlur(): void;
	protected abstract onFindInputFocusTrackerFocus(): void;
	protected abstract onFindInputFocusTrackerBlur(): void;
C
cleidigh 已提交
152

153 154 155 156
	protected get inputValue() {
		return this._findInput.getValue();
	}

157
	public get focusTracker(): dom.IFocusTracker {
158
		return this._focusTracker;
159 160
	}

M
Matt Bierner 已提交
161 162
	public updateTheme(theme: ITheme): void {
		const inputStyles = {
163 164 165 166 167
			inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
			inputBackground: theme.getColor(inputBackground),
			inputForeground: theme.getColor(inputForeground),
			inputBorder: theme.getColor(inputBorder),
			inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground),
168
			inputValidationInfoForeground: theme.getColor(inputValidationInfoForeground),
169 170
			inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder),
			inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground),
171
			inputValidationWarningForeground: theme.getColor(inputValidationWarningForeground),
172 173
			inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder),
			inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground),
174
			inputValidationErrorForeground: theme.getColor(inputValidationErrorForeground),
175 176 177 178 179
			inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder)
		};
		this._findInput.style(inputStyles);
	}

M
Matt Bierner 已提交
180
	dispose() {
181 182 183 184
		super.dispose();

		if (this._domNode && this._domNode.parentElement) {
			this._domNode.parentElement.removeChild(this._domNode);
185
			this._domNode = undefined;
186 187 188
		}
	}

189 190 191 192
	public getDomNode(): HTMLElement {
		return this._domNode;
	}

193
	public reveal(initialInput?: string): void {
194 195 196 197
		if (initialInput) {
			this._findInput.setValue(initialInput);
		}

198
		if (this._isVisible) {
199 200 201 202 203 204 205
			this._findInput.select();
			return;
		}

		this._isVisible = true;

		setTimeout(() => {
206 207
			dom.addClass(this._innerDomNode, 'visible');
			this._innerDomNode.setAttribute('aria-hidden', 'false');
208
			setTimeout(() => {
209
				this._findInput.select();
210 211 212 213
			}, 200);
		}, 0);
	}

214 215 216 217 218 219 220 221 222 223 224 225 226
	public show(initialInput?: string): void {
		if (initialInput && !this._isVisible) {
			this._findInput.setValue(initialInput);
		}

		this._isVisible = true;

		setTimeout(() => {
			dom.addClass(this._innerDomNode, 'visible');
			this._innerDomNode.setAttribute('aria-hidden', 'false');
		}, 0);
	}

227 228 229 230
	public hide(): void {
		if (this._isVisible) {
			this._isVisible = false;

231 232
			dom.removeClass(this._innerDomNode, 'visible');
			this._innerDomNode.setAttribute('aria-hidden', 'true');
233 234
		}
	}
235 236 237 238 239 240

	protected _delayedUpdateHistory() {
		this._updateHistoryDelayer.trigger(this._updateHistory.bind(this));
	}

	protected _updateHistory() {
S
Sandeep Somavarapu 已提交
241
		this._findInput.inputBox.addToHistory();
242
	}
243 244 245 246 247 248 249 250 251 252 253 254

	protected _getRegexValue(): boolean {
		return this._findInput.getRegex();
	}

	protected _getWholeWordValue(): boolean {
		return this._findInput.getWholeWords();
	}

	protected _getCaseSensitiveValue(): boolean {
		return this._findInput.getCaseSensitive();
	}
255 256 257 258 259 260 261 262 263
}

// theming
registerThemingParticipant((theme, collector) => {
	const findWidgetBGColor = theme.getColor(editorWidgetBackground);
	if (findWidgetBGColor) {
		collector.addRule(`.monaco-workbench .simple-find-part { background-color: ${findWidgetBGColor} !important; }`);
	}

M
Matt Bierner 已提交
264
	const widgetShadowColor = theme.getColor(widgetShadow);
265 266 267 268
	if (widgetShadowColor) {
		collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 2px 8px ${widgetShadowColor}; }`);
	}
});