Adopt WCO for Windows (#147947)

* add api to native host service

* get working on windows

* switching machines

* working on windows+web

* fix for macos

* fix initial window control colors

* address comments

* add windows check for calling over to main process

* 💄

* 💄

Co-authored-by: Benjamin Pasero <benjamin.pasero@gmail.com>
This commit is contained in:
SteVen Batten
2022-05-02 12:02:49 -07:00
committed by GitHub
parent 708cb0c507
commit 78914e7dd3
9 changed files with 211 additions and 175 deletions
+2
View File
@@ -71,6 +71,8 @@ export interface ICommonNativeHostService {
unmaximizeWindow(): Promise<void>;
minimizeWindow(): Promise<void>;
updateTitleBarOverlay(backgroundColor: string, foregroundColor: string): Promise<void>;
setMinimumSize(width: number | undefined, height: number | undefined): Promise<void>;
saveWindowSplash(splash: IPartsSplash): Promise<void>;
@@ -212,6 +212,20 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
}
}
async updateTitleBarOverlay(windowId: number | undefined, backgroundColor: string, foregroundColor: string): Promise<void> {
if (!isWindows) {
return; // Windows only
}
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<void> {
if (options && typeof options.windowId === 'number') {
windowId = options.windowId;
@@ -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;
@@ -246,6 +247,19 @@ export class CodeWindow extends Disposable implements ICodeWindow {
if (!isMacintosh) {
options.frame = false;
}
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: 29, // The smallest size of the title bar on windows accounting for the border on windows 11
color: titleBarColor,
symbolColor
};
}
}
// Create the browser window
@@ -3,27 +3,51 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .part.titlebar > .titlebar-container {
/* Part Element */
.monaco-workbench .part.titlebar {
display: flex;
flex-direction: row;
}
.monaco-workbench.mac .part.titlebar {
flex-direction: row-reverse;
}
/* Root Container */
.monaco-workbench .part.titlebar>.titlebar-container {
box-sizing: border-box;
width: 100%;
padding: 0 70px;
overflow: hidden;
flex-shrink: 0;
flex-shrink: 1;
flex-grow: 1;
align-items: center;
justify-content: center;
user-select: none;
-webkit-user-select: none;
zoom: 1; /* prevent zooming */
line-height: 22px;
height: 22px;
display: flex;
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 {
zoom: calc(1.0 / var(--zoom-factor));
}
.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-drag-region {
/* Platform specific root element */
.monaco-workbench.mac .part.titlebar>.titlebar-container {
line-height: 22px;
}
.monaco-workbench.web .part.titlebar>.titlebar-container,
.monaco-workbench.windows .part.titlebar>.titlebar-container,
.monaco-workbench.linux .part.titlebar>.titlebar-container {
height: 30px;
line-height: 30px;
justify-content: left;
}
/* Draggable region */
.monaco-workbench .part.titlebar>.titlebar-container>.titlebar-drag-region {
top: 0;
left: 0;
display: block;
@@ -33,6 +57,7 @@
-webkit-app-region: drag;
}
/* 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);
@@ -67,7 +92,8 @@
margin-left: auto;
}
.monaco-workbench .part.titlebar > .titlebar-container > .window-title {
/* Window title text */
.monaco-workbench .part.titlebar>.titlebar-container>.window-title {
flex: 0 1 auto;
font-size: 12px;
overflow: hidden;
@@ -75,17 +101,12 @@
text-overflow: ellipsis;
margin-left: auto;
margin-right: auto;
zoom: 1; /* prevent zooming */
}
.monaco-workbench .part.titlebar > .titlebar-container.enable-title-menu > .window-title {
display: none;
}
/* 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;
@@ -93,23 +114,31 @@
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 > .menubar {
.monaco-workbench .part.titlebar>.titlebar-container.enable-title-menu>.window-title {
display: none;
}
.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;
min-width: 36px;
}
.monaco-workbench.linux .part.titlebar > .titlebar-container > .window-title {
font-size: inherit;
}
.monaco-workbench.windows .part.titlebar > .titlebar-container > .resizer,
.monaco-workbench.linux .part.titlebar > .titlebar-container > .resizer {
/* 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;
@@ -117,12 +146,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;
}
.monaco-workbench .part.titlebar > .titlebar-container > .window-appicon {
/* App Icon */
.monaco-workbench .part.titlebar>.titlebar-container>.window-appicon {
width: 35px;
height: 100%;
position: relative;
@@ -130,20 +160,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 > .home-bar-icon-badge {
.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;
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;
@@ -153,71 +192,32 @@
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;
z-index: 3000;
-webkit-app-region: no-drag;
height: 100%;
min-width: 138px;
margin-left: auto;
height: 30px;
width: 138px;
zoom: calc(1 / var(--zoom-factor));
}
.monaco-workbench .part.titlebar > .titlebar-container > .window-controls-container.show-layout-control {
min-width: 160px;
.monaco-workbench.mac .part.titlebar>.window-controls-container {
width: 70px;
height: env(titlebar-area-width, 28px);
}
.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;
}
.monaco-workbench.fullscreen .part.titlebar > .titlebar-container > .window-controls-container {
.monaco-workbench.web .part.titlebar>.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%;
@@ -225,18 +225,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;
}
@@ -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());
}
@@ -58,7 +58,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
@@ -69,6 +69,7 @@ export class TitlebarPart extends Part implements ITitleService {
declare readonly _serviceBrand: undefined;
protected rootContainer!: HTMLElement;
protected windowControls: HTMLElement | undefined;
protected readonly titleMenuElement: HTMLElement = $('div.title-menu');
protected title!: HTMLElement;
@@ -76,7 +77,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;
@@ -156,12 +157,12 @@ export class TitlebarPart extends Part implements ITitleService {
}
}
if (event.affectsConfiguration('window.experimental.titleMenu')) {
this.updateTitleMenu();
if (this.titleBarStyle !== 'native' && this.layoutControls && event.affectsConfiguration('workbench.layoutControl.enabled')) {
this.layoutControls.classList.toggle('show-layout-control', this.layoutControlEnabled);
}
if (this.titleBarStyle !== 'native' && this.windowControls && event.affectsConfiguration('workbench.layoutControl.enabled')) {
this.windowControls.classList.toggle('show-layout-control', this.layoutControlEnabled);
if (event.affectsConfiguration('window.experimental.titleMenu')) {
this.updateTitleMenu();
}
}
@@ -206,10 +207,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);
}
this.onDidUpdateTitle.fire();
@@ -464,21 +463,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!);
}));
@@ -500,6 +498,8 @@ export class TitlebarPart extends Part implements ITitleService {
updateLayoutMenu();
}
this.windowControls = append(this.element, $('div.window-controls-container'));
// Context menu on title
[EventType.CONTEXT_MENU, EventType.MOUSE_DOWN].forEach(event => {
this._register(addDisposableListener(this.title, event, e => {
@@ -617,19 +617,18 @@ 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 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 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;
}
// 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';
@@ -649,16 +648,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());
@@ -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, 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';
@@ -31,6 +31,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;
@@ -133,28 +134,6 @@ export class TitlebarPart extends BrowserTitleBarPart {
}
}
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();
@@ -188,7 +167,8 @@ 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) {
// Minimize
const minimizeIcon = append(this.windowControls, $('div.window-icon.window-minimize' + Codicon.chromeMinimize.cssSelector));
this._register(addDisposableListener(minimizeIcon, EventType.CLICK, e => {
@@ -222,25 +202,15 @@ export class TitlebarPart extends BrowserTitleBarPart {
return ret;
}
override updateLayout(dimension: Dimension): void {
this.lastLayoutDimensions = dimension;
override updateStyles(): void {
super.updateStyles();
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);
// 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);
}
}
}
@@ -97,10 +97,24 @@ 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);
// 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;
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;
@@ -203,6 +203,7 @@ export class TestNativeHostService implements INativeHostService {
async maximizeWindow(): Promise<void> { }
async unmaximizeWindow(): Promise<void> { }
async minimizeWindow(): Promise<void> { }
async updateTitleBarOverlay(backgroundColor: string, foregroundColor: string): Promise<void> { }
async setMinimumSize(width: number | undefined, height: number | undefined): Promise<void> { }
async saveWindowSplash(value: IPartsSplash): Promise<void> { }
async focusWindow(options?: { windowId?: number | undefined } | undefined): Promise<void> { }