From 3aab8b192a27dfc5a3725b75e5fd9038d1502cbc Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 14 Apr 2022 14:54:06 -0700 Subject: [PATCH 01/10] add api to native host service --- src/vs/platform/native/common/native.ts | 2 ++ .../native/electron-main/nativeHostMainService.ts | 10 ++++++++++ src/vs/platform/windows/electron-main/window.ts | 4 ++++ .../test/electron-browser/workbenchTestServices.ts | 1 + 4 files changed, 17 insertions(+) diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index f0d57b347d54a..7b0921a01a974 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -71,6 +71,8 @@ export interface ICommonNativeHostService { unmaximizeWindow(): Promise; minimizeWindow(): Promise; + updateTitleBarOverlay(backgroundColor: string, foregroundColor: string): Promise; + setMinimumSize(width: number | undefined, height: number | undefined): Promise; saveWindowSplash(splash: IPartsSplash): Promise; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index ac91aa1472781..31f6e4a3ed48d 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -211,6 +211,16 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } } + async updateTitleBarOverlay(windowId: number | undefined, backgroundColor: string, foregroundColor: string): Promise { + const window = this.windowById(windowId); + if (window?.win) { + window.win.setTitleBarOverlay({ + color: backgroundColor, + symbolColor: foregroundColor + }); + } + } + async focusWindow(windowId: number | undefined, options?: { windowId?: number; force?: boolean }): Promise { if (options && typeof options.windowId === 'number') { windowId = options.windowId; diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index aca98a776dbd1..0e0151c7c6918 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -245,6 +245,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { if (!isMacintosh) { options.frame = false; } + + if (isWindows) { + options.titleBarOverlay = true; + } } // Create the browser window diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index ba5b81b1d9748..7686c767f1459 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -203,6 +203,7 @@ export class TestNativeHostService implements INativeHostService { async maximizeWindow(): Promise { } async unmaximizeWindow(): Promise { } async minimizeWindow(): Promise { } + async updateTitleBarOverlay(backgroundColor: string, foregroundColor: string): Promise { } async setMinimumSize(width: number | undefined, height: number | undefined): Promise { } async saveWindowSplash(value: IPartsSplash): Promise { } async focusWindow(options?: { windowId?: number | undefined } | undefined): Promise { } From 624bcf3e5da7ad298ed6a3a5b7cb6de22d30c77d Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 15 Apr 2022 17:37:32 -0700 Subject: [PATCH 02/10] get working on windows --- .../electron-main/nativeHostMainService.ts | 10 +- .../platform/windows/electron-main/window.ts | 4 +- .../parts/titlebar/media/titlebarpart.css | 182 ++++++++++-------- .../browser/parts/titlebar/menubarControl.ts | 4 - .../browser/parts/titlebar/titlebarPart.ts | 48 +++-- .../parts/titlebar/titlebarPart.ts | 55 +++--- 6 files changed, 167 insertions(+), 136 deletions(-) diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 31f6e4a3ed48d..f5b9a25464884 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -214,10 +214,12 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async updateTitleBarOverlay(windowId: number | undefined, backgroundColor: string, foregroundColor: string): Promise { const window = this.windowById(windowId); if (window?.win) { - window.win.setTitleBarOverlay({ - color: backgroundColor, - symbolColor: foregroundColor - }); + try { + window.win.setTitleBarOverlay({ + color: backgroundColor, + symbolColor: foregroundColor + }); + } catch { } } } diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 0e0151c7c6918..721cfb75026d5 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -247,7 +247,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { } if (isWindows) { - options.titleBarOverlay = true; + options.titleBarOverlay = { + height: 30 + }; } } diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 2471bcee7f837..a8ded4c777739 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -3,26 +3,61 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + /* Root Element */ .monaco-workbench .part.titlebar > .titlebar-container { box-sizing: border-box; - width: 100%; - padding: 0 70px; overflow: hidden; flex-shrink: 0; align-items: center; justify-content: center; user-select: none; -webkit-user-select: none; - zoom: 1; /* prevent zooming */ - line-height: 22px; - height: 22px; display: flex; + transform-origin: 0 0; + height: 100%; + width: 100%; } -.monaco-workbench .part.titlebar > .titlebar-container { - transform-origin: 0 0; +/* Account for zooming */ +.monaco-workbench .part.titlebar > .titlebar-container.counter-zoom { + height: calc(100% * var(--zoom-factor)); + width: calc(100% * var(--zoom-factor)); + transform: scale(calc(1.0 / var(--zoom-factor))); +} + +/* Platform specific root element */ +.monaco-workbench.mac .part.titlebar > .titlebar-container { + line-height: 22px; + padding: 0 70px; +} + +.monaco-workbench.web .part.titlebar > .titlebar-container, +.monaco-workbench.windows .part.titlebar > .titlebar-container, +.monaco-workbench.linux .part.titlebar > .titlebar-container { + padding: 0; + height: 30px; + line-height: 30px; + justify-content: left; + overflow: visible; +} +.monaco-workbench.windows .part.titlebar > .titlebar-container, +.monaco-workbench.linux .part.titlebar > .titlebar-container { + padding-right: calc((100vw - env(titlebar-area-width, calc(100vw - 138px / var(--zoom-factor))))); +} +.monaco-workbench.windows .part.titlebar > .titlebar-container.counter-zoom, +.monaco-workbench.linux .part.titlebar > .titlebar-container.counter-zoom { + padding-right: calc(var(--zoom-factor) * (100vw - env(titlebar-area-width, calc(100vw - 138px / var(--zoom-factor))))); +} + +.monaco-workbench.web .part.titlebar > .titlebar-container { + padding-right: calc((var(--zoom-factor) * (100vw - env(titlebar-area-width, 100vw)))); +} + +.monaco-workbench.web .part.titlebar > .titlebar-container.counter-zoom { + padding-right: calc(((100vw - env(titlebar-area-width, 100vw)))); } +/* Draggable region */ .monaco-workbench .part.titlebar > .titlebar-container > .titlebar-drag-region { top: 0; left: 0; @@ -33,6 +68,7 @@ -webkit-app-region: drag; } +/* Window title text */ .monaco-workbench .part.titlebar > .titlebar-container > .window-title { flex: 0 1 auto; font-size: 12px; @@ -41,18 +77,6 @@ text-overflow: ellipsis; margin-left: auto; margin-right: auto; - zoom: 1; /* prevent zooming */ -} - -/* Windows/Linux: Rules for custom title (icon, window controls) */ -.monaco-workbench.web .part.titlebar > .titlebar-container, -.monaco-workbench.windows .part.titlebar > .titlebar-container, -.monaco-workbench.linux .part.titlebar > .titlebar-container { - padding: 0; - height: 30px; - line-height: 30px; - justify-content: left; - overflow: visible; } .monaco-workbench.web .part.titlebar > .titlebar-container > .window-title, @@ -61,15 +85,17 @@ cursor: default; } +.monaco-workbench.linux .part.titlebar > .titlebar-container > .window-title { + font-size: inherit; /* see #55435 */ +} + +/* Menubar */ .monaco-workbench .part.titlebar > .titlebar-container > .menubar { /* move menubar above drag region as negative z-index on drag region cause greyscale AA */ z-index: 2500; } -.monaco-workbench.linux .part.titlebar > .titlebar-container > .window-title { - font-size: inherit; -} - +/* Resizer */ .monaco-workbench.windows .part.titlebar > .titlebar-container > .resizer, .monaco-workbench.linux .part.titlebar > .titlebar-container > .resizer { -webkit-app-region: no-drag; @@ -84,6 +110,7 @@ display: none; } +/* App Icon */ .monaco-workbench .part.titlebar > .titlebar-container > .window-appicon { width: 35px; height: 100%; @@ -99,6 +126,14 @@ background-size: 16px; } +.monaco-workbench .part.titlebar > .titlebar-container > .window-appicon.codicon { + line-height: 30px; +} + +.monaco-workbench.fullscreen .part.titlebar > .titlebar-container > .window-appicon { + display: none; +} + .monaco-workbench .part.titlebar > .titlebar-container .window-appicon > .home-bar-icon-badge { position: absolute; right: 9px; @@ -115,71 +150,29 @@ border-left: 1px solid transparent; } -.monaco-workbench .part.titlebar > .titlebar-container > .window-appicon.codicon { - line-height: 30px; -} - -.monaco-workbench.fullscreen .part.titlebar > .titlebar-container > .window-appicon { - display: none; -} - -.monaco-workbench .part.titlebar > .titlebar-container > .window-controls-container { +/* Window Controls (Minimize, Max/Restore, Close) */ +.monaco-workbench .part.titlebar > .window-controls-container { display: flex; flex-grow: 0; flex-shrink: 0; text-align: center; - position: relative; + position: absolute; + top: 0; + right: 0; z-index: 3000; -webkit-app-region: no-drag; - height: 100%; - min-width: 138px; - margin-left: auto; -} - -.monaco-workbench .part.titlebar > .titlebar-container > .window-controls-container.show-layout-control { - min-width: 160px; -} - -.monaco-workbench.web .part.titlebar > .titlebar-container > .window-controls-container.show-layout-control { - min-width: 28px; - padding-right: 8px; -} - -.monaco-workbench.mac:not(.web) .part.titlebar > .titlebar-container > .window-controls-container { - position: absolute; - right: 8px; - min-width: 28px; - display: none; -} - -.monaco-workbench.mac:not(.web) .part.titlebar > .titlebar-container > .window-controls-container.show-layout-control { - display: flex; + height: 30px; + width: 138px; + zoom: calc(1 / var(--zoom-factor)); } -.monaco-workbench.fullscreen .part.titlebar > .titlebar-container > .window-controls-container { +.monaco-workbench.fullscreen .part.titlebar > .window-controls-container { display: none; background-color: transparent; } -.monaco-workbench .part.titlebar > .titlebar-container > .window-controls-container > .layout-dropdown-container { - padding-right: 2px; - display: none; -} - -.monaco-workbench .part.titlebar > .titlebar-container > .window-controls-container.show-layout-control > .layout-dropdown-container { - display: flex; - justify-content: center; -} - -.monaco-workbench:not(.mac):not(.web) .part.titlebar > .titlebar-container > .window-controls-container.show-layout-control > .layout-dropdown-container { - min-width: 46px; -} - -.monaco-workbench .part.titlebar > .titlebar-container > .window-controls-container.show-layout-control > .layout-dropdown-container .codicon { - color: inherit; -} - -.monaco-workbench .part.titlebar > .titlebar-container > .window-controls-container > .window-icon { +/* Window Control Icons */ +.monaco-workbench .part.titlebar > .window-controls-container > .window-icon { display: inline-block; line-height: 30px; height: 100%; @@ -187,18 +180,47 @@ font-size: 16px; } -.monaco-workbench .part.titlebar > .titlebar-container > .window-controls-container > .window-icon:hover { +.monaco-workbench .part.titlebar > .window-controls-container > .window-icon:hover { background-color: rgba(255, 255, 255, 0.1); } -.monaco-workbench .part.titlebar.light > .titlebar-container > .window-controls-container > .window-icon:hover { +.monaco-workbench .part.titlebar.light > .window-controls-container > .window-icon:hover { background-color: rgba(0, 0, 0, 0.1); } -.monaco-workbench .part.titlebar > .titlebar-container > .window-controls-container > .window-icon.window-close:hover { +.monaco-workbench .part.titlebar > .window-controls-container > .window-icon.window-close:hover { background-color: rgba(232, 17, 35, 0.9); } -.monaco-workbench .part.titlebar > .titlebar-container > .window-controls-container .window-icon.window-close:hover { +.monaco-workbench .part.titlebar > .window-controls-container .window-icon.window-close:hover { color: white; } + +/* Layout Controls */ +.monaco-workbench .part.titlebar > .titlebar-container > .layout-controls-container { + display: none; + padding-right: 2px; + flex-grow: 0; + flex-shrink: 0; + text-align: center; + position: relative; + z-index: 3000; + -webkit-app-region: no-drag; + height: 100%; + margin-left: auto; + min-width: 28px; +} + +.monaco-workbench.mac:not(.web) .part.titlebar > .layout-controls-container { + position: absolute; + right: 8px; +} + +.monaco-workbench .part.titlebar > .titlebar-container > .layout-controls-container.show-layout-control { + display: flex; + justify-content: center; +} + +.monaco-workbench .part.titlebar > .titlebar-container > .layout-controls-container .codicon { + color: inherit; +} diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index fd8c91724bfb6..88e69dc8ceb70 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -921,10 +921,6 @@ export class CustomMenubarControl extends MenubarControl { } layout(dimension: Dimension) { - if (this.container) { - this.container.style.height = `${dimension.height}px`; - } - this.menubar?.update(this.getMenuBarOptions()); } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 52771febbf50f..6967c49007e81 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -25,7 +25,7 @@ import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform' import { URI } from 'vs/base/common/uri'; import { Color } from 'vs/base/common/color'; import { trim } from 'vs/base/common/strings'; -import { EventType, EventHelper, Dimension, isAncestor, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame, prepend } from 'vs/base/browser/dom'; +import { EventType, EventHelper, Dimension, isAncestor, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame, prepend, IDomNodePagePosition } from 'vs/base/browser/dom'; import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { template } from 'vs/base/common/labels'; @@ -57,7 +57,7 @@ export class TitlebarPart extends Part implements ITitleService { readonly minimumWidth: number = 0; readonly maximumWidth: number = Number.POSITIVE_INFINITY; - get minimumHeight(): number { return 30 / (this.currentMenubarVisibility === 'hidden' ? getZoomFactor() : 1); } + get minimumHeight(): number { return 30 / (this.currentMenubarVisibility === 'hidden' || getZoomFactor() < 1 ? getZoomFactor() : 1); } get maximumHeight(): number { return this.minimumHeight; } //#endregion @@ -74,7 +74,7 @@ export class TitlebarPart extends Part implements ITitleService { protected appIcon: HTMLElement | undefined; private appIconBadge: HTMLElement | undefined; protected menubar?: HTMLElement; - protected windowControls: HTMLElement | undefined; + protected layoutControls: HTMLElement | undefined; private layoutToolbar: ToolBar | undefined; protected lastLayoutDimensions: Dimension | undefined; private titleBarStyle: 'native' | 'custom'; @@ -150,8 +150,8 @@ export class TitlebarPart extends Part implements ITitleService { } } - if (this.titleBarStyle !== 'native' && this.windowControls && event.affectsConfiguration('workbench.layoutControl.enabled')) { - this.windowControls.classList.toggle('show-layout-control', this.layoutControlEnabled); + if (this.titleBarStyle !== 'native' && this.layoutControls && event.affectsConfiguration('workbench.layoutControl.enabled')) { + this.layoutControls.classList.toggle('show-layout-control', this.layoutControlEnabled); } } @@ -404,21 +404,20 @@ export class TitlebarPart extends Part implements ITitleService { } if (this.titleBarStyle !== 'native') { - this.windowControls = append(this.rootContainer, $('div.window-controls-container')); - this.windowControls.classList.toggle('show-layout-control', this.layoutControlEnabled); + this.layoutControls = append(this.rootContainer, $('div.layout-controls-container')); + this.layoutControls.classList.toggle('show-layout-control', this.layoutControlEnabled); - const layoutDropdownContainer = append(this.windowControls, $('div.layout-dropdown-container')); - this.layoutToolbar = new ToolBar(layoutDropdownContainer, this.contextMenuService, { + this.layoutToolbar = new ToolBar(this.layoutControls, this.contextMenuService, { actionViewItemProvider: action => { return createActionViewItem(this.instantiationService, action); }, allowContextMenu: true }); - this._register(addDisposableListener(layoutDropdownContainer, EventType.CONTEXT_MENU, e => { + this._register(addDisposableListener(this.layoutControls, EventType.CONTEXT_MENU, e => { EventHelper.stop(e); - this.onLayoutControlContextMenu(e, layoutDropdownContainer); + this.onLayoutControlContextMenu(e, this.layoutControls!); })); @@ -573,6 +572,16 @@ export class TitlebarPart extends Part implements ITitleService { this.title.style.transform = 'translate(-50%, 0)'; } + protected getTitlebarAreaRect(): IDomNodePagePosition { + const nav = navigator as { windowControlsOverlay?: { getTitlebarAreaRect: () => DOMRect } }; + + if (typeof nav.windowControlsOverlay === undefined || typeof nav.windowControlsOverlay!.getTitlebarAreaRect === undefined) { + this.element.getBoundingClientRect(); + } + + return nav.windowControlsOverlay!.getTitlebarAreaRect(); + } + protected get currentMenubarVisibility(): MenuBarVisibility { return getMenuBarVisibility(this.configurationService); } @@ -585,16 +594,13 @@ export class TitlebarPart extends Part implements ITitleService { this.lastLayoutDimensions = dimension; if (getTitleBarStyle(this.configurationService) === 'custom') { - // Only prevent zooming behavior on macOS or when the menubar is not visible - if ((!isWeb && isMacintosh) || this.currentMenubarVisibility === 'hidden') { - this.rootContainer.style.height = `${100.0 * getZoomFactor()}%`; - this.rootContainer.style.width = `${100.0 * getZoomFactor()}%`; - this.rootContainer.style.transform = `scale(${1 / getZoomFactor()})`; - } else { - this.rootContainer.style.height = '100%'; - this.rootContainer.style.width = '100%'; - this.rootContainer.style.transform = ''; - } + // Prevent zooming behavior if any of the following conditions are met: + // 1. Native macOS + // 2. Menubar is hidden + // 3. Shrinking below the window control size (zoom < 1) + const zoomFactor = getZoomFactor(); + this.element.style.setProperty('--zoom-factor', zoomFactor.toString()); + this.rootContainer.classList.toggle('counter-zoom', zoomFactor < 1 || (!isWeb && isMacintosh) || this.currentMenubarVisibility === 'hidden'); runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter()); diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 0afcfd8ade753..d15505662b008 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getZoomFactor } from 'vs/base/browser/browser'; -import { $, addDisposableListener, append, Dimension, EventType, hide, prepend, runAtThisOrScheduleAtNextAnimationFrame, show } from 'vs/base/browser/dom'; +import { $, addDisposableListener, append, EventType, hide, IDomNodePagePosition, prepend, 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'; @@ -30,6 +30,7 @@ export class TitlebarPart extends BrowserTitleBarPart { private maxRestoreControl: HTMLElement | undefined; private dragRegion: HTMLElement | undefined; private resizer: HTMLElement | undefined; + private windowControls: HTMLElement | undefined; private getMacTitlebarSize() { const osVersion = this.environmentService.os.release; @@ -133,11 +134,12 @@ export class TitlebarPart extends BrowserTitleBarPart { protected override adjustTitleMarginToCenter(): void { if (this.customMenubar && this.menubar) { + const titleBarRect = this.getTitlebarAreaRect(); const leftMarker = (this.appIcon ? this.appIcon.clientWidth : 0) + this.menubar.clientWidth + 10; - const rightMarker = this.element.clientWidth - (this.windowControls ? this.windowControls.clientWidth : 0) - 10; + const rightMarker = titleBarRect.left + titleBarRect.width - (this.layoutControls ? this.layoutControls.clientWidth : 0) - 10; // Not enough space to center the titlebar within window, - // Center between menu and window controls + // Center between left and right controls if (leftMarker > (this.element.clientWidth - this.title.clientWidth) / 2 || rightMarker < (this.element.clientWidth + this.title.clientWidth) / 2) { this.title.style.position = ''; @@ -150,7 +152,23 @@ export class TitlebarPart extends BrowserTitleBarPart { 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)`; + this.title.style.maxWidth = `calc(100vw - ${2 * ((this.layoutControls?.clientWidth || 70) + 10)}px)`; + } + + protected override getTitlebarAreaRect(): IDomNodePagePosition { + if (this.windowControls) { + const boundingRect = this.element.getBoundingClientRect(); + const controlsBoundingRect = this.windowControls.getBoundingClientRect(); + return { + height: boundingRect.height, + width: boundingRect.width - controlsBoundingRect.width, + left: boundingRect.left, + top: boundingRect.top + }; + } + + return super.getTitlebarAreaRect(); + } protected override installMenubar(): void { @@ -186,7 +204,10 @@ export class TitlebarPart extends BrowserTitleBarPart { this.dragRegion = prepend(this.rootContainer, $('div.titlebar-drag-region')); // Window Controls (Native Windows/Linux) - if (!isMacintosh && this.windowControls) { + const hasWindowControlsOverlay = typeof (navigator as any).windowControlsOverlay !== 'undefined'; + if (!isMacintosh && getTitleBarStyle(this.configurationService) !== 'native' && !hasWindowControlsOverlay) { + 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 => { @@ -220,26 +241,8 @@ export class TitlebarPart extends BrowserTitleBarPart { return ret; } - override updateLayout(dimension: Dimension): void { - this.lastLayoutDimensions = dimension; - - if (getTitleBarStyle(this.configurationService) === 'custom') { - if (isMacintosh || this.currentMenubarVisibility === 'hidden') { - this.rootContainer.style.height = `${100.0 * getZoomFactor()}%`; - this.rootContainer.style.width = `${100.0 * getZoomFactor()}%`; - this.rootContainer.style.transform = `scale(${1 / getZoomFactor()})`; - } else { - this.rootContainer.style.height = `100%`; - this.rootContainer.style.width = `100%`; - this.rootContainer.style.transform = ''; - } - - runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter()); - - if (this.customMenubar) { - const menubarDimension = new Dimension(0, dimension.height); - this.customMenubar.layout(menubarDimension); - } - } + override updateStyles(): void { + super.updateStyles(); + this.nativeHostService.updateTitleBarOverlay(this.element.style.backgroundColor, this.element.style.color); } } From 06300869ccf20a37a4ee08616dba79b2875d1a76 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 22 Apr 2022 09:32:47 -0700 Subject: [PATCH 03/10] switching machines --- .../platform/windows/electron-main/window.ts | 6 +- .../parts/titlebar/media/titlebarpart.css | 103 +++++++++--------- .../browser/parts/titlebar/titlebarPart.ts | 15 ++- .../parts/titlebar/titlebarPart.ts | 8 +- .../electron-sandbox/contextmenuService.ts | 14 ++- 5 files changed, 86 insertions(+), 60 deletions(-) diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index e77413de960dc..a8a04969ad8b4 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -248,9 +248,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { } if (isWindows) { - options.titleBarOverlay = { - height: 30 - }; + // options.titleBarOverlay = { + // height: 30 + // }; } } diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index a8ded4c777739..34f45d396952f 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -3,8 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - /* Root Element */ -.monaco-workbench .part.titlebar > .titlebar-container { +/* Part Element */ +.monaco-workbench .part.titlebar { + display: flex; + flex-direction: row; +} + +/* Root Container */ +.monaco-workbench .part.titlebar>.titlebar-container { box-sizing: border-box; overflow: hidden; flex-shrink: 0; @@ -19,46 +25,46 @@ } /* Account for zooming */ -.monaco-workbench .part.titlebar > .titlebar-container.counter-zoom { - height: calc(100% * var(--zoom-factor)); - width: calc(100% * var(--zoom-factor)); - transform: scale(calc(1.0 / var(--zoom-factor))); +.monaco-workbench .part.titlebar>.titlebar-container.counter-zoom { + zoom: calc(1.0 / var(--zoom-factor)); } /* Platform specific root element */ -.monaco-workbench.mac .part.titlebar > .titlebar-container { +.monaco-workbench.mac .part.titlebar>.titlebar-container { line-height: 22px; padding: 0 70px; } -.monaco-workbench.web .part.titlebar > .titlebar-container, -.monaco-workbench.windows .part.titlebar > .titlebar-container, -.monaco-workbench.linux .part.titlebar > .titlebar-container { +.monaco-workbench.web .part.titlebar>.titlebar-container, +.monaco-workbench.windows .part.titlebar>.titlebar-container, +.monaco-workbench.linux .part.titlebar>.titlebar-container { padding: 0; + flex-shrink: 1; + flex-grow: 1; height: 30px; line-height: 30px; justify-content: left; - overflow: visible; } -.monaco-workbench.windows .part.titlebar > .titlebar-container, + +/* .monaco-workbench.windows .part.titlebar > .titlebar-container, .monaco-workbench.linux .part.titlebar > .titlebar-container { - padding-right: calc((100vw - env(titlebar-area-width, calc(100vw - 138px / var(--zoom-factor))))); + margin-right: calc((100vw - env(titlebar-area-width, calc(100vw - 138px / var(--zoom-factor))))); } .monaco-workbench.windows .part.titlebar > .titlebar-container.counter-zoom, .monaco-workbench.linux .part.titlebar > .titlebar-container.counter-zoom { - padding-right: calc(var(--zoom-factor) * (100vw - env(titlebar-area-width, calc(100vw - 138px / var(--zoom-factor))))); + margin-right: calc(var(--zoom-factor) * (100vw - env(titlebar-area-width, calc(100vw - 138px / var(--zoom-factor))))); } .monaco-workbench.web .part.titlebar > .titlebar-container { - padding-right: calc((var(--zoom-factor) * (100vw - env(titlebar-area-width, 100vw)))); + margin-right: calc((var(--zoom-factor) * (100vw - env(titlebar-area-width, 100vw)))); } .monaco-workbench.web .part.titlebar > .titlebar-container.counter-zoom { - padding-right: calc(((100vw - env(titlebar-area-width, 100vw)))); -} + margin-right: calc(((100vw - env(titlebar-area-width, 100vw)))); +} */ /* Draggable region */ -.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-drag-region { +.monaco-workbench .part.titlebar>.titlebar-container>.titlebar-drag-region { top: 0; left: 0; display: block; @@ -69,7 +75,7 @@ } /* Window title text */ -.monaco-workbench .part.titlebar > .titlebar-container > .window-title { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title { flex: 0 1 auto; font-size: 12px; overflow: hidden; @@ -79,25 +85,26 @@ margin-right: auto; } -.monaco-workbench.web .part.titlebar > .titlebar-container > .window-title, -.monaco-workbench.windows .part.titlebar > .titlebar-container > .window-title, -.monaco-workbench.linux .part.titlebar > .titlebar-container > .window-title { +.monaco-workbench.web .part.titlebar>.titlebar-container>.window-title, +.monaco-workbench.windows .part.titlebar>.titlebar-container>.window-title, +.monaco-workbench.linux .part.titlebar>.titlebar-container>.window-title { cursor: default; } -.monaco-workbench.linux .part.titlebar > .titlebar-container > .window-title { - font-size: inherit; /* see #55435 */ +.monaco-workbench.linux .part.titlebar>.titlebar-container>.window-title { + font-size: inherit; + /* see #55435 */ } /* Menubar */ -.monaco-workbench .part.titlebar > .titlebar-container > .menubar { +.monaco-workbench .part.titlebar>.titlebar-container>.menubar { /* move menubar above drag region as negative z-index on drag region cause greyscale AA */ z-index: 2500; } /* Resizer */ -.monaco-workbench.windows .part.titlebar > .titlebar-container > .resizer, -.monaco-workbench.linux .part.titlebar > .titlebar-container > .resizer { +.monaco-workbench.windows .part.titlebar>.titlebar-container>.resizer, +.monaco-workbench.linux .part.titlebar>.titlebar-container>.resizer { -webkit-app-region: no-drag; position: absolute; top: 0; @@ -105,13 +112,13 @@ height: 4px; } -.monaco-workbench.windows.fullscreen .part.titlebar > .titlebar-container > .resizer, -.monaco-workbench.linux.fullscreen .part.titlebar > .titlebar-container > .resizer { +.monaco-workbench.windows.fullscreen .part.titlebar>.titlebar-container>.resizer, +.monaco-workbench.linux.fullscreen .part.titlebar>.titlebar-container>.resizer { display: none; } /* App Icon */ -.monaco-workbench .part.titlebar > .titlebar-container > .window-appicon { +.monaco-workbench .part.titlebar>.titlebar-container>.window-appicon { width: 35px; height: 100%; position: relative; @@ -119,28 +126,29 @@ flex-shrink: 0; } -.monaco-workbench .part.titlebar > .titlebar-container > .window-appicon:not(.codicon) { +.monaco-workbench .part.titlebar>.titlebar-container>.window-appicon:not(.codicon) { background-image: url('../../../media/code-icon.svg'); background-repeat: no-repeat; background-position: center center; background-size: 16px; } -.monaco-workbench .part.titlebar > .titlebar-container > .window-appicon.codicon { +.monaco-workbench .part.titlebar>.titlebar-container>.window-appicon.codicon { line-height: 30px; } -.monaco-workbench.fullscreen .part.titlebar > .titlebar-container > .window-appicon { +.monaco-workbench.fullscreen .part.titlebar>.titlebar-container>.window-appicon { display: none; } -.monaco-workbench .part.titlebar > .titlebar-container .window-appicon > .home-bar-icon-badge { +.monaco-workbench .part.titlebar>.titlebar-container .window-appicon>.home-bar-icon-badge { position: absolute; right: 9px; bottom: 6px; width: 8px; height: 8px; - z-index: 1; /* on top of home indicator */ + z-index: 1; + /* on top of home indicator */ background-image: url('../../../media/code-icon.svg'); background-repeat: no-repeat; background-position: center center; @@ -151,14 +159,11 @@ } /* Window Controls (Minimize, Max/Restore, Close) */ -.monaco-workbench .part.titlebar > .window-controls-container { +.monaco-workbench .part.titlebar>.window-controls-container { display: flex; flex-grow: 0; flex-shrink: 0; text-align: center; - position: absolute; - top: 0; - right: 0; z-index: 3000; -webkit-app-region: no-drag; height: 30px; @@ -166,13 +171,13 @@ zoom: calc(1 / var(--zoom-factor)); } -.monaco-workbench.fullscreen .part.titlebar > .window-controls-container { +.monaco-workbench.fullscreen .part.titlebar>.window-controls-container { display: none; background-color: transparent; } /* Window Control Icons */ -.monaco-workbench .part.titlebar > .window-controls-container > .window-icon { +.monaco-workbench .part.titlebar>.window-controls-container>.window-icon { display: inline-block; line-height: 30px; height: 100%; @@ -180,24 +185,24 @@ font-size: 16px; } -.monaco-workbench .part.titlebar > .window-controls-container > .window-icon:hover { +.monaco-workbench .part.titlebar>.window-controls-container>.window-icon:hover { background-color: rgba(255, 255, 255, 0.1); } -.monaco-workbench .part.titlebar.light > .window-controls-container > .window-icon:hover { +.monaco-workbench .part.titlebar.light>.window-controls-container>.window-icon:hover { background-color: rgba(0, 0, 0, 0.1); } -.monaco-workbench .part.titlebar > .window-controls-container > .window-icon.window-close:hover { +.monaco-workbench .part.titlebar>.window-controls-container>.window-icon.window-close:hover { background-color: rgba(232, 17, 35, 0.9); } -.monaco-workbench .part.titlebar > .window-controls-container .window-icon.window-close:hover { +.monaco-workbench .part.titlebar>.window-controls-container .window-icon.window-close:hover { color: white; } /* Layout Controls */ -.monaco-workbench .part.titlebar > .titlebar-container > .layout-controls-container { +.monaco-workbench .part.titlebar>.titlebar-container>.layout-controls-container { display: none; padding-right: 2px; flex-grow: 0; @@ -211,16 +216,16 @@ min-width: 28px; } -.monaco-workbench.mac:not(.web) .part.titlebar > .layout-controls-container { +.monaco-workbench.mac:not(.web) .part.titlebar>.layout-controls-container { position: absolute; right: 8px; } -.monaco-workbench .part.titlebar > .titlebar-container > .layout-controls-container.show-layout-control { +.monaco-workbench .part.titlebar>.titlebar-container>.layout-controls-container.show-layout-control { display: flex; justify-content: center; } -.monaco-workbench .part.titlebar > .titlebar-container > .layout-controls-container .codicon { +.monaco-workbench .part.titlebar>.titlebar-container>.layout-controls-container .codicon { color: inherit; } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 6967c49007e81..c64db110f5f33 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -576,10 +576,21 @@ export class TitlebarPart extends Part implements ITitleService { const nav = navigator as { windowControlsOverlay?: { getTitlebarAreaRect: () => DOMRect } }; if (typeof nav.windowControlsOverlay === undefined || typeof nav.windowControlsOverlay!.getTitlebarAreaRect === undefined) { - this.element.getBoundingClientRect(); + this.rootContainer.getBoundingClientRect(); } - return nav.windowControlsOverlay!.getTitlebarAreaRect(); + const nativeTitlebarRect = nav.windowControlsOverlay!.getTitlebarAreaRect(); + let scale = 1.0; + if (nativeTitlebarRect.height !== this.rootContainer.clientHeight) { + scale = this.rootContainer.clientHeight / nativeTitlebarRect.height; + } + + return { + height: nativeTitlebarRect.height * scale, + width: nativeTitlebarRect.width * scale, + left: nativeTitlebarRect.left * scale, + top: nativeTitlebarRect.top * scale + }; } protected get currentMenubarVisibility(): MenuBarVisibility { diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index d15505662b008..1630d589a43e8 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -134,14 +134,14 @@ export class TitlebarPart extends BrowserTitleBarPart { protected override adjustTitleMarginToCenter(): void { if (this.customMenubar && this.menubar) { - const titleBarRect = this.getTitlebarAreaRect(); + // const titleBarRect = this.getTitlebarAreaRect(); const leftMarker = (this.appIcon ? this.appIcon.clientWidth : 0) + this.menubar.clientWidth + 10; - const rightMarker = titleBarRect.left + titleBarRect.width - (this.layoutControls ? this.layoutControls.clientWidth : 0) - 10; + const rightMarker = this.rootContainer.clientWidth - (this.layoutControls ? this.layoutControls.clientWidth : 0) - 10; // Not enough space to center the titlebar within window, // Center between left and right controls - if (leftMarker > (this.element.clientWidth - this.title.clientWidth) / 2 || - rightMarker < (this.element.clientWidth + this.title.clientWidth) / 2) { + if (leftMarker > (this.rootContainer.clientWidth + (this.windowControls?.clientWidth ?? 0) - this.title.clientWidth) / 2 || + rightMarker < (this.element.clientWidth + (this.windowControls?.clientWidth ?? 0) + this.title.clientWidth) / 2) { this.title.style.position = ''; this.title.style.left = ''; this.title.style.transform = ''; diff --git a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts index 464e02e1d57aa..e68ab5f8c1ab0 100644 --- a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts @@ -46,7 +46,7 @@ export class ContextMenuService extends Disposable implements IContextMenuServic super(); // Custom context menu: Linux/Windows if custom title is enabled - if (!isMacintosh && getTitleBarStyle(configurationService) === 'custom') { + if (false && getTitleBarStyle(configurationService) === 'custom') { this.impl = new HTMLContextMenuService(telemetryService, notificationService, contextViewService, keybindingService, themeService); } @@ -97,10 +97,20 @@ class NativeContextMenuService extends Disposable implements IContextMenuService let x: number; let y: number; - const zoom = getZoomFactor(); + let zoom = getZoomFactor(); if (dom.isHTMLElement(anchor)) { const elementPosition = dom.getDomNodePagePosition(anchor); + let testElement: HTMLElement | null = anchor; + do { + const elementZoomLevel = (dom.getComputedStyle(testElement) as any).zoom; + if (elementZoomLevel !== null && elementZoomLevel !== undefined && elementZoomLevel !== '1') { + zoom *= elementZoomLevel; + } + + testElement = testElement.parentElement; + } while (testElement !== null && testElement !== document.documentElement); + x = elementPosition.left; y = elementPosition.top + elementPosition.height; From 46ed0a85e60cd021e68572d65c984ec045384b74 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 22 Apr 2022 10:26:14 -0700 Subject: [PATCH 04/10] working on windows+web --- .../platform/windows/electron-main/window.ts | 6 +-- .../parts/titlebar/media/titlebarpart.css | 29 +++++------- .../browser/parts/titlebar/titlebarPart.ts | 36 ++++----------- .../parts/titlebar/titlebarPart.ts | 45 +------------------ .../electron-sandbox/contextmenuService.ts | 2 +- 5 files changed, 26 insertions(+), 92 deletions(-) diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index a8a04969ad8b4..0f826e69d5404 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -248,9 +248,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { } if (isWindows) { - // options.titleBarOverlay = { - // height: 30 - // }; + options.titleBarOverlay = { + height: 31 + }; } } diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 34f45d396952f..a6b3acaa392d6 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -9,6 +9,10 @@ flex-direction: row; } +.monaco-workbench.mac .part.titlebar { + flex-direction: row-reverse; +} + /* Root Container */ .monaco-workbench .part.titlebar>.titlebar-container { box-sizing: border-box; @@ -46,23 +50,6 @@ justify-content: left; } -/* .monaco-workbench.windows .part.titlebar > .titlebar-container, -.monaco-workbench.linux .part.titlebar > .titlebar-container { - margin-right: calc((100vw - env(titlebar-area-width, calc(100vw - 138px / var(--zoom-factor))))); -} -.monaco-workbench.windows .part.titlebar > .titlebar-container.counter-zoom, -.monaco-workbench.linux .part.titlebar > .titlebar-container.counter-zoom { - margin-right: calc(var(--zoom-factor) * (100vw - env(titlebar-area-width, calc(100vw - 138px / var(--zoom-factor))))); -} - -.monaco-workbench.web .part.titlebar > .titlebar-container { - margin-right: calc((var(--zoom-factor) * (100vw - env(titlebar-area-width, 100vw)))); -} - -.monaco-workbench.web .part.titlebar > .titlebar-container.counter-zoom { - margin-right: calc(((100vw - env(titlebar-area-width, 100vw)))); -} */ - /* Draggable region */ .monaco-workbench .part.titlebar>.titlebar-container>.titlebar-drag-region { top: 0; @@ -97,9 +84,10 @@ } /* Menubar */ -.monaco-workbench .part.titlebar>.titlebar-container>.menubar { +.monaco-workbench .part.titlebar > .titlebar-container > .menubar { /* move menubar above drag region as negative z-index on drag region cause greyscale AA */ z-index: 2500; + min-width: 36px; } /* Resizer */ @@ -171,6 +159,11 @@ zoom: calc(1 / var(--zoom-factor)); } +.monaco-workbench.mac .part.titlebar>.window-controls-container { + width: 70px; + height: env(titlebar-area-width, 28px); +} + .monaco-workbench.fullscreen .part.titlebar>.window-controls-container { display: none; background-color: transparent; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index c64db110f5f33..9443773363996 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -25,7 +25,7 @@ import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform' import { URI } from 'vs/base/common/uri'; import { Color } from 'vs/base/common/color'; import { trim } from 'vs/base/common/strings'; -import { EventType, EventHelper, Dimension, isAncestor, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame, prepend, IDomNodePagePosition } from 'vs/base/browser/dom'; +import { EventType, EventHelper, Dimension, isAncestor, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame, prepend } from 'vs/base/browser/dom'; import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { template } from 'vs/base/common/labels'; @@ -68,6 +68,7 @@ export class TitlebarPart extends Part implements ITitleService { declare readonly _serviceBrand: undefined; protected rootContainer!: HTMLElement; + protected windowControls: HTMLElement | undefined; protected title!: HTMLElement; protected customMenubar: CustomMenubarControl | undefined; @@ -439,6 +440,8 @@ export class TitlebarPart extends Part implements ITitleService { updateLayoutMenu(); } + this.windowControls = append(this.element, $('div.window-controls')); + // Context menu on title [EventType.CONTEXT_MENU, EventType.MOUSE_DOWN].forEach(event => { this._register(addDisposableListener(this.title, event, e => { @@ -553,13 +556,13 @@ export class TitlebarPart extends Part implements ITitleService { protected adjustTitleMarginToCenter(): void { if (this.customMenubar && this.menubar) { - const leftMarker = (this.appIcon ? this.appIcon.clientWidth : 0) + this.menubar.clientWidth + 10; - const rightMarker = this.element.clientWidth - 10; + const leftMarker = (this.appIcon?.clientWidth ?? 0) + this.menubar.clientWidth + 10; + const rightMarker = this.rootContainer.clientWidth - (this.layoutControls?.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) { + // Center between left and right controls + if (leftMarker > (this.rootContainer.clientWidth + (this.windowControls?.clientWidth ?? 0) - this.title.clientWidth) / 2 || + rightMarker < (this.rootContainer.clientWidth + (this.windowControls?.clientWidth ?? 0) + this.title.clientWidth) / 2) { this.title.style.position = ''; this.title.style.left = ''; this.title.style.transform = ''; @@ -572,27 +575,6 @@ export class TitlebarPart extends Part implements ITitleService { this.title.style.transform = 'translate(-50%, 0)'; } - protected getTitlebarAreaRect(): IDomNodePagePosition { - const nav = navigator as { windowControlsOverlay?: { getTitlebarAreaRect: () => DOMRect } }; - - if (typeof nav.windowControlsOverlay === undefined || typeof nav.windowControlsOverlay!.getTitlebarAreaRect === undefined) { - this.rootContainer.getBoundingClientRect(); - } - - const nativeTitlebarRect = nav.windowControlsOverlay!.getTitlebarAreaRect(); - let scale = 1.0; - if (nativeTitlebarRect.height !== this.rootContainer.clientHeight) { - scale = this.rootContainer.clientHeight / nativeTitlebarRect.height; - } - - return { - height: nativeTitlebarRect.height * scale, - width: nativeTitlebarRect.width * scale, - left: nativeTitlebarRect.left * scale, - top: nativeTitlebarRect.top * scale - }; - } - protected get currentMenubarVisibility(): MenuBarVisibility { return getMenuBarVisibility(this.configurationService); } diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 1630d589a43e8..1fd362018a1e3 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getZoomFactor } from 'vs/base/browser/browser'; -import { $, addDisposableListener, append, EventType, hide, IDomNodePagePosition, prepend, show } from 'vs/base/browser/dom'; +import { $, addDisposableListener, append, EventType, hide, prepend, 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'; @@ -30,7 +30,6 @@ export class TitlebarPart extends BrowserTitleBarPart { private maxRestoreControl: HTMLElement | undefined; private dragRegion: HTMLElement | undefined; private resizer: HTMLElement | undefined; - private windowControls: HTMLElement | undefined; private getMacTitlebarSize() { const osVersion = this.environmentService.os.release; @@ -132,45 +131,6 @@ export class TitlebarPart extends BrowserTitleBarPart { } } - protected override adjustTitleMarginToCenter(): void { - if (this.customMenubar && this.menubar) { - // const titleBarRect = this.getTitlebarAreaRect(); - const leftMarker = (this.appIcon ? this.appIcon.clientWidth : 0) + this.menubar.clientWidth + 10; - const rightMarker = this.rootContainer.clientWidth - (this.layoutControls ? this.layoutControls.clientWidth : 0) - 10; - - // Not enough space to center the titlebar within window, - // Center between left and right controls - if (leftMarker > (this.rootContainer.clientWidth + (this.windowControls?.clientWidth ?? 0) - this.title.clientWidth) / 2 || - rightMarker < (this.element.clientWidth + (this.windowControls?.clientWidth ?? 0) + 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.layoutControls?.clientWidth || 70) + 10)}px)`; - } - - protected override getTitlebarAreaRect(): IDomNodePagePosition { - if (this.windowControls) { - const boundingRect = this.element.getBoundingClientRect(); - const controlsBoundingRect = this.windowControls.getBoundingClientRect(); - return { - height: boundingRect.height, - width: boundingRect.width - controlsBoundingRect.width, - left: boundingRect.left, - top: boundingRect.top - }; - } - - return super.getTitlebarAreaRect(); - - } - protected override installMenubar(): void { super.installMenubar(); @@ -205,9 +165,8 @@ export class TitlebarPart extends BrowserTitleBarPart { // Window Controls (Native Windows/Linux) const hasWindowControlsOverlay = typeof (navigator as any).windowControlsOverlay !== 'undefined'; + this.windowControls = append(this.element, $('div.window-controls-container')); if (!isMacintosh && getTitleBarStyle(this.configurationService) !== 'native' && !hasWindowControlsOverlay) { - 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 => { diff --git a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts index e68ab5f8c1ab0..c7ea9a706785b 100644 --- a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts @@ -46,7 +46,7 @@ export class ContextMenuService extends Disposable implements IContextMenuServic super(); // Custom context menu: Linux/Windows if custom title is enabled - if (false && getTitleBarStyle(configurationService) === 'custom') { + if (!isMacintosh && getTitleBarStyle(configurationService) === 'custom') { this.impl = new HTMLContextMenuService(telemetryService, notificationService, contextViewService, keybindingService, themeService); } From 23fda1d77e2b625931c20e1cb016417ae0220f1c Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 22 Apr 2022 11:20:11 -0700 Subject: [PATCH 05/10] fix for macos --- .../parts/titlebar/media/titlebarpart.css | 9 ++--- .../browser/parts/titlebar/titlebarPart.ts | 33 +++++++++---------- .../parts/titlebar/titlebarPart.ts | 3 +- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index a6b3acaa392d6..a40b4e09f7133 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -17,13 +17,13 @@ .monaco-workbench .part.titlebar>.titlebar-container { box-sizing: border-box; overflow: hidden; - flex-shrink: 0; + flex-shrink: 1; + flex-grow: 1; align-items: center; justify-content: center; user-select: none; -webkit-user-select: none; display: flex; - transform-origin: 0 0; height: 100%; width: 100%; } @@ -36,15 +36,11 @@ /* Platform specific root element */ .monaco-workbench.mac .part.titlebar>.titlebar-container { line-height: 22px; - padding: 0 70px; } .monaco-workbench.web .part.titlebar>.titlebar-container, .monaco-workbench.windows .part.titlebar>.titlebar-container, .monaco-workbench.linux .part.titlebar>.titlebar-container { - padding: 0; - flex-shrink: 1; - flex-grow: 1; height: 30px; line-height: 30px; justify-content: left; @@ -164,6 +160,7 @@ height: env(titlebar-area-width, 28px); } +.monaco-workbench.web .part.titlebar > .window-controls-container, .monaco-workbench.fullscreen .part.titlebar>.window-controls-container { display: none; background-color: transparent; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 9443773363996..60cc527f6981b 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -197,10 +197,8 @@ export class TitlebarPart extends Part implements ITitleService { this.pendingTitle = title; } - if ((isWeb || isWindows || isLinux) && this.title) { - if (this.lastLayoutDimensions) { - this.updateLayout(this.lastLayoutDimensions); - } + if (this.lastLayoutDimensions) { + this.updateLayout(this.lastLayoutDimensions); } } @@ -440,7 +438,7 @@ export class TitlebarPart extends Part implements ITitleService { updateLayoutMenu(); } - this.windowControls = append(this.element, $('div.window-controls')); + this.windowControls = append(this.element, $('div.window-controls-container')); // Context menu on title [EventType.CONTEXT_MENU, EventType.MOUSE_DOWN].forEach(event => { @@ -555,19 +553,18 @@ export class TitlebarPart extends Part implements ITitleService { } protected adjustTitleMarginToCenter(): void { - if (this.customMenubar && this.menubar) { - const leftMarker = (this.appIcon?.clientWidth ?? 0) + this.menubar.clientWidth + 10; - const rightMarker = this.rootContainer.clientWidth - (this.layoutControls?.clientWidth ?? 0) - 10; - - // Not enough space to center the titlebar within window, - // Center between left and right controls - if (leftMarker > (this.rootContainer.clientWidth + (this.windowControls?.clientWidth ?? 0) - this.title.clientWidth) / 2 || - rightMarker < (this.rootContainer.clientWidth + (this.windowControls?.clientWidth ?? 0) + this.title.clientWidth) / 2) { - this.title.style.position = ''; - this.title.style.left = ''; - this.title.style.transform = ''; - return; - } + const base = isMacintosh ? (this.windowControls?.clientWidth ?? 0) : 0; + const leftMarker = base + (this.appIcon?.clientWidth ?? 0) + (this.menubar?.clientWidth ?? 0) + 10; + const rightMarker = base + this.rootContainer.clientWidth - (this.layoutControls?.clientWidth ?? 0) - 10; + + // Not enough space to center the titlebar within window, + // Center between left and right controls + if (leftMarker > (this.rootContainer.clientWidth + (this.windowControls?.clientWidth ?? 0) - this.title.clientWidth) / 2 || + rightMarker < (this.rootContainer.clientWidth + (this.windowControls?.clientWidth ?? 0) + this.title.clientWidth) / 2) { + this.title.style.position = ''; + this.title.style.left = ''; + this.title.style.transform = ''; + return; } this.title.style.position = 'absolute'; diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 1fd362018a1e3..0491784cc3f51 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -165,8 +165,7 @@ export class TitlebarPart extends BrowserTitleBarPart { // Window Controls (Native Windows/Linux) const hasWindowControlsOverlay = typeof (navigator as any).windowControlsOverlay !== 'undefined'; - this.windowControls = append(this.element, $('div.window-controls-container')); - if (!isMacintosh && getTitleBarStyle(this.configurationService) !== 'native' && !hasWindowControlsOverlay) { + if (!isMacintosh && getTitleBarStyle(this.configurationService) !== 'native' && !hasWindowControlsOverlay && this.windowControls) { // Minimize const minimizeIcon = append(this.windowControls, $('div.window-icon.window-minimize' + Codicon.chromeMinimize.cssSelector)); this._register(addDisposableListener(minimizeIcon, EventType.CLICK, e => { From 2f958b58438cc998d7dc0425863845e7b8f773a2 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 22 Apr 2022 12:12:39 -0700 Subject: [PATCH 06/10] fix initial window control colors --- src/vs/platform/windows/electron-main/window.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 0f826e69d5404..c37d3e10a4a54 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -38,6 +38,7 @@ import { IWindowsMainService, OpenContext } from 'vs/platform/windows/electron-m import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { IWindowState, ICodeWindow, ILoadEvent, WindowMode, WindowError, LoadReason, defaultWindowState } from 'vs/platform/window/electron-main/window'; +import { Color } from 'vs/base/common/color'; export interface IWindowCreationOptions { state: IWindowState; @@ -248,8 +249,15 @@ export class CodeWindow extends Disposable implements ICodeWindow { } if (isWindows) { + // This logic will not perfectly guess the right colors to use on initialization, + // but prefer to keep things simple as it is temporary and not noticeable + const titleBarColor = this.themeMainService.getWindowSplash()?.colorInfo.titleBarBackground ?? this.themeMainService.getBackgroundColor(); + const symbolColor = Color.fromHex(titleBarColor).isDarker() ? '#FFFFFF' : '#000000'; + options.titleBarOverlay = { - height: 31 + height: 29, + color: titleBarColor, + symbolColor }; } } From 6f1bbb67633f7ad09fed1c35b954c49efd4b171b Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 25 Apr 2022 08:59:53 -0700 Subject: [PATCH 07/10] address comments --- .../native/electron-main/nativeHostMainService.ts | 15 +++++++++------ src/vs/platform/windows/electron-main/window.ts | 2 +- .../parts/titlebar/titlebarPart.ts | 7 ++++++- .../electron-sandbox/contextmenuService.ts | 4 ++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 5cb1b55411d65..e76dee55fcac0 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -213,14 +213,17 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async updateTitleBarOverlay(windowId: number | undefined, backgroundColor: string, foregroundColor: string): Promise { + // The API only exists on Windows + if (!isWindows) { + return; + } + const window = this.windowById(windowId); if (window?.win) { - try { - window.win.setTitleBarOverlay({ - color: backgroundColor, - symbolColor: foregroundColor - }); - } catch { } + window.win.setTitleBarOverlay({ + color: backgroundColor, + symbolColor: foregroundColor + }); } } diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index c37d3e10a4a54..5a92e03d5afcb 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -255,7 +255,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { const symbolColor = Color.fromHex(titleBarColor).isDarker() ? '#FFFFFF' : '#000000'; options.titleBarOverlay = { - height: 29, + height: 29, // The smallest size of the title bar on windows accounting for the border on windows 11 color: titleBarColor, symbolColor }; diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 0491784cc3f51..0f29d5015d4e1 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -30,6 +30,7 @@ export class TitlebarPart extends BrowserTitleBarPart { private maxRestoreControl: HTMLElement | undefined; private dragRegion: HTMLElement | undefined; private resizer: HTMLElement | undefined; + private cachedWindowControlStyles: { bgColor: string; fgColor: string } | undefined; private getMacTitlebarSize() { const osVersion = this.environmentService.os.release; @@ -201,6 +202,10 @@ export class TitlebarPart extends BrowserTitleBarPart { override updateStyles(): void { super.updateStyles(); - this.nativeHostService.updateTitleBarOverlay(this.element.style.backgroundColor, this.element.style.color); + if (!this.cachedWindowControlStyles || + this.cachedWindowControlStyles.bgColor !== this.element.style.backgroundColor || + this.cachedWindowControlStyles.fgColor !== this.element.style.color) { + this.nativeHostService.updateTitleBarOverlay(this.element.style.backgroundColor, this.element.style.color); + } } } diff --git a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts index c7ea9a706785b..b491ce7079e13 100644 --- a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts @@ -101,6 +101,10 @@ class NativeContextMenuService extends Disposable implements IContextMenuService if (dom.isHTMLElement(anchor)) { const elementPosition = dom.getDomNodePagePosition(anchor); + // When drawing context menus, we adjust the pixel position for native menus using zoom level + // In areas where zoom is applied to the element or its ancestors, we need to adjust accordingly + // e.g. The title bar has counter zoom behavior meaning it applies the inverse of zoom level. + // Window Zoom Level: 1.5, Title Bar Zoom: 1/1.5, Coordinate Multiplier: 1.5 * 1.0 / 1.5 = 1.0 let testElement: HTMLElement | null = anchor; do { const elementZoomLevel = (dom.getComputedStyle(testElement) as any).zoom; From d9430a2739f2545fbf8e06f15eb2945bc0b37fc0 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 25 Apr 2022 10:24:13 -0700 Subject: [PATCH 08/10] add windows check for calling over to main process --- .../electron-sandbox/parts/titlebar/titlebarPart.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 0f29d5015d4e1..3b1c2153ad568 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -202,10 +202,14 @@ export class TitlebarPart extends BrowserTitleBarPart { override updateStyles(): void { super.updateStyles(); - if (!this.cachedWindowControlStyles || - this.cachedWindowControlStyles.bgColor !== this.element.style.backgroundColor || - this.cachedWindowControlStyles.fgColor !== this.element.style.color) { - this.nativeHostService.updateTitleBarOverlay(this.element.style.backgroundColor, this.element.style.color); + + // WCO styles only supported on Windows currently + if (isWindows) { + if (!this.cachedWindowControlStyles || + this.cachedWindowControlStyles.bgColor !== this.element.style.backgroundColor || + this.cachedWindowControlStyles.fgColor !== this.element.style.color) { + this.nativeHostService.updateTitleBarOverlay(this.element.style.backgroundColor, this.element.style.color); + } } } } From b42403bdc20aeca746624da8b6e37763224acb39 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 26 Apr 2022 08:27:15 +0200 Subject: [PATCH 09/10] :lipstick: --- src/vs/platform/native/electron-main/nativeHostMainService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index e76dee55fcac0..c167d9011a92a 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -213,9 +213,8 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async updateTitleBarOverlay(windowId: number | undefined, backgroundColor: string, foregroundColor: string): Promise { - // The API only exists on Windows if (!isWindows) { - return; + return; // Windows only } const window = this.windowById(windowId); From cbed29b1f5f02583847e2d728b63ef046eb9c0b2 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 2 May 2022 08:32:02 -0700 Subject: [PATCH 10/10] =?UTF-8?q?=F0=9F=92=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parts/titlebar/media/titlebarpart.css | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 3cbb47dffc6e8..088a77c1acdad 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -47,7 +47,7 @@ } /* Draggable region */ -.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-drag-region { +.monaco-workbench .part.titlebar>.titlebar-container>.titlebar-drag-region { top: 0; left: 0; display: block; @@ -57,7 +57,8 @@ -webkit-app-region: drag; } -.monaco-workbench .part.titlebar > .titlebar-container>.title-menu .action-item.quickopen { +/* Command Center */ +.monaco-workbench .part.titlebar>.titlebar-container>.title-menu .action-item.quickopen { color: var(--vscode-input-foreground); border: 1px solid var(--vscode-dropdown-border); height: 20px; @@ -92,7 +93,7 @@ } /* Window title text */ -.monaco-workbench .part.titlebar > .titlebar-container > .window-title { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title { flex: 0 1 auto; font-size: 12px; overflow: hidden; @@ -103,9 +104,9 @@ } /* Windows/Linux: Rules for custom title (icon, window controls) */ -.monaco-workbench.web .part.titlebar > .titlebar-container, -.monaco-workbench.windows .part.titlebar > .titlebar-container, -.monaco-workbench.linux .part.titlebar > .titlebar-container { +.monaco-workbench.web .part.titlebar>.titlebar-container, +.monaco-workbench.windows .part.titlebar>.titlebar-container, +.monaco-workbench.linux .part.titlebar>.titlebar-container { padding: 0; height: 30px; line-height: 30px; @@ -113,13 +114,13 @@ overflow: visible; } -.monaco-workbench.web .part.titlebar > .titlebar-container > .window-title, -.monaco-workbench.windows .part.titlebar > .titlebar-container > .window-title, -.monaco-workbench.linux .part.titlebar > .titlebar-container > .window-title { +.monaco-workbench.web .part.titlebar>.titlebar-container>.window-title, +.monaco-workbench.windows .part.titlebar>.titlebar-container>.window-title, +.monaco-workbench.linux .part.titlebar>.titlebar-container>.window-title { cursor: default; } -.monaco-workbench .part.titlebar > .titlebar-container.enable-title-menu > .window-title { +.monaco-workbench .part.titlebar>.titlebar-container.enable-title-menu>.window-title { display: none; } @@ -129,7 +130,7 @@ } /* Menubar */ -.monaco-workbench .part.titlebar > .titlebar-container > .menubar { +.monaco-workbench .part.titlebar>.titlebar-container>.menubar { /* move menubar above drag region as negative z-index on drag region cause greyscale AA */ z-index: 2500; min-width: 36px; @@ -209,7 +210,7 @@ height: env(titlebar-area-width, 28px); } -.monaco-workbench.web .part.titlebar > .window-controls-container, +.monaco-workbench.web .part.titlebar>.window-controls-container, .monaco-workbench.fullscreen .part.titlebar>.window-controls-container { display: none; background-color: transparent;