Allow custom titlebar on Linux as experiment (microsoft/vscode-internalbacklog#4857) (#237337)

pull/212471/head^2
Benjamin Pasero 2025-01-06 15:37:07 +01:00 committed by GitHub
parent 292d0bd51a
commit b7f437d2a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 81 additions and 12 deletions

View File

@ -83,7 +83,7 @@ import { NativeURLService } from '../../platform/url/common/urlService.js';
import { ElectronURLListener } from '../../platform/url/electron-main/electronUrlListener.js';
import { IWebviewManagerService } from '../../platform/webview/common/webviewManagerService.js';
import { WebviewMainService } from '../../platform/webview/electron-main/webviewMainService.js';
import { isFolderToOpen, isWorkspaceToOpen, IWindowOpenable } from '../../platform/window/common/window.js';
import { isFolderToOpen, isWorkspaceToOpen, IWindowOpenable, TitlebarStyle, overrideDefaultTitlebarStyle } from '../../platform/window/common/window.js';
import { IWindowsMainService, OpenContext } from '../../platform/windows/electron-main/windows.js';
import { ICodeWindow } from '../../platform/window/electron-main/window.js';
import { WindowsMainService } from '../../platform/windows/electron-main/windowsMainService.js';
@ -593,6 +593,14 @@ export class CodeApplication extends Disposable {
// Services
const appInstantiationService = await this.initServices(machineId, sqmId, devDeviceId, sharedProcessReady);
// Linux (stable only): custom title default style override
if (isLinux && this.productService.quality === 'stable') {
const titleBarDefaultStyleOverride = this.stateService.getItem('window.titleBarStyleOverride');
if (titleBarDefaultStyleOverride === TitlebarStyle.CUSTOM || titleBarDefaultStyleOverride === TitlebarStyle.NATIVE) {
overrideDefaultTitlebarStyle(titleBarDefaultStyleOverride);
}
}
// Auth Handler
appInstantiationService.invokeFunction(accessor => accessor.get(IProxyAuthService));
@ -605,7 +613,7 @@ export class CodeApplication extends Disposable {
// Setup Protocol URL Handlers
const initialProtocolUrls = await appInstantiationService.invokeFunction(accessor => this.setupProtocolUrlHandlers(accessor, mainProcessElectronServer));
// Setup vscode-remote-resource protocol handler.
// Setup vscode-remote-resource protocol handler
this.setupManagedRemoteResourceUrlHandler(mainProcessElectronServer);
// Signal phase: ready - before opening first window

View File

@ -113,6 +113,9 @@ export interface ICommonNativeHostService {
*/
focusWindow(options?: INativeHostOptions & { force?: boolean }): Promise<void>;
// Titlebar default style override
overrideDefaultTitlebarStyle(style: 'native' | 'custom' | undefined): Promise<void>;
// Dialogs
showMessageBox(options: MessageBoxOptions & INativeHostOptions): Promise<MessageBoxReturnValue>;
showSaveDialog(options: SaveDialogOptions & INativeHostOptions): Promise<SaveDialogReturnValue>;
@ -143,10 +146,6 @@ export interface ICommonNativeHostService {
hasWSLFeatureInstalled(): Promise<boolean>;
// Screenshots
/**
* Gets a screenshot of the currently active Electron window.
*/
getScreenshot(): Promise<ArrayBufferLike | undefined>;
// Process
@ -199,7 +198,7 @@ export interface ICommonNativeHostService {
loadCertificates(): Promise<string[]>;
findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride?: number): Promise<number>;
// Registry (windows only)
// Registry (Windows only)
windowsGetStringRegKey(hive: 'HKEY_CURRENT_USER' | 'HKEY_LOCAL_MACHINE' | 'HKEY_CLASSES_ROOT' | 'HKEY_USERS' | 'HKEY_CURRENT_CONFIG', path: string, name: string): Promise<string | undefined>;
}

View File

@ -33,7 +33,7 @@ import { IProductService } from '../../product/common/productService.js';
import { IPartsSplash } from '../../theme/common/themeService.js';
import { IThemeMainService } from '../../theme/electron-main/themeMainService.js';
import { defaultWindowState, ICodeWindow } from '../../window/electron-main/window.js';
import { IColorScheme, IOpenedAuxiliaryWindow, IOpenedMainWindow, IOpenEmptyWindowOptions, IOpenWindowOptions, IPoint, IRectangle, IWindowOpenable } from '../../window/common/window.js';
import { IColorScheme, IOpenedAuxiliaryWindow, IOpenedMainWindow, IOpenEmptyWindowOptions, IOpenWindowOptions, IPoint, IRectangle, IWindowOpenable, overrideDefaultTitlebarStyle } from '../../window/common/window.js';
import { defaultBrowserWindowOptions, IWindowsMainService, OpenContext } from '../../windows/electron-main/windows.js';
import { isWorkspaceIdentifier, toWorkspaceIdentifier } from '../../workspace/common/workspace.js';
import { IWorkspacesManagementMainService } from '../../workspaces/electron-main/workspacesManagementMainService.js';
@ -48,6 +48,7 @@ import { IConfigurationService } from '../../configuration/common/configuration.
import { IProxyAuthService } from './auth.js';
import { AuthInfo, Credentials, IRequestService } from '../../request/common/request.js';
import { randomPath } from '../../../base/common/extpath.js';
import { IStateService } from '../../state/node/state.js';
export interface INativeHostMainService extends AddFirstParameterToFunctions<ICommonNativeHostService, Promise<unknown> /* only methods, not events */, number | undefined /* window ID */> { }
@ -70,7 +71,8 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
@IConfigurationService private readonly configurationService: IConfigurationService,
@IRequestService private readonly requestService: IRequestService,
@IProxyAuthService private readonly proxyAuthService: IProxyAuthService,
@IInstantiationService private readonly instantiationService: IInstantiationService
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IStateService private readonly stateService: IStateService
) {
super();
}
@ -324,6 +326,15 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
this.themeMainService.saveWindowSplash(windowId, splash);
}
async overrideDefaultTitlebarStyle(windowId: number | undefined, style: 'native' | 'custom' | undefined): Promise<void> {
if (typeof style === 'string') {
this.stateService.setItem('window.titleBarStyleOverride', style);
} else {
this.stateService.removeItem('window.titleBarStyleOverride');
}
overrideDefaultTitlebarStyle(style);
}
//#endregion
@ -697,6 +708,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
async getScreenshot(windowId: number | undefined, options?: INativeHostOptions): Promise<ArrayBufferLike | undefined> {
const window = this.windowById(options?.targetWindowId, windowId);
const captured = await window?.win?.webContents.capturePage();
return captured?.toJPEG(95);
}

View File

@ -187,10 +187,23 @@ export const enum CustomTitleBarVisibility {
NEVER = 'never',
}
export let titlebarStyleDefaultOverride: TitlebarStyle | undefined = undefined;
export function overrideDefaultTitlebarStyle(style: 'native' | 'custom' | undefined): void {
switch (style) {
case 'native':
titlebarStyleDefaultOverride = TitlebarStyle.NATIVE;
break;
case 'custom':
titlebarStyleDefaultOverride = TitlebarStyle.CUSTOM;
break;
default:
titlebarStyleDefaultOverride = undefined;
}
}
export function hasCustomTitlebar(configurationService: IConfigurationService, titleBarStyle?: TitlebarStyle): boolean {
// Returns if it possible to have a custom title bar in the curren session
// Does not imply that the title bar is visible
return true;
}
@ -198,6 +211,7 @@ export function hasNativeTitlebar(configurationService: IConfigurationService, t
if (!titleBarStyle) {
titleBarStyle = getTitleBarStyle(configurationService);
}
return titleBarStyle === TitlebarStyle.NATIVE;
}
@ -224,6 +238,10 @@ export function getTitleBarStyle(configurationService: IConfigurationService): T
}
}
if (titlebarStyleDefaultOverride) {
return titlebarStyleDefaultOverride;
}
return isLinux && product.quality === 'stable' ? TitlebarStyle.NATIVE : TitlebarStyle.CUSTOM; // default to custom on all OS except Linux stable (for now)
}

View File

@ -234,6 +234,7 @@ import product from '../../platform/product/common/product.js';
'type': 'string',
'enum': ['native', 'custom'],
'default': isLinux && product.quality === 'stable' ? 'native' : 'custom',
'tags': isLinux && product.quality === 'stable' ? ['onExP'] : undefined,
'scope': ConfigurationScope.APPLICATION,
'description': localize('titleBarStyle', "Adjust the appearance of the window title bar to be native by the OS or custom. On Linux and Windows, this setting also affects the application and context menu appearances. Changes require a full restart to apply."),
},

View File

@ -27,6 +27,7 @@ import { IEditorGroupsContainer, IEditorGroupsService } from '../../../services/
import { IEditorService } from '../../../services/editor/common/editorService.js';
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
import { CodeWindow, mainWindow } from '../../../../base/browser/window.js';
import { IProductService } from '../../../../platform/product/common/productService.js';
export class NativeTitlebarPart extends BrowserTitlebarPart {
@ -70,7 +71,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart {
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IContextKeyService contextKeyService: IContextKeyService,
@IHostService hostService: IHostService,
@INativeHostService private readonly nativeHostService: INativeHostService,
@INativeHostService protected readonly nativeHostService: INativeHostService,
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IEditorService editorService: IEditorService,
@IMenuService menuService: IMenuService,
@ -287,9 +288,38 @@ export class MainNativeTitlebarPart extends NativeTitlebarPart {
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IEditorService editorService: IEditorService,
@IMenuService menuService: IMenuService,
@IKeybindingService keybindingService: IKeybindingService
@IKeybindingService keybindingService: IKeybindingService,
@IProductService productService: IProductService
) {
super(Parts.TITLEBAR_PART, mainWindow, 'main', contextMenuService, configurationService, environmentService, instantiationService, themeService, storageService, layoutService, contextKeyService, hostService, nativeHostService, editorGroupService, editorService, menuService, keybindingService);
if (isLinux && productService.quality === 'stable') {
this.handleDefaultTitlebarStyle(); // TODO@bpasero remove me eventually once settled
}
}
private handleDefaultTitlebarStyle(): void {
this.updateDefaultTitlebarStyle();
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('window.titleBarStyle')) {
this.updateDefaultTitlebarStyle();
}
}));
}
private updateDefaultTitlebarStyle(): void {
const titleBarStyle = this.configurationService.inspect('window.titleBarStyle');
let titleBarStyleOverride: 'custom' | undefined;
if (titleBarStyle.applicationValue || titleBarStyle.userValue || titleBarStyle.userLocalValue) {
// configured by user or application: clear override
titleBarStyleOverride = undefined;
} else {
// not configured: set override if experiment is active
titleBarStyleOverride = titleBarStyle.defaultValue === 'native' ? undefined : 'custom';
}
this.nativeHostService.overrideDefaultTitlebarStyle(titleBarStyleOverride);
}
}

View File

@ -162,6 +162,7 @@ export class TestNativeHostService implements INativeHostService {
async windowsGetStringRegKey(hive: 'HKEY_CURRENT_USER' | 'HKEY_LOCAL_MACHINE' | 'HKEY_CLASSES_ROOT' | 'HKEY_USERS' | 'HKEY_CURRENT_CONFIG', path: string, name: string): Promise<string | undefined> { return undefined; }
async profileRenderer(): Promise<any> { throw new Error(); }
async getScreenshot(): Promise<ArrayBufferLike | undefined> { return undefined; }
async overrideDefaultTitlebarStyle(style: 'native' | 'custom' | undefined): Promise<void> { }
}
export class TestExtensionTipsService extends AbstractNativeExtensionTipsService {