titlebarPart.ts 20.3 KB
Newer Older
B
Benjamin Pasero 已提交
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!./media/titlebarpart';
B
Benjamin Pasero 已提交
9
import { TPromise } from 'vs/base/common/winjs.base';
10
import { Builder, $ } from 'vs/base/browser/builder';
B
Benjamin Pasero 已提交
11
import * as paths from 'vs/base/common/paths';
B
Benjamin Pasero 已提交
12
import { Part } from 'vs/workbench/browser/part';
13
import { ITitleService, ITitleProperties } from 'vs/workbench/services/title/common/titleService';
B
Benjamin Pasero 已提交
14
import { getZoomFactor } from 'vs/base/browser/browser';
15
import { IWindowService, IWindowsService, MenuBarVisibility } from 'vs/platform/windows/common/windows';
B
Benjamin Pasero 已提交
16 17 18 19
import * as errors from 'vs/base/common/errors';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { IAction, Action } from 'vs/base/common/actions';
20
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
B
Benjamin Pasero 已提交
21
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
22
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
23
import * as nls from 'vs/nls';
B
Benjamin Pasero 已提交
24
import { EditorInput, toResource, Verbosity } from 'vs/workbench/common/editor';
25
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
26
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
S
SteVen Batten 已提交
27
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
28
import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER } from 'vs/workbench/common/theme';
B
Benjamin Pasero 已提交
29
import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform';
B
Benjamin Pasero 已提交
30
import URI from 'vs/base/common/uri';
R
Ryan Adolf 已提交
31
import { Color } from 'vs/base/common/color';
B
Benjamin Pasero 已提交
32
import { trim } from 'vs/base/common/strings';
S
SteVen Batten 已提交
33
import { addDisposableListener, EventType, EventHelper, Dimension } from 'vs/base/browser/dom';
34
import { MenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl';
S
SteVen Batten 已提交
35
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
I
isidor 已提交
36
import { template, getBaseLabel } from 'vs/base/common/labels';
I
isidor 已提交
37
import { ILabelService } from 'vs/platform/label/common/label';
38
import { Event } from 'vs/base/common/event';
B
Benjamin Pasero 已提交
39 40 41

export class TitlebarPart extends Part implements ITitleService {

B
Benjamin Pasero 已提交
42
	_serviceBrand: any;
B
Benjamin Pasero 已提交
43

44
	private static readonly NLS_UNSUPPORTED = nls.localize('patchedWindowTitle', "[Unsupported]");
45
	private static readonly NLS_USER_IS_ADMIN = isWindows ? nls.localize('userIsAdmin', "[Administrator]") : nls.localize('userIsSudo', "[Superuser]");
46 47 48
	private static readonly NLS_EXTENSION_HOST = nls.localize('devExtensionWindowTitlePrefix', "[Extension Development Host]");
	private static readonly TITLE_DIRTY = '\u25cf ';
	private static readonly TITLE_SEPARATOR = isMacintosh ? '' : ' - '; // macOS uses special - separator
49

B
Benjamin Pasero 已提交
50 51
	private titleContainer: Builder;
	private title: Builder;
52
	private dragRegion: Builder;
53
	private windowControls: Builder;
S
SteVen Batten 已提交
54
	private maxRestoreControl: Builder;
55
	private appIcon: Builder;
56
	private menubarPart: MenubarControl;
S
SteVen Batten 已提交
57
	private menubar: Builder;
S
SteVen Batten 已提交
58
	private resizer: Builder;
B
Benjamin Pasero 已提交
59

B
Benjamin Pasero 已提交
60
	private pendingTitle: string;
B
Benjamin Pasero 已提交
61
	private representedFileName: string;
62 63 64 65 66

	private initialSizing: {
		titleFontSize?: number;
		titlebarHeight?: number;
		controlsWidth?: number;
67
		appIconSize?: number;
68
		appIconWidth?: number;
B
Benjamin Pasero 已提交
69
	} = Object.create(null);
B
Benjamin Pasero 已提交
70

B
Benjamin Pasero 已提交
71 72
	private isInactive: boolean;

73
	private properties: ITitleProperties;
74 75
	private activeEditorListeners: IDisposable[];

76 77
	constructor(
		id: string,
B
Benjamin Pasero 已提交
78 79
		@IContextMenuService private contextMenuService: IContextMenuService,
		@IWindowService private windowService: IWindowService,
80 81
		@IConfigurationService private configurationService: IConfigurationService,
		@IWindowsService private windowsService: IWindowsService,
B
Benjamin Pasero 已提交
82
		@IEditorService private editorService: IEditorService,
83
		@IEnvironmentService private environmentService: IEnvironmentService,
B
Benjamin Pasero 已提交
84
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
S
SteVen Batten 已提交
85
		@IInstantiationService private instantiationService: IInstantiationService,
I
isidor 已提交
86
		@IThemeService themeService: IThemeService,
I
isidor 已提交
87
		@ILabelService private labelService: ILabelService
88
	) {
B
Benjamin Pasero 已提交
89
		super(id, { hasTitle: false }, themeService);
90

91
		this.properties = { isPure: true, isAdmin: false };
92 93
		this.activeEditorListeners = [];

94 95 96 97
		this.registerListeners();
	}

	private registerListeners(): void {
B
Benjamin Pasero 已提交
98 99 100 101 102 103 104
		this._register(addDisposableListener(window, EventType.BLUR, () => this.onBlur()));
		this._register(addDisposableListener(window, EventType.FOCUS, () => this.onFocus()));
		this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChanged(e)));
		this._register(this.editorService.onDidActiveEditorChange(() => this.onActiveEditorChange()));
		this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.setTitle(this.getWindowTitle())));
		this._register(this.contextService.onDidChangeWorkbenchState(() => this.setTitle(this.getWindowTitle())));
		this._register(this.contextService.onDidChangeWorkspaceName(() => this.setTitle(this.getWindowTitle())));
105 106
	}

B
Benjamin Pasero 已提交
107 108 109 110 111 112 113 114 115 116
	private onBlur(): void {
		this.isInactive = true;
		this.updateStyles();
	}

	private onFocus(): void {
		this.isInactive = false;
		this.updateStyles();
	}

117 118
	private onConfigurationChanged(event: IConfigurationChangeEvent): void {
		if (event.affectsConfiguration('window.title')) {
119 120 121 122
			this.setTitle(this.getWindowTitle());
		}
	}

S
SteVen Batten 已提交
123 124 125 126 127
	private onMenubarVisibilityChanged(visible: boolean) {
		if (isWindows || isLinux) {
			// Hide title when toggling menu bar
			if (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'toggle' && visible) {
				this.title.style('visibility', 'hidden');
128

S
SteVen Batten 已提交
129 130 131 132 133 134 135
				// Hack to fix issue #52522 with layered webkit-app-region elements appearing under cursor
				this.dragRegion.hide();
				this.dragRegion.showDelayed(50);
			} else {
				this.title.style('visibility', null);
			}
		}
136 137
	}

138 139 140 141
	onMenubarVisibilityChange(): Event<boolean> {
		return this.menubarPart.onVisibilityChange;
	}

B
Benjamin Pasero 已提交
142
	private onActiveEditorChange(): void {
143 144 145 146 147 148 149 150 151

		// Dispose old listeners
		dispose(this.activeEditorListeners);
		this.activeEditorListeners = [];

		// Calculate New Window Title
		this.setTitle(this.getWindowTitle());

		// Apply listener for dirty and label changes
B
Benjamin Pasero 已提交
152 153 154
		const activeEditor = this.editorService.activeEditor;
		if (activeEditor instanceof EditorInput) {
			this.activeEditorListeners.push(activeEditor.onDidChangeDirty(() => {
155 156 157
				this.setTitle(this.getWindowTitle());
			}));

B
Benjamin Pasero 已提交
158
			this.activeEditorListeners.push(activeEditor.onDidChangeLabel(() => {
159 160 161
				this.setTitle(this.getWindowTitle());
			}));
		}
B
Benjamin Pasero 已提交
162 163 164 165 166 167 168 169 170 171 172 173 174 175

		// Represented File Name
		this.updateRepresentedFilename();
	}

	private updateRepresentedFilename(): void {
		const file = toResource(this.editorService.activeEditor, { supportSideBySide: true, filter: 'file' });
		const path = file ? file.fsPath : '';

		// Apply to window
		this.windowService.setRepresentedFilename(path);

		// Keep for context menu
		this.representedFileName = path;
176 177 178 179
	}

	private getWindowTitle(): string {
		let title = this.doGetWindowTitle();
B
Benjamin Pasero 已提交
180
		if (!trim(title)) {
181 182 183
			title = this.environmentService.appNameLong;
		}

184 185 186 187 188
		if (this.properties.isAdmin) {
			title = `${title} ${TitlebarPart.NLS_USER_IS_ADMIN}`;
		}

		if (!this.properties.isPure) {
189 190 191 192 193 194 195 196 197 198 199
			title = `${title} ${TitlebarPart.NLS_UNSUPPORTED}`;
		}

		// Extension Development Host gets a special title to identify itself
		if (this.environmentService.isExtensionDevelopment) {
			title = `${TitlebarPart.NLS_EXTENSION_HOST} - ${title}`;
		}

		return title;
	}

B
Benjamin Pasero 已提交
200
	updateProperties(properties: ITitleProperties): void {
201 202 203 204 205 206 207 208 209 210 211
		const isAdmin = typeof properties.isAdmin === 'boolean' ? properties.isAdmin : this.properties.isAdmin;
		const isPure = typeof properties.isPure === 'boolean' ? properties.isPure : this.properties.isPure;

		if (isAdmin !== this.properties.isAdmin || isPure !== this.properties.isPure) {
			this.properties.isAdmin = isAdmin;
			this.properties.isPure = isPure;

			this.setTitle(this.getWindowTitle());
		}
	}

212 213 214
	/**
	 * Possible template values:
	 *
B
Benjamin Pasero 已提交
215 216 217
	 * {activeEditorLong}: e.g. /Users/Development/myProject/myFolder/myFile.txt
	 * {activeEditorMedium}: e.g. myFolder/myFile.txt
	 * {activeEditorShort}: e.g. myFile.txt
218
	 * {rootName}: e.g. myFolder1, myFolder2, myFolder3
219
	 * {rootPath}: e.g. /Users/Development/myProject
220 221
	 * {folderName}: e.g. myFolder
	 * {folderPath}: e.g. /Users/Development/myFolder
222 223 224 225 226
	 * {appName}: e.g. VS Code
	 * {dirty}: indiactor
	 * {separator}: conditional separator
	 */
	private doGetWindowTitle(): string {
B
Benjamin Pasero 已提交
227
		const editor = this.editorService.activeEditor;
B
Benjamin Pasero 已提交
228
		const workspace = this.contextService.getWorkspace();
229

230
		let root: URI;
231
		if (workspace.configuration) {
232
			root = workspace.configuration;
233 234
		} else if (workspace.folders.length) {
			root = workspace.folders[0].uri;
235 236 237 238
		}

		// Compute folder resource
		// Single Root Workspace: always the root single workspace in this case
239
		// Otherwise: root folder of the currently active file if any
B
Benjamin Pasero 已提交
240
		let folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(editor, { supportSideBySide: true }));
241

242
		// Variables
B
Benjamin Pasero 已提交
243 244 245
		const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : '';
		const activeEditorMedium = editor ? editor.getTitle(Verbosity.MEDIUM) : activeEditorShort;
		const activeEditorLong = editor ? editor.getTitle(Verbosity.LONG) : activeEditorMedium;
246
		const rootName = root ? this.labelService.getWorkspaceLabel(workspace) : '';
I
isidor 已提交
247
		const rootPath = root ? this.labelService.getUriLabel(root) : '';
B
Benjamin Pasero 已提交
248
		const folderName = folder ? folder.name : '';
I
isidor 已提交
249
		const folderPath = folder ? this.labelService.getUriLabel(folder.uri) : '';
B
Benjamin Pasero 已提交
250
		const dirty = editor && editor.isDirty() ? TitlebarPart.TITLE_DIRTY : '';
251 252
		const appName = this.environmentService.appNameLong;
		const separator = TitlebarPart.TITLE_SEPARATOR;
253
		const titleTemplate = this.configurationService.getValue<string>('window.title');
254

I
isidor 已提交
255
		return template(titleTemplate, {
B
Benjamin Pasero 已提交
256 257 258
			activeEditorShort,
			activeEditorLong,
			activeEditorMedium,
259 260
			rootName,
			rootPath,
261 262
			folderName,
			folderPath,
263 264 265 266 267 268
			dirty,
			appName,
			separator: { label: separator }
		});
	}

B
Benjamin Pasero 已提交
269
	createContentArea(parent: HTMLElement): HTMLElement {
B
Benjamin Pasero 已提交
270 271
		this.titleContainer = $(parent);

272 273 274
		// Draggable region that we can manipulate for #52522
		this.dragRegion = $(this.titleContainer).div({ class: 'titlebar-drag-region' });

B
Benjamin Pasero 已提交
275
		// App Icon (Windows/Linux)
R
Ryan Adolf 已提交
276
		if (!isMacintosh) {
S
SteVen Batten 已提交
277
			this.appIcon = $(this.titleContainer).div({ class: 'window-appicon' });
278
		}
S
SteVen Batten 已提交
279

S
SteVen Batten 已提交
280
		// Menubar: the menubar part which is responsible for populating both the custom and native menubars
281
		this.menubarPart = this.instantiationService.createInstance(MenubarControl, 'workbench.parts.titlebar.menubar');
S
SteVen Batten 已提交
282
		this.menubar = $(this.titleContainer).div({
283 284
			'class': ['menubar'],
			id: 'workbench.parts.titlebar.menubar',
S
SteVen Batten 已提交
285 286 287 288 289 290 291 292 293
			role: 'menubar'
		});

		this.menubarPart.create(this.menubar.getHTMLElement());

		if (!isMacintosh) {
			this._register(this.menubarPart.onVisibilityChange(e => this.onMenubarVisibilityChanged(e)));
		}

B
Benjamin Pasero 已提交
294 295 296 297
		// Title
		this.title = $(this.titleContainer).div({ class: 'window-title' });
		if (this.pendingTitle) {
			this.title.text(this.pendingTitle);
B
Benjamin Pasero 已提交
298 299
		} else {
			this.setTitle(this.getWindowTitle());
B
Benjamin Pasero 已提交
300 301
		}

302
		// Maximize/Restore on doubleclick
S
SteVen Batten 已提交
303 304 305
		if (isMacintosh) {
			this.titleContainer.on(EventType.DBLCLICK, (e) => {
				EventHelper.stop(e);
306

S
SteVen Batten 已提交
307 308 309
				this.onTitleDoubleclick();
			});
		}
310

B
Benjamin Pasero 已提交
311
		// Context menu on title
312 313 314
		this.title.on([EventType.CONTEXT_MENU, EventType.MOUSE_DOWN], (e: MouseEvent) => {
			if (e.type === EventType.CONTEXT_MENU || e.metaKey) {
				EventHelper.stop(e);
B
Benjamin Pasero 已提交
315 316 317 318 319

				this.onContextMenu(e);
			}
		});

B
Benjamin Pasero 已提交
320
		// Window Controls (Windows/Linux)
R
Ryan Adolf 已提交
321
		if (!isMacintosh) {
322
			this.windowControls = $(this.titleContainer).div({ class: 'window-controls-container' });
S
SteVen Batten 已提交
323

B
Benjamin Pasero 已提交
324
			// Minimize
S
SteVen Batten 已提交
325
			$($(this.windowControls).div({ class: 'window-icon-bg' })).div({ class: 'window-icon window-minimize' }).on(EventType.CLICK, () => {
R
Ryan Adolf 已提交
326
				this.windowService.minimizeWindow().then(null, errors.onUnexpectedError);
327
			});
R
Ryan Adolf 已提交
328

B
Benjamin Pasero 已提交
329
			// Restore
S
SteVen Batten 已提交
330
			this.maxRestoreControl = $($(this.windowControls).div({ class: 'window-icon-bg' })).div({ class: 'window-icon window-max-restore' }).on(EventType.CLICK, () => {
S
SteVen Batten 已提交
331 332 333 334
				this.windowService.isMaximized().then((maximized) => {
					if (maximized) {
						return this.windowService.unmaximizeWindow();
					}
B
Benjamin Pasero 已提交
335 336

					return this.windowService.maximizeWindow();
S
SteVen Batten 已提交
337
				}).then(null, errors.onUnexpectedError);
338
			});
R
Ryan Adolf 已提交
339

B
Benjamin Pasero 已提交
340
			// Close
S
SteVen Batten 已提交
341
			$($(this.windowControls).div({ class: 'window-icon-bg window-close-bg' })).div({ class: 'window-icon window-close' }).on(EventType.CLICK, () => {
R
Ryan Adolf 已提交
342
				this.windowService.closeWindow().then(null, errors.onUnexpectedError);
343
			});
344

S
SteVen Batten 已提交
345 346 347
			// Resizer
			this.resizer = $(this.titleContainer).div({ class: 'resizer' });

B
Benjamin Pasero 已提交
348
			const isMaximized = this.windowService.getConfiguration().maximized ? true : false;
349
			this.onDidChangeMaximized(isMaximized);
350
			this.windowService.onDidChangeMaximize(this.onDidChangeMaximized, this);
351
		}
R
Ryan Adolf 已提交
352

353 354
		// Since the title area is used to drag the window, we do not want to steal focus from the
		// currently active element. So we restore focus after a timeout back to where it was.
355
		this.titleContainer.on([EventType.MOUSE_DOWN], () => {
356 357 358 359 360 361 362 363
			const active = document.activeElement;
			setTimeout(() => {
				if (active instanceof HTMLElement) {
					active.focus();
				}
			}, 0 /* need a timeout because we are in capture phase */);
		}, void 0, true /* use capture to know the currently active element properly */);

364
		return this.titleContainer.getHTMLElement();
B
Benjamin Pasero 已提交
365 366
	}

367
	private onDidChangeMaximized(maximized: boolean) {
S
SteVen Batten 已提交
368 369 370 371 372 373 374 375
		if (this.maxRestoreControl) {
			if (maximized) {
				this.maxRestoreControl.removeClass('window-maximize');
				this.maxRestoreControl.addClass('window-unmaximize');
			} else {
				this.maxRestoreControl.removeClass('window-unmaximize');
				this.maxRestoreControl.addClass('window-maximize');
			}
S
SteVen Batten 已提交
376
		}
S
SteVen Batten 已提交
377

S
SteVen Batten 已提交
378 379 380 381 382 383
		if (this.resizer) {
			if (maximized) {
				this.resizer.hide();
			} else {
				this.resizer.show();
			}
S
SteVen Batten 已提交
384
		}
385 386
	}

B
Benjamin Pasero 已提交
387 388 389 390
	protected updateStyles(): void {
		super.updateStyles();

		// Part container
391
		if (this.titleContainer) {
S
SteVen Batten 已提交
392 393 394 395 396 397
			if (this.isInactive) {
				this.titleContainer.addClass('inactive');
			} else {
				this.titleContainer.removeClass('inactive');
			}

B
Benjamin Pasero 已提交
398 399 400
			const titleBackground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND);
			this.titleContainer.style('background-color', titleBackground);
			if (Color.fromHex(titleBackground).isLighter()) {
S
SteVen Batten 已提交
401 402 403 404
				this.titleContainer.addClass('light');
			} else {
				this.titleContainer.removeClass('light');
			}
405

B
Benjamin Pasero 已提交
406 407 408
			const titleForeground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_FOREGROUND : TITLE_BAR_ACTIVE_FOREGROUND);
			this.titleContainer.style('color', titleForeground);

409
			const titleBorder = this.getColor(TITLE_BAR_BORDER);
410
			this.titleContainer.style('border-bottom', titleBorder ? `1px solid ${titleBorder}` : null);
411
		}
B
Benjamin Pasero 已提交
412 413
	}

414
	private onTitleDoubleclick(): void {
415
		this.windowService.onWindowTitleDoubleClick().then(null, errors.onUnexpectedError);
416 417
	}

B
Benjamin Pasero 已提交
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
	private onContextMenu(e: MouseEvent): void {

		// Find target anchor
		const event = new StandardMouseEvent(e);
		const anchor = { x: event.posx, y: event.posy };

		// Show menu
		const actions = this.getContextMenuActions();
		if (actions.length) {
			this.contextMenuService.showContextMenu({
				getAnchor: () => anchor,
				getActions: () => TPromise.as(actions),
				onHide: () => actions.forEach(a => a.dispose())
			});
		}
	}

	private getContextMenuActions(): IAction[] {
		const actions: IAction[] = [];

		if (this.representedFileName) {
			const segments = this.representedFileName.split(paths.sep);
			for (let i = segments.length; i > 0; i--) {
441 442 443 444 445 446 447 448 449
				const isFile = (i === segments.length);

				let pathOffset = i;
				if (!isFile) {
					pathOffset++; // for segments which are not the file name we want to open the folder
				}

				const path = segments.slice(0, pathOffset).join(paths.sep);

B
Benjamin Pasero 已提交
450
				let label: string;
451
				if (!isFile) {
I
isidor 已提交
452
					label = getBaseLabel(paths.dirname(path));
B
Benjamin Pasero 已提交
453
				} else {
I
isidor 已提交
454
					label = getBaseLabel(path);
455 456 457
				}

				actions.push(new ShowItemInFolderAction(path, label || paths.sep, this.windowsService));
B
Benjamin Pasero 已提交
458 459 460 461 462 463
			}
		}

		return actions;
	}

B
Benjamin Pasero 已提交
464
	setTitle(title: string): void {
B
Benjamin Pasero 已提交
465 466 467 468 469 470 471 472 473 474 475 476

		// Always set the native window title to identify us properly to the OS
		window.document.title = title;

		// Apply if we can
		if (this.title) {
			this.title.text(title);
		} else {
			this.pendingTitle = title;
		}
	}

S
SteVen Batten 已提交
477 478
	private updateLayout(dimension: Dimension) {
		// Store initital title sizing if we need to prevent zooming
479
		if (typeof this.initialSizing.titleFontSize !== 'number') {
S
SteVen Batten 已提交
480
			this.initialSizing.titleFontSize = parseInt(this.title.getComputedStyle().fontSize, 10);
481 482 483
		}

		if (typeof this.initialSizing.titlebarHeight !== 'number') {
S
SteVen Batten 已提交
484
			this.initialSizing.titlebarHeight = parseInt(this.title.getComputedStyle().height, 10);
B
Benjamin Pasero 已提交
485
		}
486

S
SteVen Batten 已提交
487 488 489 490 491 492 493 494
		// Only prevent zooming behavior on macOS or when the menubar is not visible
		if (isMacintosh || this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden') {
			// To prevent zooming we need to adjust the font size with the zoom factor
			const newHeight = this.initialSizing.titlebarHeight / getZoomFactor();
			this.title.style({
				fontSize: `${this.initialSizing.titleFontSize / getZoomFactor()}px`,
				'line-height': `${newHeight}px`
			});
495

S
SteVen Batten 已提交
496 497 498 499 500
			// Windows/Linux specific layout
			if (isWindows || isLinux) {
				if (typeof this.initialSizing.controlsWidth !== 'number') {
					this.initialSizing.controlsWidth = parseInt(this.windowControls.getComputedStyle().width, 10);
				}
501

S
SteVen Batten 已提交
502 503 504
				if (typeof this.initialSizing.appIconWidth !== 'number') {
					this.initialSizing.appIconWidth = parseInt(this.appIcon.getComputedStyle().width, 10);
				}
505

S
SteVen Batten 已提交
506 507 508
				if (typeof this.initialSizing.appIconSize !== 'number') {
					this.initialSizing.appIconSize = parseInt(this.appIcon.getComputedStyle().backgroundSize, 10);
				}
509

S
SteVen Batten 已提交
510 511 512 513 514 515 516 517 518 519 520 521
				const currentAppIconHeight = parseInt(this.appIcon.getComputedStyle().height, 10);
				const newControlsWidth = this.initialSizing.controlsWidth / getZoomFactor();
				const newAppIconWidth = this.initialSizing.appIconWidth / getZoomFactor();
				const newAppIconSize = this.initialSizing.appIconSize / getZoomFactor();

				// Adjust app icon mimic menubar
				this.appIcon.style({
					'width': `${newAppIconWidth}px`,
					'background-size': `${newAppIconSize}px`,
					'padding-top': `${(newHeight - currentAppIconHeight) / 2.0}px`,
					'padding-bottom': `${(newHeight - currentAppIconHeight) / 2.0}px`
				});
522

S
SteVen Batten 已提交
523 524 525 526
				// Adjust windows controls
				this.windowControls.style({
					'width': `${newControlsWidth}px`
				});
527
			}
S
SteVen Batten 已提交
528 529 530 531 532 533
		} else {
			// We need to undo zoom prevention
			this.title.style({
				fontSize: null,
				'line-height': null
			});
534

535
			this.appIcon.style({
S
SteVen Batten 已提交
536 537 538 539
				'width': null,
				'background-size': null,
				'padding-top': null,
				'padding-bottom': null
540 541 542
			});

			this.windowControls.style({
S
SteVen Batten 已提交
543
				'width': null
544
			});
S
SteVen Batten 已提交
545
		}
546

S
SteVen Batten 已提交
547 548 549
		if (this.menubarPart) {
			const menubarDimension = new Dimension(undefined, dimension.height);
			this.menubarPart.layout(menubarDimension);
550 551 552
		}
	}

B
Benjamin Pasero 已提交
553
	layout(dimension: Dimension): Dimension[] {
S
SteVen Batten 已提交
554
		this.updateLayout(dimension);
B
Benjamin Pasero 已提交
555 556 557

		return super.layout(dimension);
	}
B
Benjamin Pasero 已提交
558 559 560 561
}

class ShowItemInFolderAction extends Action {

562 563
	constructor(private path: string, label: string, private windowsService: IWindowsService) {
		super('showItemInFolder.action.id', label);
B
Benjamin Pasero 已提交
564 565
	}

B
Benjamin Pasero 已提交
566
	run(): TPromise<void> {
B
Benjamin Pasero 已提交
567 568
		return this.windowsService.showItemInFolder(this.path);
	}
R
Ryan Adolf 已提交
569
}
S
SteVen Batten 已提交
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589

registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
	const titlebarActiveFg = theme.getColor(TITLE_BAR_ACTIVE_FOREGROUND);
	if (titlebarActiveFg) {
		collector.addRule(`
		.monaco-workbench > .part.titlebar > .window-controls-container .window-icon {
			background-color: ${titlebarActiveFg};
		}
		`);
	}

	const titlebarInactiveFg = theme.getColor(TITLE_BAR_INACTIVE_FOREGROUND);
	if (titlebarInactiveFg) {
		collector.addRule(`
		.monaco-workbench > .part.titlebar.inactive > .window-controls-container .window-icon {
				background-color: ${titlebarInactiveFg};
			}
		`);
	}
});