Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update traffic lights on macos when title bar zooms #155906

Merged
merged 5 commits into from
Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/vs/platform/native/common/native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,12 @@ export interface ICommonNativeHostService {
unmaximizeWindow(): Promise<void>;
minimizeWindow(): Promise<void>;

updateTitleBarOverlay(options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise<void>;
/**
* Only supported on Windows and macOS. Updates the window controls to match the title bar size.
*
* @param options `backgroundColor` and `foregroundColor` are only supported on Windows
*/
updateWindowControls(options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise<void>;

setMinimumSize(width: number | undefined, height: number | undefined): Promise<void>;

Expand Down
10 changes: 3 additions & 7 deletions src/vs/platform/native/electron-main/nativeHostMainService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,10 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
}
}

async updateTitleBarOverlay(windowId: number | undefined, options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise<void> {
async updateWindowControls(windowId: number | undefined, options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise<void> {
const window = this.windowById(windowId);
if (window?.win) {
window.win.setTitleBarOverlay({
color: options.backgroundColor?.trim() === '' ? undefined : options.backgroundColor,
symbolColor: options.foregroundColor?.trim() === '' ? undefined : options.foregroundColor,
height: options.height ? options.height - 1 : undefined // account for window border
});
if (window) {
window.updateWindowControls(options);
}
}

Expand Down
1 change: 0 additions & 1 deletion src/vs/platform/state/node/stateService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,5 +167,4 @@ export class StateService implements IStateService {
getItem<T>(key: string, defaultValue?: T): T | undefined {
return this.fileStorage.getItem(key, defaultValue);
}

}
2 changes: 2 additions & 0 deletions src/vs/platform/window/electron-main/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export interface ICodeWindow extends IDisposable {
updateTouchBar(items: ISerializableCommandAction[][]): void;

serializeWindowState(): IWindowState;

updateWindowControls(options: { height?: number; backgroundColor?: string; foregroundColor?: string }): void;
}

export const enum LoadReason {
Expand Down
80 changes: 37 additions & 43 deletions src/vs/platform/windows/electron-main/windowImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { app, BrowserWindow, BrowserWindowConstructorOptions, Display, Event, nativeImage, NativeImage, Point, Rectangle, screen, SegmentedControlSegment, systemPreferences, TouchBar, TouchBarSegmentedControl } from 'electron';
import { app, BrowserWindow, BrowserWindowConstructorOptions, Display, Event, nativeImage, NativeImage, Rectangle, screen, SegmentedControlSegment, systemPreferences, TouchBar, TouchBarSegmentedControl } from 'electron';
import { RunOnceScheduler } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { toErrorMessage } from 'vs/base/common/errorMessage';
Expand Down Expand Up @@ -42,6 +42,7 @@ import { Color } from 'vs/base/common/color';
import { IPolicyService } from 'vs/platform/policy/common/policy';
import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { revive } from 'vs/base/common/marshalling';
import { IStateMainService } from 'vs/platform/state/electron-main/state';
import product from 'vs/platform/product/common/product';

export interface IWindowCreationOptions {
Expand Down Expand Up @@ -83,6 +84,8 @@ const enum ReadyState {

export class CodeWindow extends Disposable implements ICodeWindow {

private static readonly windowControlHeightStateStorageKey = 'windowControlHeight';

//#region Events

private readonly _onWillLoad = this._register(new Emitter<ILoadEvent>());
Expand Down Expand Up @@ -140,9 +143,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
private representedFilename: string | undefined;
private documentEdited: boolean | undefined;

private customTrafficLightPosition: boolean | undefined;
private defaultTrafficLightPosition: Point | undefined;

private readonly whenReadyCallbacks: { (window: ICodeWindow): void }[] = [];

private readonly touchBarGroups: TouchBarSegmentedControl[] = [];
Expand Down Expand Up @@ -172,7 +172,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IProductService private readonly productService: IProductService,
@IProtocolMainService private readonly protocolMainService: IProtocolMainService,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
@IStateMainService private readonly stateMainService: IStateMainService
) {
super();

Expand Down Expand Up @@ -290,11 +291,15 @@ export class CodeWindow extends Disposable implements ICodeWindow {
this._id = this._win.id;

if (isMacintosh && useCustomTitleStyle) {
this.updateTrafficLightPosition(); // adjust traffic light position depending on command center
this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any
}

if (isMacintosh && useCustomTitleStyle) {
this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any
// Update the window controls immediately based on cached values
if ((isWindows || isMacintosh) && useCustomTitleStyle) {
const cachedWindowControlHeight = this.stateMainService.getItem<number>((CodeWindow.windowControlHeightStateStorageKey));
if (cachedWindowControlHeight) {
this.updateWindowControls({ height: cachedWindowControlHeight });
}
}

// Windows Custom System Context Menu
Expand Down Expand Up @@ -401,15 +406,15 @@ export class CodeWindow extends Disposable implements ICodeWindow {
}

setRepresentedFilename(filename: string): void {
if (isMacintosh && !this.customTrafficLightPosition) { // TODO@electron https://github.com/electron/electron/issues/34822
if (isMacintosh) {
this._win.setRepresentedFilename(filename);
} else {
this.representedFilename = filename;
}
}

getRepresentedFilename(): string | undefined {
if (isMacintosh && !this.customTrafficLightPosition) { // TODO@electron https://github.com/electron/electron/issues/34822
if (isMacintosh) {
return this._win.getRepresentedFilename();
}

Expand Down Expand Up @@ -786,9 +791,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
this.setMenuBarVisibility(newMenuBarVisibility);
}

// Traffic Lights
this.updateTrafficLightPosition(e);

// Proxy
let newHttpProxy = (this.configurationService.getValue<string>('http.proxy') || '').trim()
|| (process.env['https_proxy'] || process.env['HTTPS_PROXY'] || process.env['http_proxy'] || process.env['HTTP_PROXY'] || '').trim() // Not standardized.
Expand Down Expand Up @@ -1062,6 +1064,28 @@ export class CodeWindow extends Disposable implements ICodeWindow {
return state;
}

updateWindowControls(options: { height?: number; backgroundColor?: string; foregroundColor?: string }): void {

// Cache the height for speeds lookups on startup
if (options.height) {
this.stateMainService.setItem((CodeWindow.windowControlHeightStateStorageKey), options.height);
}

// Windows: window control overlay (WCO)
if (isWindows) {
this._win.setTitleBarOverlay({
color: options.backgroundColor?.trim() === '' ? undefined : options.backgroundColor,
symbolColor: options.foregroundColor?.trim() === '' ? undefined : options.foregroundColor,
height: options.height ? options.height - 1 : undefined // account for window border
});
}

// macOS: traffic lights
else if (isMacintosh && options.height !== undefined) {
this._win.setTrafficLightPosition({ x: 7, y: (options.height - 15) / 2 }); // 15px is the height of the traffic lights
}
}

private restoreWindowState(state?: IWindowState): [IWindowState, boolean? /* has multiple displays */] {
mark('code/willRestoreCodeWindowState');

Expand Down Expand Up @@ -1346,36 +1370,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
}
}

private updateTrafficLightPosition(e?: IConfigurationChangeEvent): void {
if (!isMacintosh) {
return; // only applies to macOS
}

const commandCenterSettingKey = 'window.commandCenter';
if (e && !e.affectsConfiguration(commandCenterSettingKey)) {
return;
}

const useCustomTitleStyle = getTitleBarStyle(this.configurationService) === 'custom';
if (!useCustomTitleStyle) {
return; // only applies with custom title bar
}

const useCustomTrafficLightPosition = this.configurationService.getValue<boolean>(commandCenterSettingKey);
if (useCustomTrafficLightPosition) {
if (!this.defaultTrafficLightPosition) {
this.defaultTrafficLightPosition = this._win.getTrafficLightPosition(); // remember default to restore later
}
this._win.setTrafficLightPosition({ x: 7, y: 10 });
} else {
if (this.defaultTrafficLightPosition) {
this._win.setTrafficLightPosition(this.defaultTrafficLightPosition);
}
}

this.customTrafficLightPosition = useCustomTrafficLightPosition;
}

handleTitleDoubleClick(): void {

// Respect system settings on mac with regards to title click on windows title
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ suite('WindowsFinder', () => {
handleTitleDoubleClick(): void { throw new Error('Method not implemented.'); }
updateTouchBar(items: UriDto<ICommandAction>[][]): void { throw new Error('Method not implemented.'); }
serializeWindowState(): IWindowState { throw new Error('Method not implemented'); }
updateWindowControls(options: { height?: number | undefined; backgroundColor?: string | undefined; foregroundColor?: string | undefined }): void { throw new Error('Method not implemented.'); }
dispose(): void { }
};
}
Expand Down
11 changes: 6 additions & 5 deletions src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co
import { IStorageService } from 'vs/platform/storage/common/storage';
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform';
import { isMacintosh, isWindows, isLinux, isNative } from 'vs/base/common/platform';
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { TitlebarPart as BrowserTitleBarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
Expand Down Expand Up @@ -222,19 +222,20 @@ export class TitlebarPart extends BrowserTitleBarPart {
if (!this.cachedWindowControlStyles ||
this.cachedWindowControlStyles.bgColor !== this.element.style.backgroundColor ||
this.cachedWindowControlStyles.fgColor !== this.element.style.color) {
this.nativeHostService.updateTitleBarOverlay({ backgroundColor: this.element.style.backgroundColor, foregroundColor: this.element.style.color });
this.nativeHostService.updateWindowControls({ backgroundColor: this.element.style.backgroundColor, foregroundColor: this.element.style.color });
}
}
}

override layout(width: number, height: number): void {
super.layout(width, height);

if (useWindowControlsOverlay(this.configurationService, this.environmentService)) {
const newHeight = Math.trunc(this.element.clientHeight * getZoomFactor());
if (useWindowControlsOverlay(this.configurationService, this.environmentService) ||
(isMacintosh && isNative && getTitleBarStyle(this.configurationService) === 'custom')) {
const newHeight = Math.round(height * getZoomFactor());
if (newHeight !== this.cachedWindowControlHeight) {
this.cachedWindowControlHeight = newHeight;
this.nativeHostService.updateTitleBarOverlay({ height: newHeight });
this.nativeHostService.updateWindowControls({ height: newHeight });
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export class TestNativeHostService implements INativeHostService {
async maximizeWindow(): Promise<void> { }
async unmaximizeWindow(): Promise<void> { }
async minimizeWindow(): Promise<void> { }
async updateTitleBarOverlay(options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise<void> { }
async updateWindowControls(options: { height?: number; 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> { }
Expand Down