mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
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:
@@ -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> { }
|
||||
|
||||
Reference in New Issue
Block a user