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

S
Sandeep Somavarapu 已提交
6
import { localize } from 'vs/nls';
7
import URI from 'vs/base/common/uri';
S
Sandeep Somavarapu 已提交
8
import { Dimension, $ } from 'vs/base/browser/builder';
9
import * as DOM from 'vs/base/browser/dom';
10
import { TPromise } from 'vs/base/common/winjs.base';
S
Sandeep Somavarapu 已提交
11
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
12 13
import { Widget } from 'vs/base/browser/ui/widget';
import Event, { Emitter } from 'vs/base/common/event';
S
Sandeep Somavarapu 已提交
14
import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
S
Sandeep Somavarapu 已提交
15
import { KeyCode } from 'vs/base/common/keyCodes';
A
Alex Dima 已提交
16
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference, IViewZone, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
17
import { InputBox, IInputOptions } from 'vs/base/browser/ui/inputbox/inputBox';
S
Sandeep Somavarapu 已提交
18
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
B
Benjamin Pasero 已提交
19
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
S
Sandeep Somavarapu 已提交
20
import { ISettingsGroup } from 'vs/workbench/parts/preferences/common/preferences';
21
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
S
Sandeep Somavarapu 已提交
22 23
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IAction, Action } from 'vs/base/common/actions';
24
import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
S
Sandeep Somavarapu 已提交
25
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
26 27
import { Position } from 'vs/editor/common/core/position';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
S
Sandeep Somavarapu 已提交
28
import { buttonBackground, buttonForeground, badgeForeground, badgeBackground, contrastBorder, errorForeground, focusBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
S
Sandeep Somavarapu 已提交
29
import { IContextKey } from 'vs/platform/contextkey/common/contextkey';
S
Sandeep Somavarapu 已提交
30
import { Separator, ActionBar, ActionsOrientation, BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
31
import { MarkdownString } from 'vs/base/common/htmlContent';
32
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
J
Joao Moreno 已提交
33
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
S
Sandeep Somavarapu 已提交
34
import { PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_INACTIVE_TITLE_FOREGROUND } from 'vs/workbench/common/theme';
A
Alex Dima 已提交
35
import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
36

S
Sandeep Somavarapu 已提交
37 38 39 40 41
export class SettingsHeaderWidget extends Widget implements IViewZone {

	private id: number;
	private _domNode: HTMLElement;

42
	protected titleContainer: HTMLElement;
S
Sandeep Somavarapu 已提交
43 44
	private messageElement: HTMLElement;

45
	constructor(protected editor: ICodeEditor, private title: string) {
S
Sandeep Somavarapu 已提交
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
		super();
		this.create();
		this._register(this.editor.onDidChangeConfiguration(() => this.layout()));
		this._register(this.editor.onDidLayoutChange(() => this.layout()));
	}

	get domNode(): HTMLElement {
		return this._domNode;
	}

	get heightInLines(): number {
		return 1;
	}

	get afterLineNumber(): number {
		return 0;
	}

64
	protected create() {
S
Sandeep Somavarapu 已提交
65 66 67
		this._domNode = DOM.$('.settings-header-widget');

		this.titleContainer = DOM.append(this._domNode, DOM.$('.title-container'));
S
Sandeep Somavarapu 已提交
68 69 70
		if (this.title) {
			DOM.append(this.titleContainer, DOM.$('.title')).textContent = this.title;
		}
S
Sandeep Somavarapu 已提交
71
		this.messageElement = DOM.append(this.titleContainer, DOM.$('.message'));
S
Sandeep Somavarapu 已提交
72 73 74
		if (this.title) {
			this.messageElement.style.paddingLeft = '12px';
		}
S
Sandeep Somavarapu 已提交
75 76 77 78 79 80 81 82 83 84 85 86 87 88

		this.editor.changeViewZones(accessor => {
			this.id = accessor.addZone(this);
			this.layout();
		});
	}

	public setMessage(message: string): void {
		this.messageElement.textContent = message;
	}

	private layout(): void {
		const configuration = this.editor.getConfiguration();
		this.titleContainer.style.fontSize = configuration.fontInfo.fontSize + 'px';
S
Sandeep Somavarapu 已提交
89
		if (!configuration.contribInfo.folding) {
S
Sandeep Somavarapu 已提交
90
			this.titleContainer.style.paddingLeft = '6px';
S
Sandeep Somavarapu 已提交
91
		}
S
Sandeep Somavarapu 已提交
92 93 94 95 96 97 98 99 100 101
	}

	public dispose() {
		this.editor.changeViewZones(accessor => {
			accessor.removeZone(this.id);
		});
		super.dispose();
	}
}

102 103 104
export class DefaultSettingsHeaderWidget extends SettingsHeaderWidget {

	private _onClick = this._register(new Emitter<void>());
105
	public readonly onClick: Event<void> = this._onClick.event;
106 107 108 109

	protected create() {
		super.create();

110
		this.toggleMessage(true);
111 112
	}

113
	public toggleMessage(hasSettings: boolean): void {
114 115 116 117 118 119 120 121
		if (hasSettings) {
			this.setMessage(localize('defaultSettings', "Place your settings in the right hand side editor to override."));
		} else {
			this.setMessage(localize('noSettingsFound', "No Settings Found."));
		}
	}
}

S
Sandeep Somavarapu 已提交
122 123 124 125 126
export class SettingsGroupTitleWidget extends Widget implements IViewZone {

	private id: number;
	private _afterLineNumber: number;
	private _domNode: HTMLElement;
127

S
Sandeep Somavarapu 已提交
128
	private titleContainer: HTMLElement;
S
Sandeep Somavarapu 已提交
129 130
	private icon: HTMLElement;
	private title: HTMLElement;
131

S
Sandeep Somavarapu 已提交
132
	private _onToggled = this._register(new Emitter<boolean>());
133
	public readonly onToggled: Event<boolean> = this._onToggled.event;
134

A
Alex Dima 已提交
135
	private previousPosition: Position;
136

S
Sandeep Somavarapu 已提交
137 138
	constructor(private editor: ICodeEditor, public settingsGroup: ISettingsGroup) {
		super();
S
Sandeep Somavarapu 已提交
139
		this.create();
S
Sandeep Somavarapu 已提交
140
		this._register(this.editor.onDidChangeConfiguration(() => this.layout()));
S
Sandeep Somavarapu 已提交
141
		this._register(this.editor.onDidLayoutChange(() => this.layout()));
142
		this._register(this.editor.onDidChangeCursorPosition((e) => this.onCursorChange(e)));
S
Sandeep Somavarapu 已提交
143
	}
144

S
Sandeep Somavarapu 已提交
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
	get domNode(): HTMLElement {
		return this._domNode;
	}

	get heightInLines(): number {
		return 1.5;
	}

	get afterLineNumber(): number {
		return this._afterLineNumber;
	}

	private create() {
		this._domNode = DOM.$('.settings-group-title-widget');

		this.titleContainer = DOM.append(this._domNode, DOM.$('.title-container'));
161
		this.titleContainer.tabIndex = 0;
S
Sandeep Somavarapu 已提交
162
		this.onclick(this.titleContainer, () => this.toggle());
163 164
		this.onkeydown(this.titleContainer, (e) => this.onKeyDown(e));
		const focusTracker = this._register(DOM.trackFocus(this.titleContainer));
165 166 167

		this._register(focusTracker.onDidFocus(() => this.toggleFocus(true)));
		this._register(focusTracker.onDidBlur(() => this.toggleFocus(false)));
S
Sandeep Somavarapu 已提交
168 169 170 171 172

		this.icon = DOM.append(this.titleContainer, DOM.$('.expand-collapse-icon'));
		this.title = DOM.append(this.titleContainer, DOM.$('.title'));
		this.title.textContent = this.settingsGroup.title + ` (${this.settingsGroup.sections.reduce((count, section) => count + section.settings.length, 0)})`;

S
Sandeep Somavarapu 已提交
173
		this.layout();
174 175
	}

S
Sandeep Somavarapu 已提交
176
	public render() {
S
Sandeep Somavarapu 已提交
177 178 179
		this._afterLineNumber = this.settingsGroup.range.startLineNumber - 2;
		this.editor.changeViewZones(accessor => {
			this.id = accessor.addZone(this);
S
Sandeep Somavarapu 已提交
180
			this.layout();
S
Sandeep Somavarapu 已提交
181
		});
S
Sandeep Somavarapu 已提交
182 183
	}

S
Sandeep Somavarapu 已提交
184 185
	public toggleCollapse(collapse: boolean) {
		DOM.toggleClass(this.titleContainer, 'collapsed', collapse);
186 187
	}

188 189 190 191
	public toggleFocus(focus: boolean): void {
		DOM.toggleClass(this.titleContainer, 'focused', focus);
	}

S
Sandeep Somavarapu 已提交
192 193 194 195
	public isCollapsed(): boolean {
		return DOM.hasClass(this.titleContainer, 'collapsed');
	}

S
Sandeep Somavarapu 已提交
196
	private layout(): void {
S
Sandeep Somavarapu 已提交
197 198
		const configuration = this.editor.getConfiguration();
		const layoutInfo = this.editor.getLayoutInfo();
S
Sandeep Somavarapu 已提交
199
		this._domNode.style.width = layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth + 'px';
S
Sandeep Somavarapu 已提交
200
		this.titleContainer.style.lineHeight = configuration.lineHeight + 3 + 'px';
S
Sandeep Somavarapu 已提交
201
		this.titleContainer.style.height = configuration.lineHeight + 3 + 'px';
S
Sandeep Somavarapu 已提交
202
		this.titleContainer.style.fontSize = configuration.fontInfo.fontSize + 'px';
S
Sandeep Somavarapu 已提交
203
		this.icon.style.minWidth = `${this.getIconSize(16)}px`;
S
Sandeep Somavarapu 已提交
204 205
	}

S
Sandeep Somavarapu 已提交
206
	private getIconSize(minSize: number): number {
S
Sandeep Somavarapu 已提交
207
		const fontSize = this.editor.getConfiguration().fontInfo.fontSize;
S
Sandeep Somavarapu 已提交
208
		return fontSize > 8 ? Math.max(fontSize, minSize) : 12;
209 210
	}

211 212 213 214
	private onKeyDown(keyboardEvent: IKeyboardEvent): void {
		switch (keyboardEvent.keyCode) {
			case KeyCode.Enter:
			case KeyCode.Space:
S
Sandeep Somavarapu 已提交
215 216 217 218 219 220 221
				this.toggle();
				break;
			case KeyCode.LeftArrow:
				this.collapse(true);
				break;
			case KeyCode.RightArrow:
				this.collapse(false);
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
				break;
			case KeyCode.UpArrow:
				if (this.settingsGroup.range.startLineNumber - 3 !== 1) {
					this.editor.focus();
					const lineNumber = this.settingsGroup.range.startLineNumber - 2;
					this.editor.setPosition({ lineNumber, column: this.editor.getModel().getLineMinColumn(lineNumber) });
				}
				break;
			case KeyCode.DownArrow:
				const lineNumber = this.isCollapsed() ? this.settingsGroup.range.startLineNumber : this.settingsGroup.range.startLineNumber - 1;
				this.editor.focus();
				this.editor.setPosition({ lineNumber, column: this.editor.getModel().getLineMinColumn(lineNumber) });
				break;
		}
	}

S
Sandeep Somavarapu 已提交
238 239 240 241 242 243 244 245 246 247 248
	private toggle() {
		this.collapse(!this.isCollapsed());
	}

	private collapse(collapse: boolean) {
		if (collapse !== this.isCollapsed()) {
			DOM.toggleClass(this.titleContainer, 'collapsed', collapse);
			this._onToggled.fire(collapse);
		}
	}

249
	private onCursorChange(e: ICursorPositionChangedEvent): void {
S
Sandeep Somavarapu 已提交
250
		if (e.source !== 'mouse' && this.focusTitle(e.position)) {
251 252 253 254
			this.titleContainer.focus();
		}
	}

A
Alex Dima 已提交
255
	private focusTitle(currentPosition: Position): boolean {
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
		const previousPosition = this.previousPosition;
		this.previousPosition = currentPosition;
		if (!previousPosition) {
			return false;
		}
		if (previousPosition.lineNumber === currentPosition.lineNumber) {
			return false;
		}
		if (currentPosition.lineNumber === this.settingsGroup.range.startLineNumber - 1 || currentPosition.lineNumber === this.settingsGroup.range.startLineNumber - 2) {
			return true;
		}
		if (this.isCollapsed() && currentPosition.lineNumber === this.settingsGroup.range.endLineNumber) {
			return true;
		}
		return false;
	}

S
Sandeep Somavarapu 已提交
273 274 275 276 277 278
	public dispose() {
		this.editor.changeViewZones(accessor => {
			accessor.removeZone(this.id);
		});
		super.dispose();
	}
S
Sandeep Somavarapu 已提交
279 280
}

S
Sandeep Somavarapu 已提交
281
export class FolderSettingsActionItem extends BaseActionItem {
282

S
Sandeep Somavarapu 已提交
283
	private _folder: IWorkspaceFolder;
284
	private _folderSettingCounts = new Map<string, number>();
285

S
Sandeep Somavarapu 已提交
286 287 288 289 290
	private container: HTMLElement;
	private anchorElement: HTMLElement;
	private labelElement: HTMLElement;
	private detailsElement: HTMLElement;
	private dropDownElement: HTMLElement;
291

S
Sandeep Somavarapu 已提交
292
	private disposables: IDisposable[] = [];
293

S
Sandeep Somavarapu 已提交
294 295 296 297 298 299 300 301 302 303
	constructor(
		action: IAction,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@IContextMenuService private contextMenuService: IContextMenuService
	) {
		super(null, action);
		const workspace = this.contextService.getWorkspace();
		this._folder = workspace.folders.length === 1 ? workspace.folders[0] : null;
		this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(() => this.onWorkspaceFoldersChanged()));
	}
304

S
Sandeep Somavarapu 已提交
305 306
	get folder(): IWorkspaceFolder {
		return this._folder;
307 308
	}

S
Sandeep Somavarapu 已提交
309 310 311
	set folder(folder: IWorkspaceFolder) {
		this._folder = folder;
		this.update();
S
Sandeep Somavarapu 已提交
312 313
	}

314 315 316
	public setCount(settingsTarget: URI, count: number): void {
		const folder = this.contextService.getWorkspaceFolder(settingsTarget).uri;
		this._folderSettingCounts.set(folder.toString(), count);
317 318 319
		this.update();
	}

S
Sandeep Somavarapu 已提交
320 321 322 323 324 325 326
	public render(container: HTMLElement): void {
		this.builder = $(container);

		this.container = container;
		this.labelElement = DOM.$('.action-title');
		this.detailsElement = DOM.$('.action-details');
		this.dropDownElement = DOM.$('.dropdown-icon.octicon.octicon-triangle-down.hide');
327
		this.anchorElement = DOM.$('a.action-label.folder-settings', {
S
Sandeep Somavarapu 已提交
328 329
			role: 'button',
			'aria-haspopup': 'true',
S
Sandeep Somavarapu 已提交
330
			'tabindex': '0'
S
Sandeep Somavarapu 已提交
331 332
		}, this.labelElement, this.detailsElement, this.dropDownElement);
		this.disposables.push(DOM.addDisposableListener(this.anchorElement, DOM.EventType.CLICK, e => this.onClick(e)));
S
Sandeep Somavarapu 已提交
333
		this.disposables.push(DOM.addDisposableListener(this.anchorElement, DOM.EventType.KEY_UP, e => this.onKeyUp(e)));
S
Sandeep Somavarapu 已提交
334 335 336 337

		DOM.append(this.container, this.anchorElement);

		this.update();
338 339
	}

S
Sandeep Somavarapu 已提交
340 341 342 343 344 345 346 347 348 349
	private onKeyUp(event: any): void {
		const keyboardEvent = new StandardKeyboardEvent(event);
		switch (keyboardEvent.keyCode) {
			case KeyCode.Enter:
			case KeyCode.Space:
				this.onClick(event);
				return;
		}
	}

S
Sandeep Somavarapu 已提交
350 351 352 353 354 355 356 357
	public onClick(event: DOM.EventLike): void {
		DOM.EventHelper.stop(event, true);
		if (!this.folder || this._action.checked) {
			this.showMenu();
		} else {
			this._action.run(this._folder);
		}
	}
358

S
Sandeep Somavarapu 已提交
359 360 361
	protected _updateEnabled(): void {
		this.update();
	}
362

S
Sandeep Somavarapu 已提交
363 364 365
	protected _updateChecked(): void {
		this.update();
	}
366

S
Sandeep Somavarapu 已提交
367 368 369 370 371 372 373 374 375
	private onWorkspaceFoldersChanged(): void {
		const oldFolder = this._folder;
		const workspace = this.contextService.getWorkspace();
		if (this._folder) {
			this._folder = workspace.folders.filter(folder => folder.uri.toString() === this._folder.uri.toString())[0] || workspace.folders[0];
		}
		this._folder = this._folder ? this._folder : workspace.folders.length === 1 ? workspace.folders[0] : null;

		this.update();
376

S
Sandeep Somavarapu 已提交
377 378 379 380 381 382 383
		if (this._action.checked) {
			if ((oldFolder || !this._folder)
				|| (!oldFolder || this._folder)
				|| (oldFolder && this._folder && oldFolder.uri.toString() === this._folder.uri.toString())) {
				this._action.run(this._folder);
			}
		}
384 385
	}

S
Sandeep Somavarapu 已提交
386
	private update(): void {
387 388 389
		let total = 0;
		this._folderSettingCounts.forEach(n => total += n);

S
Sandeep Somavarapu 已提交
390 391 392
		const workspace = this.contextService.getWorkspace();
		if (this._folder) {
			this.labelElement.textContent = this._folder.name;
S
Sandeep Somavarapu 已提交
393
			this.anchorElement.title = this._folder.name;
394
			const detailsText = this.labelWithCount(this._action.label, total);
395
			this.detailsElement.textContent = detailsText;
S
Sandeep Somavarapu 已提交
396 397
			DOM.toggleClass(this.dropDownElement, 'hide', workspace.folders.length === 1 || !this._action.checked);
		} else {
398
			const labelText = this.labelWithCount(this._action.label, total);
399
			this.labelElement.textContent = labelText;
S
Sandeep Somavarapu 已提交
400
			this.detailsElement.textContent = '';
S
Sandeep Somavarapu 已提交
401
			this.anchorElement.title = this._action.label;
S
Sandeep Somavarapu 已提交
402 403 404 405
			DOM.removeClass(this.dropDownElement, 'hide');
		}
		DOM.toggleClass(this.anchorElement, 'checked', this._action.checked);
		DOM.toggleClass(this.container, 'disabled', !this._action.enabled);
406 407
	}

S
Sandeep Somavarapu 已提交
408
	private showMenu(): void {
409
		this.contextMenuService.showContextMenu({
S
Sandeep Somavarapu 已提交
410 411
			getAnchor: () => this.container,
			getActions: () => TPromise.as(this.getDropdownMenuActions()),
S
Sandeep Somavarapu 已提交
412 413 414 415
			getActionItem: (action) => null,
			onHide: () => {
				this.anchorElement.blur();
			}
416 417 418
		});
	}

S
Sandeep Somavarapu 已提交
419
	private getDropdownMenuActions(): IAction[] {
420
		const actions: IAction[] = [];
S
Sandeep Somavarapu 已提交
421 422
		const workspaceFolders = this.contextService.getWorkspace().folders;
		if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && workspaceFolders.length > 0) {
423
			actions.push(new Separator());
424
			actions.push(...workspaceFolders.map((folder, index) => {
425
				const folderCount = this._folderSettingCounts.get(folder.uri.toString());
426 427
				return <IAction>{
					id: 'folderSettingsTarget' + index,
428
					label: this.labelWithCount(folder.name, folderCount),
S
Sandeep Somavarapu 已提交
429
					checked: this.folder && this.folder.uri.toString() === folder.uri.toString(),
430
					enabled: true,
S
Sandeep Somavarapu 已提交
431
					run: () => this._action.run(folder)
432
				};
433
			}));
434 435 436 437
		}
		return actions;
	}

438 439 440 441 442 443 444 445 446
	private labelWithCount(label: string, count: number | undefined): string {
		// Append the count if it's >0 and not undefined
		if (count) {
			label += ` (${count})`;
		}

		return label;
	}

S
Sandeep Somavarapu 已提交
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
	public dispose(): void {
		dispose(this.disposables);
		super.dispose();
	}
}

export type SettingsTarget = ConfigurationTarget.USER | ConfigurationTarget.WORKSPACE | URI;

export class SettingsTargetsWidget extends Widget {

	private settingsSwitcherBar: ActionBar;
	private userSettings: Action;
	private workspaceSettings: Action;
	private folderSettings: FolderSettingsActionItem;

	private _settingsTarget: SettingsTarget;

	private _onDidTargetChange: Emitter<SettingsTarget> = new Emitter<SettingsTarget>();
	public readonly onDidTargetChange: Event<SettingsTarget> = this._onDidTargetChange.event;

	constructor(
		parent: HTMLElement,
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
		@IInstantiationService private instantiationService: IInstantiationService
	) {
		super();
		this.create(parent);
		this._register(this.contextService.onDidChangeWorkbenchState(() => this.onWorkbenchStateChanged()));
		this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.update()));
	}

	private create(parent: HTMLElement): void {
		const settingsTabsWidget = DOM.append(parent, DOM.$('.settings-tabs-widget'));
		this.settingsSwitcherBar = this._register(new ActionBar(settingsTabsWidget, {
S
Sandeep Somavarapu 已提交
481
			orientation: ActionsOrientation.HORIZONTAL,
S
Sandeep Somavarapu 已提交
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
			ariaLabel: localize('settingsSwitcherBarAriaLabel', "Settings Switcher"),
			animated: false,
			actionItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : null
		}));

		this.userSettings = new Action('userSettings', localize('userSettings', "User Settings"), '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER));
		this.userSettings.tooltip = this.userSettings.label;

		this.workspaceSettings = new Action('workspaceSettings', localize('workspaceSettings', "Workspace Settings"), '.settings-tab', false, () => this.updateTarget(ConfigurationTarget.WORKSPACE));
		this.workspaceSettings.tooltip = this.workspaceSettings.label;

		const folderSettingsAction = new Action('folderSettings', localize('folderSettings', "Folder Settings"), '.settings-tab', false, (folder: IWorkspaceFolder) => this.updateTarget(folder ? folder.uri : ConfigurationTarget.USER));
		this.folderSettings = this.instantiationService.createInstance(FolderSettingsActionItem, folderSettingsAction);

		this.update();

S
Sandeep Somavarapu 已提交
498
		this.settingsSwitcherBar.push([this.userSettings, this.workspaceSettings, folderSettingsAction]);
S
Sandeep Somavarapu 已提交
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
	}

	public get settingsTarget(): SettingsTarget {
		return this._settingsTarget;
	}

	public set settingsTarget(settingsTarget: SettingsTarget) {
		this._settingsTarget = settingsTarget;
		this.userSettings.checked = ConfigurationTarget.USER === this.settingsTarget;
		this.workspaceSettings.checked = ConfigurationTarget.WORKSPACE === this.settingsTarget;
		if (this.settingsTarget instanceof URI) {
			this.folderSettings.getAction().checked = true;
			this.folderSettings.folder = this.contextService.getWorkspaceFolder(this.settingsTarget as URI);
		} else {
			this.folderSettings.getAction().checked = false;
514
		}
515 516
	}

517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
	public setResultCount(settingsTarget: SettingsTarget, count: number): void {
		if (settingsTarget === ConfigurationTarget.WORKSPACE) {
			let label = localize('workspaceSettings', "Workspace Settings");
			if (count) {
				label += ` (${count})`;
			}

			this.workspaceSettings.label = label;
		} else if (settingsTarget === ConfigurationTarget.USER) {
			let label = localize('userSettings', "User Settings");
			if (count) {
				label += ` (${count})`;
			}

			this.userSettings.label = label;
532
		} else if (settingsTarget instanceof URI) {
533
			this.folderSettings.setCount(settingsTarget, count);
534 535 536
		}
	}

S
Sandeep Somavarapu 已提交
537 538 539 540 541 542
	private onWorkbenchStateChanged(): void {
		this.folderSettings.folder = null;
		this.update();
		if (this.settingsTarget === ConfigurationTarget.WORKSPACE && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
			this.updateTarget(ConfigurationTarget.USER);
		}
543 544
	}

S
Sandeep Somavarapu 已提交
545 546 547 548 549
	private updateTarget(settingsTarget: SettingsTarget): TPromise<void> {
		const isSameTarget = this.settingsTarget === settingsTarget || settingsTarget instanceof URI && this.settingsTarget instanceof URI && this.settingsTarget.toString() === settingsTarget.toString();
		if (!isSameTarget) {
			this.settingsTarget = settingsTarget;
			this._onDidTargetChange.fire(this.settingsTarget);
550
		}
S
Sandeep Somavarapu 已提交
551
		return TPromise.as(null);
552
	}
S
Sandeep Somavarapu 已提交
553 554 555 556 557 558 559

	private update(): void {
		DOM.toggleClass(this.settingsSwitcherBar.domNode, 'empty-workbench', this.contextService.getWorkbenchState() === WorkbenchState.EMPTY);
		this.workspaceSettings.enabled = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY;
		this.folderSettings.getAction().enabled = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && this.contextService.getWorkspace().folders.length > 0;
	}

560 561
}

S
Sandeep Somavarapu 已提交
562
export interface SearchOptions extends IInputOptions {
S
Sandeep Somavarapu 已提交
563
	focusKey?: IContextKey<boolean>;
564
	showResultCount?: boolean;
S
Sandeep Somavarapu 已提交
565 566
}

567
export class SearchWidget extends Widget {
S
Sandeep Somavarapu 已提交
568

S
Sandeep Somavarapu 已提交
569
	public domNode: HTMLElement;
570

571
	private countElement: HTMLElement;
S
Sandeep Somavarapu 已提交
572 573
	private searchContainer: HTMLElement;
	private inputBox: InputBox;
574
	private controlsDiv: HTMLElement;
S
Sandeep Somavarapu 已提交
575

S
Sandeep Somavarapu 已提交
576
	private _onDidChange: Emitter<string> = this._register(new Emitter<string>());
S
Sandeep Somavarapu 已提交
577
	public readonly onDidChange: Event<string> = this._onDidChange.event;
S
Sandeep Somavarapu 已提交
578

S
Sandeep Somavarapu 已提交
579 580 581
	private _onFocus: Emitter<void> = this._register(new Emitter<void>());
	public readonly onFocus: Event<void> = this._onFocus.event;

S
Sandeep Somavarapu 已提交
582
	constructor(parent: HTMLElement, protected options: SearchOptions,
S
Sandeep Somavarapu 已提交
583
		@IContextViewService private contextViewService: IContextViewService,
B
Benjamin Pasero 已提交
584 585
		@IInstantiationService protected instantiationService: IInstantiationService,
		@IThemeService private themeService: IThemeService
S
Sandeep Somavarapu 已提交
586 587
	) {
		super();
S
Sandeep Somavarapu 已提交
588
		this.create(parent);
589 590
	}

S
Sandeep Somavarapu 已提交
591 592
	private create(parent: HTMLElement) {
		this.domNode = DOM.append(parent, DOM.$('div.settings-header-widget'));
593
		this.createSearchContainer(DOM.append(this.domNode, DOM.$('div.settings-search-container')));
594 595
		this.controlsDiv = DOM.append(this.domNode, DOM.$('div.settings-search-controls'));

596 597 598 599 600
		if (this.options.showResultCount) {
			this.countElement = DOM.append(this.controlsDiv, DOM.$('.settings-count-widget'));
			this._register(attachStylerCallback(this.themeService, { badgeBackground, contrastBorder }, colors => {
				const background = colors.badgeBackground ? colors.badgeBackground.toString() : null;
				const border = colors.contrastBorder ? colors.contrastBorder.toString() : null;
601

602
				this.countElement.style.backgroundColor = background;
603

604 605 606
				this.countElement.style.borderWidth = border ? '1px' : null;
				this.countElement.style.borderStyle = border ? 'solid' : null;
				this.countElement.style.borderColor = border;
607

608 609 610
				this.styleCountElementForeground();
			}));
		}
S
Sandeep Somavarapu 已提交
611

612
		this.inputBox.inputElement.setAttribute('aria-live', 'assertive');
S
Sandeep Somavarapu 已提交
613
		const focusTracker = this._register(DOM.trackFocus(this.inputBox.inputElement));
614
		this._register(focusTracker.onDidFocus(() => this._onFocus.fire()));
S
Sandeep Somavarapu 已提交
615

S
Sandeep Somavarapu 已提交
616
		if (this.options.focusKey) {
617 618
			this._register(focusTracker.onDidFocus(() => this.options.focusKey.set(true)));
			this._register(focusTracker.onDidBlur(() => this.options.focusKey.set(false)));
S
Sandeep Somavarapu 已提交
619
		}
S
Sandeep Somavarapu 已提交
620 621
	}

S
Sandeep Somavarapu 已提交
622
	private createSearchContainer(searchContainer: HTMLElement) {
S
Sandeep Somavarapu 已提交
623 624
		this.searchContainer = searchContainer;
		const searchInput = DOM.append(this.searchContainer, DOM.$('div.settings-search-input'));
S
Sandeep Somavarapu 已提交
625 626
		this.inputBox = this._register(this.createInputBox(searchInput));
		this._register(this.inputBox.onDidChange(value => this._onDidChange.fire(value)));
S
Sandeep Somavarapu 已提交
627 628
	}

629
	protected createInputBox(parent: HTMLElement): InputBox {
B
Benjamin Pasero 已提交
630 631 632 633
		const box = this._register(new InputBox(parent, this.contextViewService, this.options));
		this._register(attachInputBoxStyler(box, this.themeService));

		return box;
634 635
	}

636
	public showMessage(message: string, count: number): void {
637 638 639 640 641 642 643
		if (this.countElement) {
			this.countElement.textContent = message;
			this.inputBox.inputElement.setAttribute('aria-label', message);
			DOM.toggleClass(this.countElement, 'no-results', count === 0);
			this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px';
			this.styleCountElementForeground();
		}
644 645 646 647 648 649
	}

	private styleCountElementForeground() {
		const colorId = DOM.hasClass(this.countElement, 'no-results') ? errorForeground : badgeForeground;
		const color = this.themeService.getTheme().getColor(colorId);
		this.countElement.style.color = color ? color.toString() : null;
S
Sandeep Somavarapu 已提交
650 651 652 653
	}

	public layout(dimension: Dimension) {
		if (dimension.width < 400) {
654 655 656 657
			if (this.countElement) {
				DOM.addClass(this.countElement, 'hide');
			}

S
Sandeep Somavarapu 已提交
658 659
			this.inputBox.inputElement.style.paddingRight = '0px';
		} else {
660 661 662 663
			if (this.countElement) {
				DOM.removeClass(this.countElement, 'hide');
			}

664
			this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px';
S
Sandeep Somavarapu 已提交
665
		}
S
Sandeep Somavarapu 已提交
666
	}
667

668
	private getControlsWidth(): number {
669
		const countWidth = this.countElement ? DOM.getTotalWidth(this.countElement) : 0;
670
		return countWidth + 20;
671 672
	}

673 674
	public focus() {
		this.inputBox.focus();
S
Sandeep Somavarapu 已提交
675
		if (this.getValue()) {
S
Sandeep Somavarapu 已提交
676 677
			this.inputBox.select();
		}
678 679
	}

S
Sandeep Somavarapu 已提交
680 681 682 683
	public hasFocus(): boolean {
		return this.inputBox.hasFocus();
	}

S
Sandeep Somavarapu 已提交
684 685 686 687
	public clear() {
		this.inputBox.value = '';
	}

S
Sandeep Somavarapu 已提交
688
	public getValue(): string {
689 690 691
		return this.inputBox.value;
	}

S
Sandeep Somavarapu 已提交
692 693 694 695
	public setValue(value: string): string {
		return this.inputBox.value = value;
	}

S
Sandeep Somavarapu 已提交
696 697 698 699 700 701
	public dispose(): void {
		if (this.options.focusKey) {
			this.options.focusKey.set(false);
		}
		super.dispose();
	}
702
}
703

S
Sandeep Somavarapu 已提交
704
export class FloatingClickWidget extends Widget implements IOverlayWidget {
705 706 707

	private _domNode: HTMLElement;

S
Sandeep Somavarapu 已提交
708
	private _onClick: Emitter<void> = this._register(new Emitter<void>());
709
	public readonly onClick: Event<void> = this._onClick.event;
710

711 712 713
	constructor(
		private editor: ICodeEditor,
		private label: string,
S
Sandeep Somavarapu 已提交
714
		keyBindingAction: string,
715 716
		@IKeybindingService keybindingService: IKeybindingService,
		@IThemeService private themeService: IThemeService
717 718
	) {
		super();
719

720
		if (keyBindingAction) {
721
			let keybinding = keybindingService.lookupKeybinding(keyBindingAction);
B
Benjamin Pasero 已提交
722
			if (keybinding) {
723
				this.label += ' (' + keybinding.getLabel() + ')';
724 725 726 727 728
			}
		}
	}

	public render() {
S
Sandeep Somavarapu 已提交
729
		this._domNode = DOM.$('.floating-click-widget');
730
		this._register(attachStylerCallback(this.themeService, { buttonBackground, buttonForeground }, colors => {
731 732
			this._domNode.style.backgroundColor = colors.buttonBackground ? colors.buttonBackground.toString() : null;
			this._domNode.style.color = colors.buttonForeground ? colors.buttonForeground.toString() : null;
733 734
		}));

735
		DOM.append(this._domNode, DOM.$('')).textContent = this.label;
S
Sandeep Somavarapu 已提交
736
		this.onclick(this._domNode, e => this._onClick.fire());
737 738 739 740 741 742 743 744 745
		this.editor.addOverlayWidget(this);
	}

	public dispose(): void {
		this.editor.removeOverlayWidget(this);
		super.dispose();
	}

	public getId(): string {
S
Sandeep Somavarapu 已提交
746
		return 'editor.overlayWidget.floatingClickWidget';
747 748 749 750 751 752 753 754 755 756 757
	}

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

	public getPosition(): IOverlayWidgetPosition {
		return {
			preference: OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER
		};
	}
S
Push:  
Sandeep Somavarapu 已提交
758 759
}

S
Sandeep Somavarapu 已提交
760
export class EditPreferenceWidget<T> extends Disposable {
761

M
Matt Bierner 已提交
762
	public static readonly GLYPH_MARGIN_CLASS_NAME = 'edit-preferences-widget';
763 764 765 766

	private _line: number;
	private _preferences: T[];

S
Sandeep Somavarapu 已提交
767
	private _editPreferenceDecoration: string[];
768

S
Sandeep Somavarapu 已提交
769 770
	private _onClick: Emitter<IEditorMouseEvent> = new Emitter<IEditorMouseEvent>();
	public get onClick(): Event<IEditorMouseEvent> { return this._onClick.event; }
771

S
Sandeep Somavarapu 已提交
772
	constructor(private editor: ICodeEditor
773 774
	) {
		super();
S
Sandeep Somavarapu 已提交
775 776
		this._editPreferenceDecoration = [];
		this._register(this.editor.onMouseDown((e: IEditorMouseEvent) => {
J
Joao Moreno 已提交
777 778
			const data = e.target.detail as IMarginData;
			if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.isVisible()) {
S
Sandeep Somavarapu 已提交
779
				return;
780
			}
S
Sandeep Somavarapu 已提交
781
			this._onClick.fire(e);
782 783 784
		}));
	}

S
Sandeep Somavarapu 已提交
785 786
	get preferences(): T[] {
		return this._preferences;
787 788 789 790 791 792
	}

	getLine(): number {
		return this._line;
	}

S
Sandeep Somavarapu 已提交
793
	show(line: number, hoverMessage: string, preferences: T[]): void {
794
		this._preferences = preferences;
795
		const newDecoration: IModelDeltaDecoration[] = [];
S
Sandeep Somavarapu 已提交
796 797 798 799
		this._line = line;
		newDecoration.push({
			options: {
				glyphMarginClassName: EditPreferenceWidget.GLYPH_MARGIN_CLASS_NAME,
800
				glyphMarginHoverMessage: new MarkdownString().appendText(hoverMessage),
801
				stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
S
Sandeep Somavarapu 已提交
802 803 804 805 806 807 808 809 810
			},
			range: {
				startLineNumber: line,
				startColumn: 1,
				endLineNumber: line,
				endColumn: 1
			}
		});
		this._editPreferenceDecoration = this.editor.deltaDecorations(this._editPreferenceDecoration, newDecoration);
811 812 813
	}

	hide(): void {
S
Sandeep Somavarapu 已提交
814
		this._editPreferenceDecoration = this.editor.deltaDecorations(this._editPreferenceDecoration, []);
815 816
	}

S
Sandeep Somavarapu 已提交
817 818 819
	isVisible(): boolean {
		return this._editPreferenceDecoration.length > 0;
	}
820

S
Sandeep Somavarapu 已提交
821 822 823
	dispose(): void {
		this.hide();
		super.dispose();
824
	}
825
}
S
Sandeep Somavarapu 已提交
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885

registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {

	collector.addRule(`
		.settings-tabs-widget > .monaco-action-bar .action-item .action-label:focus,
		.settings-tabs-widget > .monaco-action-bar .action-item .action-label.checked {
			border-bottom: 1px solid;
		}
	`);
	// Title Active
	const titleActive = theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND);
	const titleActiveBorder = theme.getColor(PANEL_ACTIVE_TITLE_BORDER);
	if (titleActive || titleActiveBorder) {
		collector.addRule(`
			.settings-tabs-widget > .monaco-action-bar .action-item .action-label:hover,
			.settings-tabs-widget > .monaco-action-bar .action-item .action-label.checked {
				color: ${titleActive};
				border-bottom-color: ${titleActiveBorder};
			}
		`);
	}

	// Title Inactive
	const titleInactive = theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND);
	if (titleInactive) {
		collector.addRule(`
			.settings-tabs-widget > .monaco-action-bar .action-item .action-label {
				color: ${titleInactive};
			}
		`);
	}

	// Title focus
	const focusBorderColor = theme.getColor(focusBorder);
	if (focusBorderColor) {
		collector.addRule(`
			.settings-tabs-widget > .monaco-action-bar .action-item .action-label:focus {
				border-bottom-color: ${focusBorderColor} !important;
			}
			`);
		collector.addRule(`
			.settings-tabs-widget > .monaco-action-bar .action-item .action-label:focus {
				outline: none;
			}
			`);
	}

	// Styling with Outline color (e.g. high contrast theme)
	const outline = theme.getColor(activeContrastBorder);
	if (outline) {
		const outline = theme.getColor(activeContrastBorder);

		collector.addRule(`
			.settings-tabs-widget > .monaco-action-bar .action-item .action-label.checked,
			.settings-tabs-widget > .monaco-action-bar .action-item .action-label:hover {
				outline-color: ${outline};
				outline-width: 1px;
				outline-style: solid;
				border-bottom: none;
				padding-bottom: 0;
S
Sandeep Somavarapu 已提交
886
				outline-offset: 2px;
S
Sandeep Somavarapu 已提交
887 888 889 890 891 892 893
			}

			.settings-tabs-widget > .monaco-action-bar .action-item .action-label:not(.checked):hover {
				outline-style: dashed;
			}
		`);
	}
894
});