Skip to content
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
106 changes: 61 additions & 45 deletions src/vs/base/browser/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,67 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { $window } from 'vs/base/browser/window';
import { $window, CodeWindow, mainWindow } from 'vs/base/browser/window';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, markAsSingleton } from 'vs/base/common/lifecycle';

class WindowManager {

public static readonly INSTANCE = new WindowManager();
static readonly INSTANCE = new WindowManager();

// --- Zoom Level
private _zoomLevel: number = 0;

public getZoomLevel(): number {
return this._zoomLevel;
private readonly mapWindowIdToZoomLevel = new Map<number, number>();

private readonly _onDidChangeZoomLevel = new Emitter<number>();
readonly onDidChangeZoomLevel = this._onDidChangeZoomLevel.event;

getZoomLevel(targetWindow: Window): number {
return this.mapWindowIdToZoomLevel.get(this.getWindowId(targetWindow)) ?? 0;
}
public setZoomLevel(zoomLevel: number): void {
if (this._zoomLevel === zoomLevel) {
setZoomLevel(zoomLevel: number, targetWindow: Window): void {
if (this.getZoomLevel(targetWindow) === zoomLevel) {
return;
}
this._zoomLevel = zoomLevel;

const targetWindowId = this.getWindowId(targetWindow);
this.mapWindowIdToZoomLevel.set(targetWindowId, zoomLevel);
this._onDidChangeZoomLevel.fire(targetWindowId);
}

// --- Zoom Factor
private _zoomFactor: number = 1;

public getZoomFactor(): number {
return this._zoomFactor;
private readonly mapWindowIdToZoomFactor = new Map<number, number>();

getZoomFactor(targetWindow: Window): number {
return this.mapWindowIdToZoomFactor.get(this.getWindowId(targetWindow)) ?? 1;
}
public setZoomFactor(zoomFactor: number): void {
this._zoomFactor = zoomFactor;
setZoomFactor(zoomFactor: number, targetWindow: Window): void {
this.mapWindowIdToZoomFactor.set(this.getWindowId(targetWindow), zoomFactor);
}

// --- Fullscreen
private _fullscreen: boolean = false;
private readonly _onDidChangeFullscreen = new Emitter<void>();

public readonly onDidChangeFullscreen: Event<void> = this._onDidChangeFullscreen.event;
public setFullscreen(fullscreen: boolean): void {
if (this._fullscreen === fullscreen) {
private readonly _onDidChangeFullscreen = new Emitter<number>();
readonly onDidChangeFullscreen = this._onDidChangeFullscreen.event;

private readonly mapWindowIdToFullScreen = new Map<number, boolean>();

setFullscreen(fullscreen: boolean, targetWindow: Window): void {
if (this.isFullscreen(targetWindow) === fullscreen) {
return;
}

this._fullscreen = fullscreen;
this._onDidChangeFullscreen.fire();
const windowId = this.getWindowId(targetWindow);
this.mapWindowIdToFullScreen.set(windowId, fullscreen);
this._onDidChangeFullscreen.fire(windowId);
}
public isFullscreen(): boolean {
return this._fullscreen;
isFullscreen(targetWindow: Window): boolean {
return !!this.mapWindowIdToFullScreen.get(this.getWindowId(targetWindow));
}

private getWindowId(targetWindow: Window): number {
return (targetWindow as CodeWindow).vscodeWindowId;
}
}

Expand All @@ -58,7 +73,7 @@ class WindowManager {
class DevicePixelRatioMonitor extends Disposable {

private readonly _onDidChange = this._register(new Emitter<void>());
public readonly onDidChange = this._onDidChange.event;
readonly onDidChange = this._onDidChange.event;

private readonly _listener: () => void;
private _mediaQueryList: MediaQueryList | null;
Expand Down Expand Up @@ -86,11 +101,11 @@ class DevicePixelRatioMonitor extends Disposable {
class PixelRatioImpl extends Disposable {

private readonly _onDidChange = this._register(new Emitter<number>());
public readonly onDidChange = this._onDidChange.event;
readonly onDidChange = this._onDidChange.event;

private _value: number;

public get value(): number {
get value(): number {
return this._value;
}

Expand Down Expand Up @@ -131,21 +146,21 @@ class PixelRatioFacade {
/**
* Get the current value.
*/
public get value(): number {
get value(): number {
return this._getOrCreatePixelRatioMonitor().value;
}

/**
* Listen for changes.
*/
public get onDidChange(): Event<number> {
get onDidChange(): Event<number> {
return this._getOrCreatePixelRatioMonitor().onDidChange;
}
}

export function addMatchMediaChangeListener(query: string | MediaQueryList, callback: (this: MediaQueryList, ev: MediaQueryListEvent) => any): void {
export function addMatchMediaChangeListener(targetWindow: Window, query: string | MediaQueryList, callback: (this: MediaQueryList, ev: MediaQueryListEvent) => any): void {
if (typeof query === 'string') {
query = $window.matchMedia(query);
query = targetWindow.matchMedia(query);
}
query.addEventListener('change', callback);
}
Expand All @@ -160,26 +175,27 @@ export function addMatchMediaChangeListener(query: string | MediaQueryList, call
export const PixelRatio = new PixelRatioFacade();

/** A zoom index, e.g. 1, 2, 3 */
export function setZoomLevel(zoomLevel: number): void {
WindowManager.INSTANCE.setZoomLevel(zoomLevel);
export function setZoomLevel(zoomLevel: number, targetWindow: Window): void {
WindowManager.INSTANCE.setZoomLevel(zoomLevel, targetWindow);
}
export function getZoomLevel(): number {
return WindowManager.INSTANCE.getZoomLevel();
export function getZoomLevel(targetWindow: Window): number {
return WindowManager.INSTANCE.getZoomLevel(targetWindow);
}
export const onDidChangeZoomLevel = WindowManager.INSTANCE.onDidChangeZoomLevel;

/** The zoom scale for an index, e.g. 1, 1.2, 1.4 */
export function getZoomFactor(): number {
return WindowManager.INSTANCE.getZoomFactor();
export function getZoomFactor(targetWindow: Window): number {
return WindowManager.INSTANCE.getZoomFactor(targetWindow);
}
export function setZoomFactor(zoomFactor: number): void {
WindowManager.INSTANCE.setZoomFactor(zoomFactor);
export function setZoomFactor(zoomFactor: number, targetWindow: Window): void {
WindowManager.INSTANCE.setZoomFactor(zoomFactor, targetWindow);
}

export function setFullscreen(fullscreen: boolean): void {
WindowManager.INSTANCE.setFullscreen(fullscreen);
export function setFullscreen(fullscreen: boolean, targetWindow: Window): void {
WindowManager.INSTANCE.setFullscreen(fullscreen, targetWindow);
}
export function isFullscreen(): boolean {
return WindowManager.INSTANCE.isFullscreen();
export function isFullscreen(targetWindow: Window): boolean {
return WindowManager.INSTANCE.isFullscreen(targetWindow);
}
export const onDidChangeFullscreen = WindowManager.INSTANCE.onDidChangeFullscreen;

Expand All @@ -194,11 +210,11 @@ export const isElectron = (userAgent.indexOf('Electron/') >= 0);
export const isAndroid = (userAgent.indexOf('Android') >= 0);

let standalone = false;
if ($window.matchMedia) {
const standaloneMatchMedia = $window.matchMedia('(display-mode: standalone) or (display-mode: window-controls-overlay)');
const fullScreenMatchMedia = $window.matchMedia('(display-mode: fullscreen)');
if (typeof mainWindow.matchMedia === 'function') {
const standaloneMatchMedia = mainWindow.matchMedia('(display-mode: standalone) or (display-mode: window-controls-overlay)');
const fullScreenMatchMedia = mainWindow.matchMedia('(display-mode: fullscreen)');
standalone = standaloneMatchMedia.matches;
addMatchMediaChangeListener(standaloneMatchMedia, ({ matches }) => {
addMatchMediaChangeListener(mainWindow, standaloneMatchMedia, ({ matches }) => {
// entering fullscreen would change standaloneMatchMedia.matches to false
// if standalone is true (running as PWA) and entering fullscreen, skip this change
if (standalone && fullScreenMatchMedia.matches) {
Expand Down
2 changes: 1 addition & 1 deletion src/vs/base/browser/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ export let runAtThisOrScheduleAtNextAnimationFrame: (targetWindow: Window, runne
*/
export let scheduleAtNextAnimationFrame: (targetWindow: Window, runner: () => void, priority?: number) => IDisposable;

export function disposableWindowInterval(targetWindow: Window & typeof globalThis, handler: () => void | boolean /* stop interval */ | Promise<unknown>, interval: number, iterations?: number): IDisposable {
export function disposableWindowInterval(targetWindow: Window, handler: () => void | boolean /* stop interval */ | Promise<unknown>, interval: number, iterations?: number): IDisposable {
let iteration = 0;
const timer = targetWindow.setInterval(() => {
iteration++;
Expand Down
3 changes: 2 additions & 1 deletion src/vs/base/browser/ui/menu/menubar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { isMacintosh } from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
import 'vs/css!./menubar';
import * as nls from 'vs/nls';
import { mainWindow } from 'vs/base/browser/window';

const $ = DOM.$;

Expand Down Expand Up @@ -786,7 +787,7 @@ export class MenuBar extends Disposable {
private setUnfocusedState(): void {
if (this.options.visibility === 'toggle' || this.options.visibility === 'hidden') {
this.focusState = MenubarState.HIDDEN;
} else if (this.options.visibility === 'classic' && browser.isFullscreen()) {
} else if (this.options.visibility === 'classic' && browser.isFullscreen(mainWindow)) {
this.focusState = MenubarState.HIDDEN;
} else {
this.focusState = MenubarState.VISIBLE;
Expand Down
3 changes: 2 additions & 1 deletion src/vs/base/browser/ui/scrollbar/scrollableElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ export class MouseWheelClassifier {
}

public acceptStandardWheelEvent(e: StandardWheelEvent): void {
const osZoomFactor = dom.getWindow(e.browserEvent).devicePixelRatio / getZoomFactor();
const targetWindow = dom.getWindow(e.browserEvent);
const osZoomFactor = targetWindow.devicePixelRatio / getZoomFactor(targetWindow);
if (platform.isWindows || platform.isLinux) {
// On Windows and Linux, the incoming delta events are multiplied with the OS zoom factor.
// The OS zoom factor can be reverse engineered by using the device pixel ratio and the configured zoom factor into account.
Expand Down
7 changes: 7 additions & 0 deletions src/vs/code/electron-main/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,13 @@ export class CodeApplication extends Disposable {

validatedIpcMain.on('vscode:reloadWindow', event => event.sender.reload());

validatedIpcMain.handle('vscode:notifyZoomLevel', async (event, zoomLevel: number | undefined) => {
const window = this.windowsMainService?.getWindowById(event.sender.id);
if (window) {
window.notifyZoomLevel(zoomLevel);
}
});

//#endregion
}

Expand Down
6 changes: 3 additions & 3 deletions src/vs/code/electron-sandbox/issue/issueReporterService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export class IssueReporter extends Disposable {

this.setUpTypes();
this.setEventHandlers();
applyZoom(configuration.data.zoomLevel);
applyZoom(configuration.data.zoomLevel, mainWindow);
this.applyStyles(configuration.data.styles);
this.handleExtensionData(configuration.data.enabledExtensions);
this.updateExperimentsInfo(configuration.data.experiments);
Expand Down Expand Up @@ -435,12 +435,12 @@ export class IssueReporter extends Disposable {

// Cmd/Ctrl + zooms in
if (cmdOrCtrlKey && e.keyCode === 187) {
zoomIn();
zoomIn(mainWindow);
}

// Cmd/Ctrl - zooms out
if (cmdOrCtrlKey && e.keyCode === 189) {
zoomOut();
zoomOut(mainWindow);
}

// With latest electron upgrade, cmd+a is no longer propagating correctly for inputs in this window on mac
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,12 +288,12 @@ class ProcessExplorer {

// Cmd/Ctrl + zooms in
if (cmdOrCtrlKey && e.keyCode === 187) {
zoomIn();
zoomIn(mainWindow);
}

// Cmd/Ctrl - zooms out
if (cmdOrCtrlKey && e.keyCode === 189) {
zoomOut();
zoomOut(mainWindow);
}
};
}
Expand Down Expand Up @@ -595,7 +595,7 @@ export function startup(configuration: ProcessExplorerWindowConfiguration): void
const platformClass = configuration.data.platform === 'win32' ? 'windows' : configuration.data.platform === 'linux' ? 'linux' : 'mac';
mainWindow.document.body.classList.add(platformClass); // used by our fonts
createCodiconStyleSheet();
applyZoom(configuration.data.zoomLevel);
applyZoom(configuration.data.zoomLevel, mainWindow);

new ProcessExplorer(configuration.windowId, configuration.data);
}
2 changes: 1 addition & 1 deletion src/vs/editor/standalone/browser/standaloneThemeService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ export class StandaloneThemeService extends Disposable implements IStandaloneThe
this._updateCSS();
}));

addMatchMediaChangeListener('(forced-colors: active)', () => {
addMatchMediaChangeListener(mainWindow, '(forced-colors: active)', () => {
this._onOSSchemeChanged();
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow {
) {
super(configurationService, stateService, environmentMainService);

contents.removeAllListeners('devtools-reload-page'); // remove built in listener as aux windows have no reload

// Try to claim window
this.tryClaimWindow();
}
Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/window/common/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native
colorScheme: IColorScheme;
autoDetectHighContrast?: boolean;
autoDetectColorScheme?: boolean;
isCustomZoomLevel?: boolean;

perfMarks: PerformanceMark[];

Expand Down
3 changes: 3 additions & 0 deletions src/vs/platform/window/electron-main/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ export interface ICodeWindow extends IBaseWindow {

updateTouchBar(items: ISerializableCommandAction[][]): void;

notifyZoomLevel(zoomLevel: number | undefined): void;

serializeWindowState(): IWindowState;
}

Expand Down Expand Up @@ -131,6 +133,7 @@ export interface IWindowState {
x?: number;
y?: number;
mode?: WindowMode;
zoomLevel?: number;
readonly display?: number;
}

Expand Down
34 changes: 24 additions & 10 deletions src/vs/platform/window/electron-sandbox/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,35 @@
*--------------------------------------------------------------------------------------------*/

import { getZoomLevel, setZoomFactor, setZoomLevel } from 'vs/base/browser/browser';
import { getWindows } from 'vs/base/browser/dom';
import { getActiveWindow, getWindows } from 'vs/base/browser/dom';
import { mainWindow } from 'vs/base/browser/window';
import { ISandboxGlobals, ipcRenderer, webFrame } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { zoomLevelToZoomFactor } from 'vs/platform/window/common/window';

export enum ApplyZoomTarget {
ACTIVE_WINDOW = 1,
ALL_WINDOWS
}

/**
* Apply a zoom level to the window. Also sets it in our in-memory
* browser helper so that it can be accessed in non-electron layers.
*/
export function applyZoom(zoomLevel: number): void {
for (const { window } of getWindows()) {
getGlobals(window)?.webFrame?.setZoomLevel(zoomLevel);
export function applyZoom(zoomLevel: number, target: ApplyZoomTarget | Window): void {
const targetWindows: Window[] = [];
if (target === ApplyZoomTarget.ACTIVE_WINDOW) {
targetWindows.push(getActiveWindow());
} else if (target === ApplyZoomTarget.ALL_WINDOWS) {
targetWindows.push(...Array.from(getWindows()).map(({ window }) => window));
} else {
targetWindows.push(target);
}

for (const targetWindow of targetWindows) {
getGlobals(targetWindow)?.webFrame?.setZoomLevel(zoomLevel);
setZoomFactor(zoomLevelToZoomFactor(zoomLevel), targetWindow);
setZoomLevel(zoomLevel, targetWindow);
}
setZoomFactor(zoomLevelToZoomFactor(zoomLevel));
setZoomLevel(zoomLevel);
}

function getGlobals(win: Window): ISandboxGlobals | undefined {
Expand All @@ -36,10 +50,10 @@ function getGlobals(win: Window): ISandboxGlobals | undefined {
return undefined;
}

export function zoomIn(): void {
applyZoom(getZoomLevel() + 1);
export function zoomIn(target: ApplyZoomTarget | Window): void {
applyZoom(getZoomLevel(typeof target === 'number' ? getActiveWindow() : target) + 1, target);
}

export function zoomOut(): void {
applyZoom(getZoomLevel() - 1);
export function zoomOut(target: ApplyZoomTarget | Window): void {
applyZoom(getZoomLevel(typeof target === 'number' ? getActiveWindow() : target) - 1, target);
}
Loading