Skip to content

Commit de18c49

Browse files
fix(code): restore zoom after external resize (#3571)
Co-authored-by: posthog[bot] <206114724+posthog[bot]@users.noreply.github.com> Co-authored-by: richardsolomou <2622273+richardsolomou@users.noreply.github.com>
1 parent f41d71e commit de18c49

2 files changed

Lines changed: 100 additions & 6 deletions

File tree

apps/code/src/main/zoom.test.ts

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,20 @@ vi.mock("./utils/store", () => ({
2020
import { adjustWindowZoom, restoreWindowZoom, setupWindowZoom } from "./zoom";
2121

2222
class FakeWebContents extends EventEmitter {
23+
public destroyed = false;
24+
public readonly setZoomLevelCalls: number[] = [];
2325
public zoomLevel = 0;
2426

27+
public isDestroyed(): boolean {
28+
return this.destroyed;
29+
}
30+
31+
public getZoomLevel(): number {
32+
return this.zoomLevel;
33+
}
34+
2535
public setZoomLevel(level: number): void {
36+
this.setZoomLevelCalls.push(level);
2637
this.zoomLevel = level;
2738
}
2839
}
@@ -85,6 +96,30 @@ describe("window zoom", () => {
8596
expect(window.webContents.zoomLevel).toBe(0.5);
8697
});
8798

99+
it("restores the current level after an external window resize", () => {
100+
const window = createWindow();
101+
setupWindowZoom(window);
102+
103+
window.webContents.emit("zoom-changed", { preventDefault: vi.fn() }, "in");
104+
vi.runAllTimers();
105+
window.webContents.zoomLevel = 0;
106+
107+
window.emit("resize");
108+
vi.runAllTimers();
109+
const restoredZoomLevel = window.webContents.zoomLevel;
110+
adjustWindowZoom(window, 0.5);
111+
112+
expect({
113+
restoredZoomLevel,
114+
zoomLevel: window.webContents.zoomLevel,
115+
saved: store.save.mock.calls,
116+
}).toEqual({
117+
restoredZoomLevel: 1,
118+
zoomLevel: 1.5,
119+
saved: [[1], [1.5]],
120+
});
121+
});
122+
88123
it.each([
89124
["in", 1],
90125
["out", 0],
@@ -110,20 +145,70 @@ describe("window zoom", () => {
110145
},
111146
);
112147

113-
it("keeps wheel zoom after resizing", () => {
148+
it.each(["resize", "resized"] as const)(
149+
"keeps wheel zoom after %s",
150+
(resizeEvent) => {
151+
const window = createWindow();
152+
setupWindowZoom(window);
153+
154+
window.webContents.emit(
155+
"zoom-changed",
156+
{ preventDefault: vi.fn() },
157+
"in",
158+
);
159+
window.emit(resizeEvent);
160+
vi.runAllTimers();
161+
162+
expect({
163+
zoomLevel: window.webContents.zoomLevel,
164+
saved: store.save.mock.calls,
165+
}).toEqual({
166+
zoomLevel: 1,
167+
saved: [[1]],
168+
});
169+
},
170+
);
171+
172+
it("skips redundant restoration during a resize storm", () => {
173+
const window = createWindow();
174+
setupWindowZoom(window);
175+
window.webContents.zoomLevel = 0.5;
176+
177+
window.emit("resize");
178+
vi.runAllTimers();
179+
vi.advanceTimersByTime(16);
180+
window.emit("resize");
181+
vi.runAllTimers();
182+
const callsBeforeReset = [...window.webContents.setZoomLevelCalls];
183+
184+
window.webContents.zoomLevel = 0;
185+
window.emit("resize");
186+
vi.runAllTimers();
187+
188+
expect({
189+
callsBeforeReset,
190+
callsAfterReset: window.webContents.setZoomLevelCalls,
191+
}).toEqual({
192+
callsBeforeReset: [],
193+
callsAfterReset: [0.5],
194+
});
195+
});
196+
197+
it("ignores queued zoom work after the window is destroyed", () => {
114198
const window = createWindow();
115199
setupWindowZoom(window);
116200

117201
window.webContents.emit("zoom-changed", { preventDefault: vi.fn() }, "in");
118-
window.emit("resized");
202+
window.emit("resize");
203+
window.webContents.destroyed = true;
119204
vi.runAllTimers();
120205

121206
expect({
122-
zoomLevel: window.webContents.zoomLevel,
207+
zoomLevelCalls: window.webContents.setZoomLevelCalls,
123208
saved: store.save.mock.calls,
124209
}).toEqual({
125-
zoomLevel: 1,
126-
saved: [[1]],
210+
zoomLevelCalls: [],
211+
saved: [],
127212
});
128213
});
129214

apps/code/src/main/zoom.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const ZOOM_MIN = -3;
66
const ZOOM_MAX = 3;
77

88
interface ZoomWebContents {
9+
getZoomLevel(): number;
10+
isDestroyed(): boolean;
911
on(event: "did-finish-load", listener: () => void): void;
1012
on(
1113
event: "zoom-changed",
@@ -23,6 +25,7 @@ interface ZoomWindow {
2325
| "enter-full-screen"
2426
| "leave-full-screen"
2527
| "maximize"
28+
| "resize"
2629
| "resized"
2730
| "unmaximize",
2831
listener: () => void,
@@ -62,6 +65,7 @@ function runAfterWheelZoom(window: ZoomWindow, action: () => void): void {
6265
}
6366

6467
export function setWindowZoom(window: ZoomWindow, level: number): void {
68+
if (window.webContents.isDestroyed()) return;
6569
const nextLevel = clampZoomLevel(level);
6670
const state = zoomStates.get(window);
6771
if (state) state.currentZoomLevel = nextLevel;
@@ -82,7 +86,11 @@ export function adjustWindowZoom(
8286

8387
export function restoreWindowZoom(window: ZoomWindow): void {
8488
runAfterWheelZoom(window, () => {
85-
window.webContents.setZoomLevel(getCurrentZoomLevel(window));
89+
if (window.webContents.isDestroyed()) return;
90+
const zoomLevel = getCurrentZoomLevel(window);
91+
if (window.webContents.getZoomLevel() !== zoomLevel) {
92+
window.webContents.setZoomLevel(zoomLevel);
93+
}
8694
});
8795
}
8896

@@ -120,6 +128,7 @@ export function setupWindowZoom(window: ZoomWindow): void {
120128

121129
window.on("maximize", scheduleRestore);
122130
window.on("unmaximize", scheduleRestore);
131+
window.on("resize", scheduleRestore);
123132
window.on("resized", scheduleRestore);
124133
window.on("enter-full-screen", scheduleRestore);
125134
window.on("leave-full-screen", scheduleRestore);

0 commit comments

Comments
 (0)