titlebarPart.ts 19.5 KB
Newer Older
B
Benjamin Pasero 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  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!./media/titlebarpart';
7
import * as resources from 'vs/base/common/resources';
B
Benjamin Pasero 已提交
8
import { Part } from 'vs/workbench/browser/part';
9
import { ITitleService, ITitleProperties } from 'vs/workbench/services/title/common/titleService';
B
Benjamin Pasero 已提交
10
import { getZoomFactor } from 'vs/base/browser/browser';
11
import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/windows/common/windows';
B
Benjamin Pasero 已提交
12 13
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
14
import { IAction } from 'vs/base/common/actions';
15
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
B
Benjamin Pasero 已提交
16
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
17
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
18
import * as nls from 'vs/nls';
B
Benjamin Pasero 已提交
19
import { toResource, Verbosity, SideBySideEditor } from 'vs/workbench/common/editor';
20
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
21
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
M
Martin Aeschlimann 已提交
22
import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
23
import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER, WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
24
import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform';
25
import { URI } from 'vs/base/common/uri';
R
Ryan Adolf 已提交
26
import { Color } from 'vs/base/common/color';
B
Benjamin Pasero 已提交
27
import { trim } from 'vs/base/common/strings';
S
SteVen Batten 已提交
28
import { EventType, EventHelper, Dimension, isAncestor, removeClass, addClass, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame, removeNode } from 'vs/base/browser/dom';
29
import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl';
S
SteVen Batten 已提交
30
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
31
import { template } from 'vs/base/common/labels';
I
isidor 已提交
32
import { ILabelService } from 'vs/platform/label/common/label';
B
Benjamin Pasero 已提交
33
import { Emitter } from 'vs/base/common/event';
B
Benjamin Pasero 已提交
34
import { IStorageService } from 'vs/platform/storage/common/storage';
35
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
36
import { RunOnceScheduler } from 'vs/base/common/async';
37 38 39
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
40
import { IHostService } from 'vs/workbench/services/host/browser/host';
41
import { IProductService } from 'vs/platform/product/common/productService';
42
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
B
Benjamin Pasero 已提交
43

44
export class TitlebarPart extends Part implements ITitleService {
B
Benjamin Pasero 已提交
45

46
	private static readonly NLS_UNSUPPORTED = nls.localize('patchedWindowTitle', "[Unsupported]");
47
	private static readonly NLS_USER_IS_ADMIN = isWindows ? nls.localize('userIsAdmin', "[Administrator]") : nls.localize('userIsSudo', "[Superuser]");
48 49 50
	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
51

52
	//#region IView
53

B
Benjamin Pasero 已提交
54 55
	readonly minimumWidth: number = 0;
	readonly maximumWidth: number = Number.POSITIVE_INFINITY;
S
SteVen Batten 已提交
56 57
	get minimumHeight(): number { return isMacintosh && !isWeb ? 22 / getZoomFactor() : (30 / (this.currentMenubarVisibility === 'hidden' ? getZoomFactor() : 1)); }
	get maximumHeight(): number { return isMacintosh && !isWeb ? 22 / getZoomFactor() : (30 / (this.currentMenubarVisibility === 'hidden' ? getZoomFactor() : 1)); }
B
Benjamin Pasero 已提交
58

59
	//#endregion
60

61
	private _onMenubarVisibilityChange = this._register(new Emitter<boolean>());
B
Benjamin Pasero 已提交
62
	readonly onMenubarVisibilityChange = this._onMenubarVisibilityChange.event;
63

64
	_serviceBrand: undefined;
B
Benjamin Pasero 已提交
65

S
SteVen Batten 已提交
66 67 68 69
	protected title!: HTMLElement;
	protected customMenubar: CustomMenubarControl | undefined;
	protected menubar?: HTMLElement;
	protected lastLayoutDimensions: Dimension | undefined;
70
	private titleBarStyle: 'native' | 'custom';
B
Benjamin Pasero 已提交
71

S
SteVen Batten 已提交
72
	private pendingTitle: string | undefined;
73

S
SteVen Batten 已提交
74
	private isInactive: boolean = false;
B
Benjamin Pasero 已提交
75

76 77
	private readonly properties: ITitleProperties = { isPure: true, isAdmin: false };
	private readonly activeEditorListeners = this._register(new DisposableStore());
78

79
	private readonly titleUpdater = this._register(new RunOnceScheduler(() => this.doUpdateTitle(), 0));
80

81 82
	private contextMenu: IMenu;

83
	constructor(
84
		@IContextMenuService private readonly contextMenuService: IContextMenuService,
S
SteVen Batten 已提交
85
		@IConfigurationService protected readonly configurationService: IConfigurationService,
86
		@IEditorService private readonly editorService: IEditorService,
S
SteVen Batten 已提交
87
		@IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService,
88 89
		@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
I
isidor 已提交
90
		@IThemeService themeService: IThemeService,
91
		@ILabelService private readonly labelService: ILabelService,
92
		@IStorageService storageService: IStorageService,
93 94
		@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
		@IMenuService menuService: IMenuService,
95
		@IContextKeyService contextKeyService: IContextKeyService,
96
		@IHostService private readonly hostService: IHostService,
97
		@IProductService private readonly productService: IProductService,
98
	) {
99
		super(Parts.TITLEBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
100

101 102
		this.contextMenu = this._register(menuService.createMenu(MenuId.TitleBarContext, contextKeyService));

103 104
		this.titleBarStyle = getTitleBarStyle(this.configurationService, this.environmentService);

105 106 107 108
		this.registerListeners();
	}

	private registerListeners(): void {
109
		this._register(this.hostService.onDidChangeFocus(focused => focused ? this.onFocus() : this.onBlur()));
B
Benjamin Pasero 已提交
110 111
		this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChanged(e)));
		this._register(this.editorService.onDidActiveEditorChange(() => this.onActiveEditorChange()));
112 113 114 115
		this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.titleUpdater.schedule()));
		this._register(this.contextService.onDidChangeWorkbenchState(() => this.titleUpdater.schedule()));
		this._register(this.contextService.onDidChangeWorkspaceName(() => this.titleUpdater.schedule()));
		this._register(this.labelService.onDidChangeFormatters(() => this.titleUpdater.schedule()));
116 117
	}

B
Benjamin Pasero 已提交
118 119 120 121 122 123 124 125 126 127
	private onBlur(): void {
		this.isInactive = true;
		this.updateStyles();
	}

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

S
SteVen Batten 已提交
128
	protected onConfigurationChanged(event: IConfigurationChangeEvent): void {
129
		if (event.affectsConfiguration('window.title')) {
130
			this.titleUpdater.schedule();
131
		}
S
SteVen Batten 已提交
132

133 134 135 136 137 138 139
		if (this.titleBarStyle !== 'native') {
			if (event.affectsConfiguration('window.menuBarVisibility')) {
				if (this.currentMenubarVisibility === 'compact') {
					this.uninstallMenubar();
				} else {
					this.installMenubar();
				}
S
SteVen Batten 已提交
140 141
			}
		}
142 143
	}

S
SteVen Batten 已提交
144
	protected onMenubarVisibilityChanged(visible: boolean) {
145
		if (isWeb || isWindows || isLinux) {
S
SteVen Batten 已提交
146
			this.adjustTitleMarginToCenter();
147 148

			this._onMenubarVisibilityChange.fire(visible);
S
SteVen Batten 已提交
149
		}
150 151
	}

B
Benjamin Pasero 已提交
152
	private onActiveEditorChange(): void {
153 154

		// Dispose old listeners
155
		this.activeEditorListeners.clear();
156 157

		// Calculate New Window Title
158
		this.titleUpdater.schedule();
159 160

		// Apply listener for dirty and label changes
B
Benjamin Pasero 已提交
161
		const activeEditor = this.editorService.activeEditor;
B
Benjamin Pasero 已提交
162
		if (activeEditor) {
163 164
			this.activeEditorListeners.add(activeEditor.onDidChangeDirty(() => this.titleUpdater.schedule()));
			this.activeEditorListeners.add(activeEditor.onDidChangeLabel(() => this.titleUpdater.schedule()));
165 166 167
		}
	}

B
Benjamin Pasero 已提交
168 169 170 171 172 173
	private doUpdateTitle(): void {
		const title = this.getWindowTitle();

		// Always set the native window title to identify us properly to the OS
		let nativeTitle = title;
		if (!trim(nativeTitle)) {
174
			nativeTitle = this.productService.nameLong;
B
Benjamin Pasero 已提交
175 176 177 178 179 180 181 182 183
		}
		window.document.title = nativeTitle;

		// Apply custom title if we can
		if (this.title) {
			this.title.innerText = title;
		} else {
			this.pendingTitle = title;
		}
S
SteVen Batten 已提交
184

185
		if ((isWeb || isWindows || isLinux) && this.title) {
S
SteVen Batten 已提交
186 187 188
			if (this.lastLayoutDimensions) {
				this.updateLayout(this.lastLayoutDimensions);
			}
189
		}
B
Benjamin Pasero 已提交
190 191
	}

192 193 194
	private getWindowTitle(): string {
		let title = this.doGetWindowTitle();

195
		if (this.properties.isAdmin) {
196
			title = `${title || this.productService.nameLong} ${TitlebarPart.NLS_USER_IS_ADMIN}`;
197 198 199
		}

		if (!this.properties.isPure) {
200
			title = `${title || this.productService.nameLong} ${TitlebarPart.NLS_UNSUPPORTED}`;
201 202 203
		}

		if (this.environmentService.isExtensionDevelopment) {
204
			title = `${TitlebarPart.NLS_EXTENSION_HOST} - ${title || this.productService.nameLong}`;
205 206 207 208 209
		}

		return title;
	}

B
Benjamin Pasero 已提交
210
	updateProperties(properties: ITitleProperties): void {
211 212 213 214 215 216 217
		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;

218
			this.titleUpdater.schedule();
219 220 221
		}
	}

222 223 224
	/**
	 * Possible template values:
	 *
225 226
	 * {activeEditorLong}: e.g. /Users/Development/myFolder/myFileFolder/myFile.txt
	 * {activeEditorMedium}: e.g. myFolder/myFileFolder/myFile.txt
B
Benjamin Pasero 已提交
227
	 * {activeEditorShort}: e.g. myFile.txt
228 229 230
	 * {activeFolderLong}: e.g. /Users/Development/myFolder/myFileFolder
	 * {activeFolderMedium}: e.g. myFolder/myFileFolder
	 * {activeFolderShort}: e.g. myFileFolder
231
	 * {rootName}: e.g. myFolder1, myFolder2, myFolder3
232
	 * {rootPath}: e.g. /Users/Development
233 234
	 * {folderName}: e.g. myFolder
	 * {folderPath}: e.g. /Users/Development/myFolder
235
	 * {appName}: e.g. VS Code
236
	 * {remoteName}: e.g. SSH
237
	 * {dirty}: indicator
238 239 240
	 * {separator}: conditional separator
	 */
	private doGetWindowTitle(): string {
B
Benjamin Pasero 已提交
241
		const editor = this.editorService.activeEditor;
B
Benjamin Pasero 已提交
242
		const workspace = this.contextService.getWorkspace();
243

B
Benjamin Pasero 已提交
244
		// Compute root
245
		let root: URI | undefined;
246
		if (workspace.configuration) {
247
			root = workspace.configuration;
248 249
		} else if (workspace.folders.length) {
			root = workspace.folders[0].uri;
250 251
		}

B
Benjamin Pasero 已提交
252 253 254
		// Compute active editor folder
		const editorResource = editor ? toResource(editor) : undefined;
		let editorFolderResource = editorResource ? resources.dirname(editorResource) : undefined;
B
Benjamin Pasero 已提交
255
		if (editorFolderResource?.path === '.') {
B
Benjamin Pasero 已提交
256 257 258
			editorFolderResource = undefined;
		}

259 260
		// Compute folder resource
		// Single Root Workspace: always the root single workspace in this case
261
		// Otherwise: root folder of the currently active file if any
262 263 264 265 266 267 268 269 270
		let folder: IWorkspaceFolder | null = null;
		if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
			folder = workspace.folders[0];
		} else {
			const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
			if (resource) {
				folder = this.contextService.getWorkspaceFolder(resource);
			}
		}
271

272
		// Variables
B
Benjamin Pasero 已提交
273 274 275
		const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : '';
		const activeEditorMedium = editor ? editor.getTitle(Verbosity.MEDIUM) : activeEditorShort;
		const activeEditorLong = editor ? editor.getTitle(Verbosity.LONG) : activeEditorMedium;
B
Benjamin Pasero 已提交
276 277 278
		const activeFolderShort = editorFolderResource ? resources.basename(editorFolderResource) : '';
		const activeFolderMedium = editorFolderResource ? this.labelService.getUriLabel(editorFolderResource, { relative: true }) : '';
		const activeFolderLong = editorFolderResource ? this.labelService.getUriLabel(editorFolderResource) : '';
I
isidor 已提交
279
		const rootName = this.labelService.getWorkspaceLabel(workspace);
I
isidor 已提交
280
		const rootPath = root ? this.labelService.getUriLabel(root) : '';
B
Benjamin Pasero 已提交
281
		const folderName = folder ? folder.name : '';
I
isidor 已提交
282
		const folderPath = folder ? this.labelService.getUriLabel(folder.uri) : '';
283
		const dirty = editor?.isDirty() && !editor.isSaving() ? TitlebarPart.TITLE_DIRTY : '';
284
		const appName = this.productService.nameLong;
285
		const remoteName = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority);
286
		const separator = TitlebarPart.TITLE_SEPARATOR;
287
		const titleTemplate = this.configurationService.getValue<string>('window.title');
288

I
isidor 已提交
289
		return template(titleTemplate, {
B
Benjamin Pasero 已提交
290 291 292
			activeEditorShort,
			activeEditorLong,
			activeEditorMedium,
293 294 295
			activeFolderShort,
			activeFolderMedium,
			activeFolderLong,
296 297
			rootName,
			rootPath,
298 299
			folderName,
			folderPath,
300 301
			dirty,
			appName,
302
			remoteName,
303 304 305 306
			separator: { label: separator }
		});
	}

S
SteVen Batten 已提交
307 308 309 310 311 312 313 314 315 316 317 318
	private uninstallMenubar(): void {
		if (this.customMenubar) {
			this.customMenubar.dispose();
			this.customMenubar = undefined;
		}

		if (this.menubar) {
			removeNode(this.menubar);
			this.menubar = undefined;
		}
	}

S
SteVen Batten 已提交
319
	protected installMenubar(): void {
S
SteVen Batten 已提交
320 321 322 323 324
		// If the menubar is already installed, skip
		if (this.menubar) {
			return;
		}

S
SteVen Batten 已提交
325 326 327 328 329 330 331 332 333 334 335
		this.customMenubar = this._register(this.instantiationService.createInstance(CustomMenubarControl));

		this.menubar = this.element.insertBefore($('div.menubar'), this.title);

		this.menubar.setAttribute('role', 'menubar');

		this.customMenubar.create(this.menubar);

		this._register(this.customMenubar.onVisibilityChange(e => this.onMenubarVisibilityChanged(e)));
	}

B
Benjamin Pasero 已提交
336
	createContentArea(parent: HTMLElement): HTMLElement {
337
		this.element = parent;
B
Benjamin Pasero 已提交
338

339
		// Menubar: install a custom menu bar depending on configuration
S
SteVen Batten 已提交
340
		// and when not in activity bar
341
		if (this.titleBarStyle !== 'native'
S
SteVen Batten 已提交
342 343 344
			&& (!isMacintosh || isWeb)
			&& this.currentMenubarVisibility !== 'compact') {
			this.installMenubar();
S
SteVen Batten 已提交
345 346
		}

B
Benjamin Pasero 已提交
347
		// Title
348
		this.title = append(this.element, $('div.window-title'));
B
Benjamin Pasero 已提交
349
		if (this.pendingTitle) {
350
			this.title.innerText = this.pendingTitle;
B
Benjamin Pasero 已提交
351
		} else {
352
			this.titleUpdater.schedule();
B
Benjamin Pasero 已提交
353 354
		}

B
Benjamin Pasero 已提交
355
		// Context menu on title
356 357 358 359
		[EventType.CONTEXT_MENU, EventType.MOUSE_DOWN].forEach(event => {
			this._register(addDisposableListener(this.title, event, e => {
				if (e.type === EventType.CONTEXT_MENU || e.metaKey) {
					EventHelper.stop(e);
B
Benjamin Pasero 已提交
360

361 362 363
					this.onContextMenu(e);
				}
			}));
B
Benjamin Pasero 已提交
364 365
		});

366 367
		// 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.
368
		this._register(addDisposableListener(this.element, EventType.MOUSE_DOWN, e => {
S
SteVen Batten 已提交
369
			if (e.target && this.menubar && isAncestor(e.target as HTMLElement, this.menubar)) {
S
SteVen Batten 已提交
370 371 372
				return;
			}

373 374 375 376 377 378
			const active = document.activeElement;
			setTimeout(() => {
				if (active instanceof HTMLElement) {
					active.focus();
				}
			}, 0 /* need a timeout because we are in capture phase */);
379
		}, true /* use capture to know the currently active element properly */));
380

S
SteVen Batten 已提交
381 382
		this.updateStyles();

383
		return this.element;
B
Benjamin Pasero 已提交
384 385
	}

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

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

397 398 399 400 401 402 403
			const titleBackground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND, (color, theme) => {
				// LCD Rendering Support: the title bar part is a defining its own GPU layer.
				// To benefit from LCD font rendering, we must ensure that we always set an
				// opaque background color. As such, we compute an opaque color given we know
				// the background color is the workbench background.
				return color.isOpaque() ? color : color.makeOpaque(WORKBENCH_BACKGROUND(theme));
			}) || '';
404
			this.element.style.backgroundColor = titleBackground;
M
Matt Bierner 已提交
405
			if (titleBackground && Color.fromHex(titleBackground).isLighter()) {
406
				addClass(this.element, 'light');
S
SteVen Batten 已提交
407
			} else {
408
				removeClass(this.element, 'light');
S
SteVen Batten 已提交
409
			}
410

B
Benjamin Pasero 已提交
411
			const titleForeground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_FOREGROUND : TITLE_BAR_ACTIVE_FOREGROUND);
M
Matt Bierner 已提交
412
			this.element.style.color = titleForeground || '';
B
Benjamin Pasero 已提交
413

414
			const titleBorder = this.getColor(TITLE_BAR_BORDER);
M
Matt Bierner 已提交
415
			this.element.style.borderBottom = titleBorder ? `1px solid ${titleBorder}` : '';
416
		}
B
Benjamin Pasero 已提交
417 418
	}

B
Benjamin Pasero 已提交
419 420 421 422 423 424
	private onContextMenu(e: MouseEvent): void {

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

425
		// Fill in contributed actions
B
Benjamin Pasero 已提交
426
		const actions: IAction[] = [];
427
		const actionsDisposable = createAndFillInContextMenuActions(this.contextMenu, undefined, actions, this.contextMenuService);
B
Benjamin Pasero 已提交
428

429 430 431 432 433 434
		// Show it
		this.contextMenuService.showContextMenu({
			getAnchor: () => anchor,
			getActions: () => actions,
			onHide: () => dispose(actionsDisposable)
		});
B
Benjamin Pasero 已提交
435 436
	}

S
SteVen Batten 已提交
437
	protected adjustTitleMarginToCenter(): void {
S
SteVen Batten 已提交
438
		if (this.customMenubar && this.menubar) {
S
SteVen Batten 已提交
439 440
			const leftMarker = this.menubar.clientWidth + 10;
			const rightMarker = this.element.clientWidth - 10;
441 442 443 444 445

			// Not enough space to center the titlebar within window,
			// Center between menu and window controls
			if (leftMarker > (this.element.clientWidth - this.title.clientWidth) / 2 ||
				rightMarker < (this.element.clientWidth + this.title.clientWidth) / 2) {
M
Matt Bierner 已提交
446 447
				this.title.style.position = '';
				this.title.style.left = '';
M
Matt Bierner 已提交
448
				this.title.style.transform = '';
449 450
				return;
			}
S
SteVen Batten 已提交
451
		}
452 453 454 455

		this.title.style.position = 'absolute';
		this.title.style.left = '50%';
		this.title.style.transform = 'translate(-50%, 0)';
S
SteVen Batten 已提交
456 457
	}

S
SteVen Batten 已提交
458
	protected get currentMenubarVisibility(): MenuBarVisibility {
459
		return getMenuBarVisibility(this.configurationService, this.environmentService);
S
SteVen Batten 已提交
460 461
	}

462
	updateLayout(dimension: Dimension): void {
S
SteVen Batten 已提交
463 464
		this.lastLayoutDimensions = dimension;

B
Benjamin Pasero 已提交
465
		if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') {
466
			// Only prevent zooming behavior on macOS or when the menubar is not visible
S
SteVen Batten 已提交
467
			if ((!isWeb && isMacintosh) || this.currentMenubarVisibility === 'hidden') {
468
				this.title.style.zoom = `${1 / getZoomFactor()}`;
469 470
			} else {
				this.title.style.zoom = null;
471 472
			}

S
SteVen Batten 已提交
473
			runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter());
474

475
			if (this.customMenubar) {
M
Matt Bierner 已提交
476
				const menubarDimension = new Dimension(0, dimension.height);
477
				this.customMenubar.layout(menubarDimension);
478
			}
479
		}
480 481
	}

B
Benjamin Pasero 已提交
482 483
	layout(width: number, height: number): void {
		this.updateLayout(new Dimension(width, height));
484

B
Benjamin Pasero 已提交
485
		super.layoutContents(width, height);
486
	}
B
Benjamin Pasero 已提交
487

488 489 490 491
	toJSON(): object {
		return {
			type: Parts.TITLEBAR_PART
		};
B
Benjamin Pasero 已提交
492
	}
B
Benjamin Pasero 已提交
493 494
}

M
Martin Aeschlimann 已提交
495
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
S
SteVen Batten 已提交
496 497 498
	const titlebarActiveFg = theme.getColor(TITLE_BAR_ACTIVE_FOREGROUND);
	if (titlebarActiveFg) {
		collector.addRule(`
499
		.monaco-workbench .part.titlebar > .window-controls-container .window-icon {
500
			color: ${titlebarActiveFg};
S
SteVen Batten 已提交
501 502 503 504 505 506 507
		}
		`);
	}

	const titlebarInactiveFg = theme.getColor(TITLE_BAR_INACTIVE_FOREGROUND);
	if (titlebarInactiveFg) {
		collector.addRule(`
508
		.monaco-workbench .part.titlebar.inactive > .window-controls-container .window-icon {
509
				color: ${titlebarInactiveFg};
S
SteVen Batten 已提交
510 511 512 513
			}
		`);
	}
});