simpleFindWidget.ts 7.6 KB
Newer Older
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.
 *--------------------------------------------------------------------------------------------*/

import 'vs/css!./simpleFindWidget';
import * as nls from 'vs/nls';
import { Widget } from 'vs/base/browser/ui/widget';
9 10
import { Delayer } from 'vs/base/common/async';
import { HistoryNavigator } from 'vs/base/common/history';
11 12 13 14 15 16
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as dom from 'vs/base/browser/dom';
import { FindInput } from 'vs/base/browser/ui/findinput/findInput';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { registerThemingParticipant, ITheme } from 'vs/platform/theme/common/themeService';
import { inputBackground, inputActiveOptionBorder, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorBorder, editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
R
rebornix 已提交
17
import { SimpleButton } from './findWidget';
18 19 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 {
	protected _findInput: FindInput;
	protected _domNode: HTMLElement;
28
	protected _innerDomNode: HTMLElement;
29 30
	protected _isVisible: boolean;
	protected _focusTracker: dom.IFocusTracker;
31 32 33
	protected _findInputFocusTracker: dom.IFocusTracker;
	protected _findHistory: HistoryNavigator<string>;
	protected _updateHistoryDelayer: Delayer<void>;
34

35
	constructor(
36
		@IContextViewService private readonly _contextViewService: IContextViewService,
37
		private animate: boolean = true
38 39 40 41 42 43 44
	) {
		super();
		this._findInput = this._register(new FindInput(null, this._contextViewService, {
			label: NLS_FIND_INPUT_LABEL,
			placeholder: NLS_FIND_INPUT_PLACEHOLDER,
		}));

45 46 47 48
		// Find History with update delayer
		this._findHistory = new HistoryNavigator<string>();
		this._updateHistoryDelayer = new Delayer<void>(500);

49 50
		this.oninput(this._findInput.domNode, (e) => {
			this.onInputChanged();
51
			this._delayedUpdateHistory();
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
		});

		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;
			}
		}));

		let prevBtn = new SimpleButton({
			label: NLS_PREVIOUS_MATCH_BTN_LABEL,
			className: 'previous',
			onTrigger: () => {
				this.find(true);
			},
			onKeyDown: (e) => { }
		});

		let nextBtn = new SimpleButton({
			label: NLS_NEXT_MATCH_BTN_LABEL,
			className: 'next',
			onTrigger: () => {
				this.find(false);
			},
			onKeyDown: (e) => { }
		});

		let closeBtn = new SimpleButton({
			label: NLS_CLOSE_BTN_LABEL,
			className: 'close-fw',
			onTrigger: () => {
				this.hide();
			},
			onKeyDown: (e) => { }
		});

95 96 97 98 99 100 101 102
		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
103
		this._domNode = document.createElement('div');
104 105
		this._domNode.classList.add('simple-find-part-wrapper');
		this._domNode.appendChild(this._innerDomNode);
106

107
		this.onkeyup(this._innerDomNode, e => {
108 109 110 111 112 113 114
			if (e.equals(KeyCode.Escape)) {
				this.hide();
				e.preventDefault();
				return;
			}
		});

115
		this._focusTracker = this._register(dom.trackFocus(this._innerDomNode));
116 117
		this._register(this._focusTracker.onDidFocus(this.onFocusTrackerFocus.bind(this)));
		this._register(this._focusTracker.onDidBlur(this.onFocusTrackerBlur.bind(this)));
118

119
		this._findInputFocusTracker = this._register(dom.trackFocus(this._findInput.domNode));
120 121
		this._register(this._findInputFocusTracker.onDidFocus(this.onFindInputFocusTrackerFocus.bind(this)));
		this._register(this._findInputFocusTracker.onDidBlur(this.onFindInputFocusTrackerBlur.bind(this)));
122

123
		this._register(dom.addDisposableListener(this._innerDomNode, 'click', (event) => {
124 125 126 127
			event.stopPropagation();
		}));
	}

128 129 130 131 132 133
	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 已提交
134

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
	protected get inputValue() {
		return this._findInput.getValue();
	}

	public updateTheme(theme?: ITheme): void {
		let inputStyles = {
			inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
			inputBackground: theme.getColor(inputBackground),
			inputForeground: theme.getColor(inputForeground),
			inputBorder: theme.getColor(inputBorder),
			inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground),
			inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder),
			inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground),
			inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder),
			inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground),
			inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder)
		};
		this._findInput.style(inputStyles);
	}

	public getDomNode(): HTMLElement {
		return this._domNode;
	}

159
	public reveal(initialInput?: string): void {
160 161 162 163
		if (initialInput) {
			this._findInput.setValue(initialInput);
		}

164
		if (this._isVisible) {
165 166 167 168 169 170 171
			this._findInput.select();
			return;
		}

		this._isVisible = true;

		setTimeout(() => {
172 173
			dom.addClass(this._innerDomNode, 'visible');
			this._innerDomNode.setAttribute('aria-hidden', 'false');
174
			if (!this.animate) {
175
				dom.addClass(this._innerDomNode, 'noanimation');
176 177
			}
			setTimeout(() => {
178
				dom.removeClass(this._innerDomNode, 'noanimation');
179
				this._findInput.select();
180 181 182 183 184 185 186 187
			}, 200);
		}, 0);
	}

	public hide(): void {
		if (this._isVisible) {
			this._isVisible = false;

188 189
			dom.removeClass(this._innerDomNode, 'visible');
			this._innerDomNode.setAttribute('aria-hidden', 'true');
190 191
		}
	}
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215

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

	protected _updateHistory() {
		if (this.inputValue) {
			this._findHistory.add(this._findInput.getValue());
		}
	}

	public showNextFindTerm() {
		let next = this._findHistory.next();
		if (next) {
			this._findInput.setValue(next);
		}
	}

	public showPreviousFindTerm() {
		let previous = this._findHistory.previous();
		if (previous) {
			this._findInput.setValue(previous);
		}
	}
216 217 218 219 220 221 222 223 224 225 226 227 228 229
}

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

	let widgetShadowColor = theme.getColor(widgetShadow);
	if (widgetShadowColor) {
		collector.addRule(`.monaco-workbench .simple-find-part { box-shadow: 0 2px 8px ${widgetShadowColor}; }`);
	}
});