vscode/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts
Matt Bierner 0762d23ae7
Build VS Code using TS 4.4 (#127823)
* Build VS Code using TS 4.4

* Remove usages of deprecated `ClientRectList`

* Add any casts for missing `caretRangeFromPoint`

* Add temporary any casts for `zoom` css propery

This non-standard css property no longer exists in lib.dom.d.ts

* MouseWheelEvent -> WheelEvent

* Pick up new TS nightly

Co-authored-by: Alexandru Dima <alexdima@microsoft.com>
2021-07-08 14:27:39 -07:00

264 lines
10 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getZoomFactor } from 'vs/base/browser/browser';
import { $, addDisposableListener, append, Dimension, EventType, hide, prepend, runAtThisOrScheduleAtNextAnimationFrame, show } from 'vs/base/browser/dom';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { ILabelService } from 'vs/platform/label/common/label';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform';
import { IMenuService } from 'vs/platform/actions/common/actions';
import { TitlebarPart as BrowserTitleBarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IProductService } from 'vs/platform/product/common/productService';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { getTitleBarStyle } from 'vs/platform/windows/common/windows';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Codicon } from 'vs/base/common/codicons';
import { NativeMenubarControl } from 'vs/workbench/electron-sandbox/parts/titlebar/menubarControl';
export class TitlebarPart extends BrowserTitleBarPart {
private windowControls: HTMLElement | undefined;
private maxRestoreControl: HTMLElement | undefined;
private dragRegion: HTMLElement | undefined;
private resizer: HTMLElement | undefined;
private getMacTitlebarSize() {
const osVersion = this.environmentService.os.release;
if (parseFloat(osVersion) >= 20) { // Big Sur increases title bar height
return 28;
}
return 22;
}
override get minimumHeight(): number { return isMacintosh ? this.getMacTitlebarSize() / getZoomFactor() : super.minimumHeight; }
override get maximumHeight(): number { return this.minimumHeight; }
protected override readonly environmentService: INativeWorkbenchEnvironmentService;
constructor(
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
@IEditorService editorService: IEditorService,
@INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@ILabelService labelService: ILabelService,
@IStorageService storageService: IStorageService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IMenuService menuService: IMenuService,
@IContextKeyService contextKeyService: IContextKeyService,
@IHostService hostService: IHostService,
@IProductService productService: IProductService,
@INativeHostService private readonly nativeHostService: INativeHostService
) {
super(contextMenuService, configurationService, editorService, environmentService, contextService, instantiationService, themeService, labelService, storageService, layoutService, menuService, contextKeyService, hostService, productService);
this.environmentService = environmentService;
}
private onUpdateAppIconDragBehavior(): void {
const setting = this.configurationService.getValue('window.doubleClickIconToClose');
if (setting && this.appIcon) {
(this.appIcon.style as any)['-webkit-app-region'] = 'no-drag';
} else if (this.appIcon) {
(this.appIcon.style as any)['-webkit-app-region'] = 'drag';
}
}
private onDidChangeWindowMaximized(maximized: boolean): void {
if (this.maxRestoreControl) {
if (maximized) {
this.maxRestoreControl.classList.remove(...Codicon.chromeMaximize.classNamesArray);
this.maxRestoreControl.classList.add(...Codicon.chromeRestore.classNamesArray);
} else {
this.maxRestoreControl.classList.remove(...Codicon.chromeRestore.classNamesArray);
this.maxRestoreControl.classList.add(...Codicon.chromeMaximize.classNamesArray);
}
}
if (this.resizer) {
if (maximized) {
hide(this.resizer);
} else {
show(this.resizer);
}
}
this.adjustTitleMarginToCenter();
}
private onMenubarFocusChanged(focused: boolean): void {
if ((isWindows || isLinux) && this.currentMenubarVisibility !== 'compact' && this.dragRegion) {
if (focused) {
hide(this.dragRegion);
} else {
show(this.dragRegion);
}
}
}
protected override onMenubarVisibilityChanged(visible: boolean): void {
// Hide title when toggling menu bar
if ((isWindows || isLinux) && this.currentMenubarVisibility === 'toggle' && visible) {
// Hack to fix issue #52522 with layered webkit-app-region elements appearing under cursor
if (this.dragRegion) {
hide(this.dragRegion);
setTimeout(() => show(this.dragRegion!), 50);
}
}
super.onMenubarVisibilityChanged(visible);
}
protected override onConfigurationChanged(event: IConfigurationChangeEvent): void {
super.onConfigurationChanged(event);
if (event.affectsConfiguration('window.doubleClickIconToClose')) {
if (this.appIcon) {
this.onUpdateAppIconDragBehavior();
}
}
}
protected override adjustTitleMarginToCenter(): void {
if (this.customMenubar && this.menubar) {
const leftMarker = (this.appIcon ? this.appIcon.clientWidth : 0) + this.menubar.clientWidth + 10;
const rightMarker = this.element.clientWidth - (this.windowControls ? this.windowControls.clientWidth : 0) - 10;
// 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) {
this.title.style.position = '';
this.title.style.left = '';
this.title.style.transform = '';
return;
}
}
this.title.style.position = 'absolute';
this.title.style.left = '50%';
this.title.style.transform = 'translate(-50%, 0)';
this.title.style.maxWidth = `calc(100vw - ${2 * ((this.windowControls?.clientWidth || 70) + 10)}px)`;
}
protected override installMenubar(): void {
super.installMenubar();
if (this.menubar) {
return;
}
if (this.customMenubar) {
this._register(this.customMenubar.onFocusStateChange(e => this.onMenubarFocusChanged(e)));
}
}
override createContentArea(parent: HTMLElement): HTMLElement {
const ret = super.createContentArea(parent);
// Native menu controller
if (isMacintosh || getTitleBarStyle(this.configurationService) === 'native') {
this._register(this.instantiationService.createInstance(NativeMenubarControl));
}
// App Icon (Native Windows/Linux)
if (this.appIcon) {
this.onUpdateAppIconDragBehavior();
this._register(addDisposableListener(this.appIcon, EventType.DBLCLICK, (e => {
this.nativeHostService.closeWindow();
})));
}
// Draggable region that we can manipulate for #52522
this.dragRegion = prepend(this.element, $('div.titlebar-drag-region'));
// Window Controls (Native Windows/Linux)
if (!isMacintosh) {
this.windowControls = append(this.element, $('div.window-controls-container'));
// Minimize
const minimizeIcon = append(this.windowControls, $('div.window-icon.window-minimize' + Codicon.chromeMinimize.cssSelector));
this._register(addDisposableListener(minimizeIcon, EventType.CLICK, e => {
this.nativeHostService.minimizeWindow();
}));
// Restore
this.maxRestoreControl = append(this.windowControls, $('div.window-icon.window-max-restore'));
this._register(addDisposableListener(this.maxRestoreControl, EventType.CLICK, async e => {
const maximized = await this.nativeHostService.isMaximized();
if (maximized) {
return this.nativeHostService.unmaximizeWindow();
}
return this.nativeHostService.maximizeWindow();
}));
// Close
const closeIcon = append(this.windowControls, $('div.window-icon.window-close' + Codicon.chromeClose.cssSelector));
this._register(addDisposableListener(closeIcon, EventType.CLICK, e => {
this.nativeHostService.closeWindow();
}));
// Resizer
this.resizer = append(this.element, $('div.resizer'));
this._register(this.layoutService.onDidChangeWindowMaximized(maximized => this.onDidChangeWindowMaximized(maximized)));
this.onDidChangeWindowMaximized(this.layoutService.isWindowMaximized());
}
return ret;
}
override updateLayout(dimension: Dimension): void {
this.lastLayoutDimensions = dimension;
if (getTitleBarStyle(this.configurationService) === 'custom') {
// Only prevent zooming behavior on macOS or when the menubar is not visible
if (isMacintosh || this.currentMenubarVisibility === 'hidden') {
(this.title.style as any).zoom = `${1 / getZoomFactor()}`;
if (isWindows || isLinux) {
if (this.appIcon) {
(this.appIcon.style as any).zoom = `${1 / getZoomFactor()}`;
}
if (this.windowControls) {
(this.windowControls.style as any).zoom = `${1 / getZoomFactor()}`;
}
}
} else {
(this.title.style as any).zoom = '';
if (isWindows || isLinux) {
if (this.appIcon) {
(this.appIcon.style as any).zoom = '';
}
if (this.windowControls) {
(this.windowControls.style as any).zoom = '';
}
}
}
runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter());
if (this.customMenubar) {
const menubarDimension = new Dimension(0, dimension.height);
this.customMenubar.layout(menubarDimension);
}
}
}
}