Files
MyGoNavi/frontend/src/utils/windowRestoreBounds.ts
Syngnat 080ae0986a 🐛 fix(window): 适配分辨率变化后的窗口边界
- 运行期检测普通窗口是否超出当前可用屏幕并自动收回
- 启动恢复时同步裁剪超过当前屏幕的窗口尺寸

Fixes #594
2026-06-27 11:17:42 +08:00

90 lines
2.9 KiB
TypeScript

export type WindowRestoreBounds = {
width: number;
height: number;
x: number;
y: number;
};
type VisibleViewport = {
availWidth: number;
availHeight: number;
availLeft?: number;
availTop?: number;
};
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,
viewport: VisibleViewport,
): WindowRestoreBounds => {
const visibleWidth = Math.trunc(Number(viewport.availWidth) || 0);
const visibleHeight = Math.trunc(Number(viewport.availHeight) || 0);
if (visibleWidth <= 0 || visibleHeight <= 0) {
return bounds;
}
const visibleLeft = Math.trunc(Number(viewport.availLeft) || 0);
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(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 {
...resizedBounds,
x: visibleLeft + Math.max(0, Math.trunc((visibleWidth - resizedBounds.width) / 2)),
y: visibleTop + Math.max(0, Math.trunc((visibleHeight - resizedBounds.height) / 2)),
};
};