🐛 fix(window): 适配分辨率变化后的窗口边界

- 运行期检测普通窗口是否超出当前可用屏幕并自动收回
- 启动恢复时同步裁剪超过当前屏幕的窗口尺寸

Fixes #594
This commit is contained in:
Syngnat
2026-06-27 11:17:42 +08:00
parent bfb61c8449
commit 080ae0986a
4 changed files with 156 additions and 13 deletions

View File

@@ -23,4 +23,18 @@ describe('windowRestoreBounds', () => {
{ availWidth: 1600, availHeight: 900, availLeft: 0, availTop: 0 },
)).toEqual({ width: 900, height: 640, x: 350, y: 130 });
});
it('shrinks a restored window that is larger than the current visible screen', () => {
expect(resolveVisibleStartupWindowBounds(
{ width: 2560, height: 1440, x: 0, y: 0 },
{ availWidth: 1440, availHeight: 860, availLeft: 0, availTop: 25 },
)).toEqual({ width: 1440, height: 860, x: 0, y: 25 });
});
it('keeps oversized restored bounds inside an offset visible screen', () => {
expect(resolveVisibleStartupWindowBounds(
{ width: 2400, height: 1200, x: 1800, y: 60 },
{ availWidth: 1728, availHeight: 1040, availLeft: 1728, availTop: 40 },
)).toEqual({ width: 1728, height: 1040, x: 1728, y: 40 });
});
});

View File

@@ -14,6 +14,31 @@ type VisibleViewport = {
const MIN_VISIBLE_WIDTH = 160;
const MIN_VISIBLE_HEIGHT = 120;
const MIN_RESTORED_WIDTH = 400;
const MIN_RESTORED_HEIGHT = 300;
const resolveVisibleDimension = (
rawDimension: number,
visibleDimension: number,
minimumDimension: number,
): number => {
const dimension = Math.trunc(Number(rawDimension) || 0);
if (visibleDimension <= 0 || dimension <= 0) {
return dimension;
}
const minimum = Math.min(minimumDimension, visibleDimension);
return Math.min(Math.max(dimension, minimum), visibleDimension);
};
const clampPosition = (
position: number,
dimension: number,
visibleStart: number,
visibleDimension: number,
): number => {
const maxPosition = visibleStart + Math.max(0, visibleDimension - dimension);
return Math.min(Math.max(position, visibleStart), maxPosition);
};
export const resolveVisibleStartupWindowBounds = (
bounds: WindowRestoreBounds,
@@ -29,19 +54,36 @@ export const resolveVisibleStartupWindowBounds = (
const visibleTop = Math.trunc(Number(viewport.availTop) || 0);
const visibleRight = visibleLeft + visibleWidth;
const visibleBottom = visibleTop + visibleHeight;
const nextWidth = resolveVisibleDimension(bounds.width, visibleWidth, MIN_RESTORED_WIDTH);
const nextHeight = resolveVisibleDimension(bounds.height, visibleHeight, MIN_RESTORED_HEIGHT);
const resizedBounds: WindowRestoreBounds = {
...bounds,
width: nextWidth,
height: nextHeight,
};
const overlapWidth = Math.min(bounds.x + bounds.width, visibleRight) - Math.max(bounds.x, visibleLeft);
const overlapHeight = Math.min(bounds.y + bounds.height, visibleBottom) - Math.max(bounds.y, visibleTop);
const overlapWidth = Math.min(resizedBounds.x + resizedBounds.width, visibleRight) - Math.max(resizedBounds.x, visibleLeft);
const overlapHeight = Math.min(resizedBounds.y + resizedBounds.height, visibleBottom) - Math.max(resizedBounds.y, visibleTop);
const sizeChanged = resizedBounds.width !== bounds.width || resizedBounds.height !== bounds.height;
if (
!sizeChanged &&
overlapWidth >= Math.min(MIN_VISIBLE_WIDTH, bounds.width) &&
overlapHeight >= Math.min(MIN_VISIBLE_HEIGHT, bounds.height)
) {
return bounds;
}
if (sizeChanged && overlapWidth > 0 && overlapHeight > 0) {
return {
...resizedBounds,
x: clampPosition(resizedBounds.x, resizedBounds.width, visibleLeft, visibleWidth),
y: clampPosition(resizedBounds.y, resizedBounds.height, visibleTop, visibleHeight),
};
}
return {
...bounds,
x: visibleLeft + Math.max(0, Math.trunc((visibleWidth - bounds.width) / 2)),
y: visibleTop + Math.max(0, Math.trunc((visibleHeight - bounds.height) / 2)),
...resizedBounds,
x: visibleLeft + Math.max(0, Math.trunc((visibleWidth - resizedBounds.width) / 2)),
y: visibleTop + Math.max(0, Math.trunc((visibleHeight - resizedBounds.height) / 2)),
};
};