Skip to content
Open
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
58 changes: 58 additions & 0 deletions electron/recorder-window-sizing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,61 @@ test("recorder fitting clamps content height and ignores invalid requests", () =
fitRecorderHeight(win, 500);
assert.deepEqual(win.size, size);
});

class FractionalScalingWindow implements RecorderWindowSizingTarget {
destroyed = false;
resizable = false;
size: [number, number] = [401, 500];
setSizeCalls = 0;

constructor(private readonly heightRoundingError: number) {}

isDestroyed(): boolean {
return this.destroyed;
}

getContentSize(): [number, number] {
const frame: [number, number] = this.resizable ? [16, 39] : [2, 31];
return [this.size[0] - frame[0], this.size[1] - frame[1]];
}

getSize(): [number, number] {
return [...this.size];
}

isResizable(): boolean {
return this.resizable;
}

setResizable(resizable: boolean): void {
this.resizable = resizable;
}

setSize(width: number, height: number): void {
this.setSizeCalls++;
this.size = [width + 2, height + this.heightRoundingError];
}
}

test("recorder fitting prevents width growth across repeated fractional-scale resizing", () => {
const win = new FractionalScalingWindow(3);

fitRecorderHeight(win, 600);
const widthAfterFirstFit = win.size[0];

for (let i = 0; i < 10; i++) {
fitRecorderHeight(win, 600);
}

assert.equal(win.setSizeCalls, 11);
assert.equal(win.size[0], widthAfterFirstFit);
});

test("recorder fitting accepts fractional-scale height differences within tolerance", () => {
const win = new FractionalScalingWindow(2);

fitRecorderHeight(win, 600);
fitRecorderHeight(win, 600);

assert.equal(win.setSizeCalls, 1);
});
13 changes: 11 additions & 2 deletions electron/recorder-window-sizing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export interface RecorderWindowSizingTarget {
setSize(width: number, height: number, animate?: boolean): void;
}

const pinnedOuterWidths = new WeakMap<RecorderWindowSizingTarget, number>();
const HEIGHT_TOLERANCE = 2;

/**
* Fit the recorder to its rendered content while preserving its fixed outer width.
* Windows changes the non-client frame thickness when resizability is toggled, so
Expand All @@ -26,14 +29,20 @@ export function fitRecorderHeight(
Math.max(RECORDER_MIN_HEIGHT, Math.min(RECORDER_MAX_HEIGHT, contentHeight)),
);
const [, currentContentHeight] = win.getContentSize();
if (Math.abs(currentContentHeight - targetContentHeight) < 1) return;
if (Math.abs(currentContentHeight - targetContentHeight) <= HEIGHT_TOLERANCE) return;

const [outerWidth, outerHeight] = win.getSize();
let pinnedWidth = pinnedOuterWidths.get(win);
if (pinnedWidth === undefined) {
pinnedWidth = outerWidth;
pinnedOuterWidths.set(win, pinnedWidth);
}

const targetOuterHeight = outerHeight + targetContentHeight - currentContentHeight;
const wasResizable = win.isResizable();
if (!wasResizable) win.setResizable(true);
try {
win.setSize(outerWidth, targetOuterHeight);
win.setSize(pinnedWidth, targetOuterHeight);
} finally {
if (!wasResizable && !win.isDestroyed()) win.setResizable(false);
}
Expand Down