[MM-58357] Account for monitor scale factor when creating the window from saved bounds (#3045)
This commit is contained in:
parent
39a2d863f1
commit
9bc0270a13
|
@ -179,7 +179,7 @@ export function validateArgs(data: Args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate bounds_info.json
|
// validate bounds_info.json
|
||||||
export function validateBoundsInfo(data: SavedWindowState) {
|
export function validateBoundsInfo(data: SavedWindowState | null) {
|
||||||
return validateAgainstSchema(data, boundsInfoSchema);
|
return validateAgainstSchema(data, boundsInfoSchema);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,7 @@ describe('main/windows/mainWindow', () => {
|
||||||
BrowserWindow.mockImplementation(() => baseWindow);
|
BrowserWindow.mockImplementation(() => baseWindow);
|
||||||
fs.readFileSync.mockImplementation(() => '{"x":400,"y":300,"width":1280,"height":700,"maximized":false,"fullscreen":false}');
|
fs.readFileSync.mockImplementation(() => '{"x":400,"y":300,"width":1280,"height":700,"maximized":false,"fullscreen":false}');
|
||||||
path.join.mockImplementation(() => 'anyfile.txt');
|
path.join.mockImplementation(() => 'anyfile.txt');
|
||||||
screen.getDisplayMatching.mockImplementation(() => ({bounds: {x: 0, y: 0, width: 1920, height: 1080}}));
|
screen.getDisplayMatching.mockImplementation(() => ({scaleFactor: 1, bounds: {x: 0, y: 0, width: 1920, height: 1080}}));
|
||||||
isInsideRectangle.mockReturnValue(true);
|
isInsideRectangle.mockReturnValue(true);
|
||||||
Validator.validateBoundsInfo.mockImplementation((data) => data);
|
Validator.validateBoundsInfo.mockImplementation((data) => data);
|
||||||
ContextMenu.mockImplementation(() => ({
|
ContextMenu.mockImplementation(() => ({
|
||||||
|
@ -129,6 +129,20 @@ describe('main/windows/mainWindow', () => {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should set scaled window size using bounds read from file', () => {
|
||||||
|
screen.getDisplayMatching.mockImplementation(() => ({scaleFactor: 2, bounds: {x: 0, y: 0, width: 1920, height: 1080}}));
|
||||||
|
const mainWindow = new MainWindow();
|
||||||
|
mainWindow.init();
|
||||||
|
expect(BrowserWindow).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
x: 400,
|
||||||
|
y: 300,
|
||||||
|
width: 640,
|
||||||
|
height: 350,
|
||||||
|
maximized: false,
|
||||||
|
fullscreen: false,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
it('should set default window size when failing to read bounds from file', () => {
|
it('should set default window size when failing to read bounds from file', () => {
|
||||||
fs.readFileSync.mockImplementation(() => 'just a bunch of garbage');
|
fs.readFileSync.mockImplementation(() => 'just a bunch of garbage');
|
||||||
const mainWindow = new MainWindow();
|
const mainWindow = new MainWindow();
|
||||||
|
|
|
@ -45,7 +45,7 @@ const ALT_MENU_KEYS = ['Alt+F', 'Alt+E', 'Alt+V', 'Alt+H', 'Alt+W', 'Alt+P'];
|
||||||
export class MainWindow extends EventEmitter {
|
export class MainWindow extends EventEmitter {
|
||||||
private win?: BrowserWindow;
|
private win?: BrowserWindow;
|
||||||
|
|
||||||
private savedWindowState?: SavedWindowState;
|
private savedWindowState?: Partial<SavedWindowState>;
|
||||||
private ready: boolean;
|
private ready: boolean;
|
||||||
private isResizing: boolean;
|
private isResizing: boolean;
|
||||||
private lastEmittedBounds?: Electron.Rectangle;
|
private lastEmittedBounds?: Electron.Rectangle;
|
||||||
|
@ -89,6 +89,7 @@ export class MainWindow extends EventEmitter {
|
||||||
spellcheck: typeof Config.useSpellChecker === 'undefined' ? true : Config.useSpellChecker,
|
spellcheck: typeof Config.useSpellChecker === 'undefined' ? true : Config.useSpellChecker,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
log.debug('main window options', windowOptions);
|
||||||
|
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
windowOptions.icon = path.join(path.resolve(app.getAppPath(), 'assets'), 'linux', 'app_icon.png');
|
windowOptions.icon = path.join(path.resolve(app.getAppPath(), 'assets'), 'linux', 'app_icon.png');
|
||||||
|
@ -245,25 +246,34 @@ export class MainWindow extends EventEmitter {
|
||||||
return os.platform() === 'darwin' || (os.platform() === 'win32' && Utils.isVersionGreaterThanOrEqualTo(os.release(), '6.2'));
|
return os.platform() === 'darwin' || (os.platform() === 'win32' && Utils.isVersionGreaterThanOrEqualTo(os.release(), '6.2'));
|
||||||
};
|
};
|
||||||
|
|
||||||
private getSavedWindowState = () => {
|
private getSavedWindowState = (): Partial<SavedWindowState> => {
|
||||||
let savedWindowState: any;
|
|
||||||
try {
|
try {
|
||||||
savedWindowState = JSON.parse(fs.readFileSync(boundsInfoPath, 'utf-8'));
|
let savedWindowState: SavedWindowState | null = JSON.parse(fs.readFileSync(boundsInfoPath, 'utf-8'));
|
||||||
savedWindowState = Validator.validateBoundsInfo(savedWindowState);
|
savedWindowState = Validator.validateBoundsInfo(savedWindowState);
|
||||||
if (!savedWindowState) {
|
if (!savedWindowState) {
|
||||||
throw new Error('Provided bounds info file does not validate, using defaults instead.');
|
throw new Error('Provided bounds info file does not validate, using defaults instead.');
|
||||||
}
|
}
|
||||||
const matchingScreen = screen.getDisplayMatching(savedWindowState);
|
const matchingScreen = screen.getDisplayMatching(savedWindowState);
|
||||||
|
log.debug('matching screen for main window', matchingScreen);
|
||||||
if (!(matchingScreen && (isInsideRectangle(matchingScreen.bounds, savedWindowState) || savedWindowState.maximized))) {
|
if (!(matchingScreen && (isInsideRectangle(matchingScreen.bounds, savedWindowState) || savedWindowState.maximized))) {
|
||||||
throw new Error('Provided bounds info are outside the bounds of your screen, using defaults instead.');
|
throw new Error('Provided bounds info are outside the bounds of your screen, using defaults instead.');
|
||||||
}
|
}
|
||||||
|
// We check for the monitor's scale factor when we want to set these bounds
|
||||||
|
// This is due to a long running Electron issue: https://github.com/electron/electron/issues/10862
|
||||||
|
return {
|
||||||
|
...savedWindowState,
|
||||||
|
width: Math.floor(savedWindowState.width / matchingScreen.scaleFactor),
|
||||||
|
height: Math.floor(savedWindowState.height / matchingScreen.scaleFactor),
|
||||||
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error(e);
|
log.error(e);
|
||||||
|
|
||||||
// Follow Electron's defaults, except for window dimensions which targets 1024x768 screen resolution.
|
// Follow Electron's defaults, except for window dimensions which targets 1024x768 screen resolution.
|
||||||
savedWindowState = {width: DEFAULT_WINDOW_WIDTH, height: DEFAULT_WINDOW_HEIGHT};
|
return {
|
||||||
|
width: DEFAULT_WINDOW_WIDTH,
|
||||||
|
height: DEFAULT_WINDOW_HEIGHT,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return savedWindowState;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private saveWindowState = (file: string, window: BrowserWindow) => {
|
private saveWindowState = (file: string, window: BrowserWindow) => {
|
||||||
|
@ -273,6 +283,7 @@ export class MainWindow extends EventEmitter {
|
||||||
fullscreen: window.isFullScreen(),
|
fullscreen: window.isFullScreen(),
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
|
log.debug('saving window state', windowState);
|
||||||
fs.writeFileSync(file, JSON.stringify(windowState));
|
fs.writeFileSync(file, JSON.stringify(windowState));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// [Linux] error happens only when the window state is changed before the config dir is created.
|
// [Linux] error happens only when the window state is changed before the config dir is created.
|
||||||
|
|
Loading…
Reference in a new issue