mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-22 00:42:47 +08:00
🐛 fix(window): 遵循启动最大化偏好并恢复窗口尺寸
- 原生窗口统一以普通状态启动,避免关闭偏好后仍被强制最大化 - 关闭偏好时恢复持久化尺寸与位置,并阻止启动瞬态覆盖窗口数据 - Windows、Linux 与 macOS 开启偏好时统一使用窗口最大化 - 更新启动窗口文案与跨平台回归测试 Refs #703
This commit is contained in:
@@ -146,8 +146,8 @@ import {
|
||||
isStartupWindowRestorePending,
|
||||
markStartupWindowRestorePending,
|
||||
resolveDefaultStartupWindowBounds,
|
||||
resolveStartupWindowRestoreMode,
|
||||
resolveWorkAreaFillWindowBounds,
|
||||
shouldPreferWindowsStartupMaximise,
|
||||
} from './utils/windowStartupLayout';
|
||||
import {
|
||||
SHORTCUT_ACTION_META,
|
||||
@@ -749,8 +749,9 @@ function App() {
|
||||
const setUiScale = useStore(state => state.setUiScale);
|
||||
const fontSize = useStore(state => state.fontSize);
|
||||
const setFontSize = useStore(state => state.setFontSize);
|
||||
const startupFullscreen = useStore(state => state.startupFullscreen);
|
||||
const setStartupFullscreen = useStore(state => state.setStartupFullscreen);
|
||||
// Keep reading the legacy persisted field; its product meaning is now startup maximise.
|
||||
const startupMaximised = useStore(state => state.startupFullscreen);
|
||||
const setStartupMaximised = useStore(state => state.setStartupFullscreen);
|
||||
const autoCheckForUpdates = useStore(state => state.autoCheckForUpdates);
|
||||
const setAutoCheckForUpdates = useStore(state => state.setAutoCheckForUpdates);
|
||||
const autoCheckForUpdatesIntervalMinutes = useStore(state => state.autoCheckForUpdatesIntervalMinutes);
|
||||
@@ -1346,16 +1347,9 @@ function App() {
|
||||
const maxApplyAttempts = 8;
|
||||
const applyRetryDelayMs = 350;
|
||||
const settleDelayMs = 180;
|
||||
const useMaximiseForStartup = isWindowsPlatform();
|
||||
const startupRestoreGraceMs = 6000;
|
||||
|
||||
const checkStartupPreferenceApplied = async (): Promise<boolean> => {
|
||||
try {
|
||||
if (await WindowIsFullscreen()) {
|
||||
return true;
|
||||
}
|
||||
} catch (_) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
if (await WindowIsMaximised()) {
|
||||
return true;
|
||||
@@ -1366,13 +1360,9 @@ function App() {
|
||||
return false;
|
||||
};
|
||||
|
||||
const markAppliedMaximisedOrFullscreen = (mode: 'maximised' | 'fullscreen') => {
|
||||
// 启动偏好成功后立刻固化 windowState,避免宽限期内被写成 normal 导致下次半窗
|
||||
if (mode === 'maximised' || useMaximiseForStartup) {
|
||||
useStore.getState().setWindowState('maximized');
|
||||
} else {
|
||||
useStore.getState().setWindowState('fullscreen');
|
||||
}
|
||||
const markStartupMaximised = () => {
|
||||
// 启动偏好成功后立刻同步实际窗口态,避免 settle 宽限期留下瞬态 normal。
|
||||
useStore.getState().setWindowState('maximized');
|
||||
clearStartupWindowRestorePending();
|
||||
};
|
||||
|
||||
@@ -1386,7 +1376,7 @@ function App() {
|
||||
WindowSetSize(nextBounds.width, nextBounds.height);
|
||||
WindowSetPosition(nextBounds.x, nextBounds.y);
|
||||
useStore.getState().setWindowBounds(nextBounds);
|
||||
// 仍记为 maximized:视觉上已铺满,下次继续走最大化恢复
|
||||
// 兜底结果视觉上等同最大化,保持标题栏状态与实际窗口一致。
|
||||
useStore.getState().setWindowState('maximized');
|
||||
void emitWindowDiagnostic('adjust:startup-work-area-fill-fallback', {
|
||||
to: nextBounds,
|
||||
@@ -1396,11 +1386,9 @@ function App() {
|
||||
}
|
||||
};
|
||||
|
||||
// mode:
|
||||
// - maximised: 始终最大化(Windows 启动偏好 / 记忆的 maximized / Windows 上的 fullscreen 记忆)
|
||||
// - fullscreen: 非 Windows 优先真全屏,失败再最大化
|
||||
// 第 1 次立即执行(delay=0),避免 Windows 先闪 1024×768 半窗再最大化
|
||||
const applyStartupWindowChrome = (attempt: number, mode: 'maximised' | 'fullscreen') => {
|
||||
// Windows、Linux 与 macOS 的启动偏好都使用普通窗口最大化,不进入系统全屏。
|
||||
// 第 1 次立即执行(delay=0),缩短普通窗口首帧到目标窗口态的过渡。
|
||||
const applyStartupWindowChrome = (attempt: number) => {
|
||||
if (startupWindowTimer !== null) {
|
||||
window.clearTimeout(startupWindowTimer);
|
||||
}
|
||||
@@ -1412,37 +1400,25 @@ function App() {
|
||||
void Promise.resolve()
|
||||
.then(async () => {
|
||||
if (await checkStartupPreferenceApplied()) {
|
||||
markAppliedMaximisedOrFullscreen(mode);
|
||||
markStartupMaximised();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (mode === 'maximised') {
|
||||
await WindowMaximise();
|
||||
await new Promise((resolve) => window.setTimeout(resolve, settleDelayMs));
|
||||
} else {
|
||||
await WindowFullscreen();
|
||||
await new Promise((resolve) => window.setTimeout(resolve, settleDelayMs));
|
||||
if (await checkStartupPreferenceApplied()) {
|
||||
markAppliedMaximisedOrFullscreen(mode);
|
||||
return;
|
||||
}
|
||||
await WindowMaximise();
|
||||
await new Promise((resolve) => window.setTimeout(resolve, settleDelayMs));
|
||||
}
|
||||
await WindowMaximise();
|
||||
await new Promise((resolve) => window.setTimeout(resolve, settleDelayMs));
|
||||
} catch (e) {
|
||||
console.warn("Wails Window APIs unavailable", e);
|
||||
}
|
||||
|
||||
if (await checkStartupPreferenceApplied()) {
|
||||
markAppliedMaximisedOrFullscreen(mode);
|
||||
markStartupMaximised();
|
||||
return;
|
||||
}
|
||||
if (attempt < maxApplyAttempts) {
|
||||
applyStartupWindowChrome(attempt + 1, mode);
|
||||
applyStartupWindowChrome(attempt + 1);
|
||||
} else {
|
||||
// 最终仍失败:Windows 铺满工作区兜底,再结束宽限
|
||||
void emitWindowDiagnostic('warn:startup-maximise-failed', {
|
||||
mode,
|
||||
attempts: attempt,
|
||||
});
|
||||
applyWindowsWorkAreaFillFallback();
|
||||
@@ -1458,16 +1434,17 @@ function App() {
|
||||
x: number;
|
||||
y: number;
|
||||
}) => {
|
||||
// Windows 可能以原生 Maximised 首帧启动,恢复普通窗前先取消最大化
|
||||
if (isWindowsPlatform()) {
|
||||
try {
|
||||
if (await WindowIsMaximised()) {
|
||||
WindowUnmaximise();
|
||||
await new Promise((resolve) => window.setTimeout(resolve, settleDelayMs));
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to unmaximise before restoring normal bounds', e);
|
||||
try {
|
||||
if (await WindowIsFullscreen()) {
|
||||
WindowUnfullscreen();
|
||||
await new Promise((resolve) => window.setTimeout(resolve, settleDelayMs));
|
||||
}
|
||||
if (await WindowIsMaximised()) {
|
||||
WindowUnmaximise();
|
||||
await new Promise((resolve) => window.setTimeout(resolve, settleDelayMs));
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to restore normal window chrome', e);
|
||||
}
|
||||
const state = useStore.getState();
|
||||
const nextBounds = resolveVisibleStartupWindowBounds(bounds, readCurrentVisibleViewport());
|
||||
@@ -1481,10 +1458,10 @@ function App() {
|
||||
from: bounds,
|
||||
to: nextBounds,
|
||||
});
|
||||
state.setWindowBounds(nextBounds);
|
||||
}
|
||||
WindowSetSize(nextBounds.width, nextBounds.height);
|
||||
WindowSetPosition(nextBounds.x, nextBounds.y);
|
||||
state.setWindowBounds(nextBounds);
|
||||
state.setWindowState('normal');
|
||||
};
|
||||
|
||||
@@ -1500,62 +1477,38 @@ function App() {
|
||||
restoredOnce = true;
|
||||
|
||||
const state = useStore.getState();
|
||||
// 1) 「启动时最大化」开关优先(Windows 按 Maximize 处理)
|
||||
if (state.startupFullscreen) {
|
||||
markStartupWindowRestorePending(3200);
|
||||
applyStartupWindowChrome(1, useMaximiseForStartup ? 'maximised' : 'fullscreen');
|
||||
const restoreMode = resolveStartupWindowRestoreMode(
|
||||
state.startupFullscreen,
|
||||
);
|
||||
if (restoreMode !== 'normal') {
|
||||
markStartupWindowRestorePending(startupRestoreGraceMs);
|
||||
applyStartupWindowChrome(1);
|
||||
return;
|
||||
}
|
||||
// 2) 记忆用户上次窗口态:最大化/全屏
|
||||
const savedState = state.windowState;
|
||||
if (savedState === 'fullscreen') {
|
||||
// Windows 上记忆的 fullscreen 也走最大化,避免真全屏后标题栏交互困难
|
||||
markStartupWindowRestorePending(3200);
|
||||
applyStartupWindowChrome(1, useMaximiseForStartup ? 'maximised' : 'fullscreen');
|
||||
return;
|
||||
}
|
||||
if (savedState === 'maximized') {
|
||||
// 必须重试:Windows 冷启动 HWND/WebView2 未就绪时单次 Maximise 经常失败,
|
||||
// 会残留 main.go 默认 1024x768 贴左上角;任务栏恢复后才“突然正常”。
|
||||
markStartupWindowRestorePending(3200);
|
||||
applyStartupWindowChrome(1, 'maximised');
|
||||
return;
|
||||
}
|
||||
// 3) 普通窗口:恢复用户调整过的尺寸和位置
|
||||
// Windows:无记忆 / 历史半窗 / 84% 默认小窗 → 直接最大化,而不是再落到浮动半窗
|
||||
|
||||
// The disabled preference is strict: restore a normal window even if
|
||||
// an older build persisted an automatic maximised/fullscreen state.
|
||||
markStartupWindowRestorePending(startupRestoreGraceMs);
|
||||
const bounds = state.windowBounds;
|
||||
const viewport = readCurrentVisibleViewport();
|
||||
if (isWindowsPlatform() && shouldPreferWindowsStartupMaximise(bounds, viewport)) {
|
||||
markStartupWindowRestorePending(3200);
|
||||
applyStartupWindowChrome(1, 'maximised');
|
||||
void emitWindowDiagnostic('adjust:startup-prefer-maximise', {
|
||||
from: bounds,
|
||||
reason: !bounds ? 'missing-bounds' : 'undersized-bounds',
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!bounds || bounds.width < 400 || bounds.height < 300) {
|
||||
// 非 Windows:无记忆时保持系统默认;Windows 已在上方走最大化
|
||||
if (isWindowsPlatform()) {
|
||||
try {
|
||||
try {
|
||||
if (!bounds || bounds.width < 400 || bounds.height < 300) {
|
||||
if (isWindowsPlatform()) {
|
||||
const nextBounds = resolveDefaultStartupWindowBounds(viewport);
|
||||
WindowSetSize(nextBounds.width, nextBounds.height);
|
||||
WindowSetPosition(nextBounds.x, nextBounds.y);
|
||||
state.setWindowBounds(nextBounds);
|
||||
state.setWindowState('normal');
|
||||
await restoreNormalWindowBounds(nextBounds);
|
||||
void emitWindowDiagnostic('adjust:startup-default-window-bounds', {
|
||||
to: nextBounds,
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('Failed to apply default Windows startup bounds', e);
|
||||
} else {
|
||||
state.setWindowState('normal');
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await restoreNormalWindowBounds(bounds);
|
||||
} catch (e) {
|
||||
console.warn('Failed to restore window bounds', e);
|
||||
} finally {
|
||||
clearStartupWindowRestorePending();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1566,7 +1519,7 @@ function App() {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
// hydration 完成后再恢复,确保读到 startupFullscreen / windowState / windowBounds
|
||||
// hydration 完成后再恢复,确保读到启动最大化偏好与 windowBounds。
|
||||
restoredOnce = false;
|
||||
void restoreWindowState();
|
||||
});
|
||||
@@ -1589,7 +1542,7 @@ function App() {
|
||||
let lastSaved = '';
|
||||
|
||||
const saveWindowState = async () => {
|
||||
if (cancelled || !hydrated) {
|
||||
if (cancelled || !hydrated || isStartupWindowRestorePending()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -1598,13 +1551,9 @@ function App() {
|
||||
safeWindowRuntimeCall(() => WindowIsMaximised(), false),
|
||||
]);
|
||||
|
||||
// 启动最大化/全屏尚未 settle 时,禁止把状态写回 normal,
|
||||
// 否则下次冷启动会落到默认 1024x768 左上角(Windows 首次打开“只显示一半”)。
|
||||
// 启动窗口恢复尚未 settle 时,不保存中间态和中间尺寸。
|
||||
if (isStartupWindowRestorePending()) {
|
||||
if (!isFs && !isMax) {
|
||||
return;
|
||||
}
|
||||
clearStartupWindowRestorePending();
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存窗口状态
|
||||
@@ -1625,7 +1574,7 @@ function App() {
|
||||
safeWindowRuntimeCall(() => WindowGetSize(), null),
|
||||
safeWindowRuntimeCall(() => WindowGetPosition(), null),
|
||||
]);
|
||||
if (!size || !pos) return;
|
||||
if (!size || !pos || isStartupWindowRestorePending()) return;
|
||||
const w = Math.trunc(Number(size.w || 0));
|
||||
const h = Math.trunc(Number(size.h || 0));
|
||||
const x = Math.trunc(Number(pos.x || 0));
|
||||
@@ -1661,7 +1610,7 @@ function App() {
|
||||
if (cancelled || !hydrated) {
|
||||
return;
|
||||
}
|
||||
// 启动最大化 settle 期间不要抢跑普通 bounds 校正
|
||||
// 启动窗口恢复期间不要抢跑普通 bounds 校正。
|
||||
if (isStartupWindowRestorePending()) {
|
||||
return;
|
||||
}
|
||||
@@ -2336,8 +2285,6 @@ function App() {
|
||||
}, [connections, openSecurityUpdateSettings, runSecurityUpdateRound, securityUpdateStatus, t]);
|
||||
const isMacRuntime = runtimePlatform === 'darwin'
|
||||
|| (runtimePlatform === '' && /mac/i.test(detectNavigatorPlatform()));
|
||||
const isWindowsRuntime = runtimePlatform === 'windows'
|
||||
|| (runtimePlatform === '' && isWindowsPlatform());
|
||||
const useNativeMacWindowControls = isMacRuntime && appearance.useNativeMacWindowControls === true;
|
||||
const activeShortcutPlatform = getShortcutPlatform(isMacRuntime);
|
||||
const macWindowDiagnosticsEnabled = shouldEnableMacWindowDiagnostics(
|
||||
@@ -6422,14 +6369,10 @@ function App() {
|
||||
{renderThemeSettingsSection(
|
||||
t('app.theme.startup_window.title'),
|
||||
renderThemeSettingsRow({
|
||||
label: isWindowsRuntime
|
||||
? t('app.theme.startup_window.fullscreen_windows')
|
||||
: t('app.theme.startup_window.fullscreen'),
|
||||
hint: isWindowsRuntime
|
||||
? t('app.theme.startup_window.windows_hint')
|
||||
: t('app.theme.startup_window.hint'),
|
||||
label: t('app.theme.startup_window.maximised'),
|
||||
hint: t('app.theme.startup_window.hint'),
|
||||
control: (
|
||||
<Switch checked={startupFullscreen} onChange={(checked) => setStartupFullscreen(checked)} />
|
||||
<Switch checked={startupMaximised} onChange={(checked) => setStartupMaximised(checked)} />
|
||||
),
|
||||
}),
|
||||
)}
|
||||
@@ -7228,11 +7171,11 @@ function App() {
|
||||
<div style={utilityPanelStyle}>
|
||||
<div style={{ marginBottom: 8, fontWeight: 500 }}>{t('app.theme.startup_window.title')}</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
|
||||
<span>{isWindowsRuntime ? t('app.theme.startup_window.fullscreen_windows') : t('app.theme.startup_window.fullscreen')}</span>
|
||||
<Switch checked={startupFullscreen} onChange={(checked) => setStartupFullscreen(checked)} />
|
||||
<span>{t('app.theme.startup_window.maximised')}</span>
|
||||
<Switch checked={startupMaximised} onChange={(checked) => setStartupMaximised(checked)} />
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: darkMode ? 'rgba(255,255,255,0.5)' : 'rgba(16,24,40,0.55)', marginTop: 4 }}>
|
||||
{isWindowsRuntime ? t('app.theme.startup_window.windows_hint') : t('app.theme.startup_window.hint')}
|
||||
{t('app.theme.startup_window.hint')}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', gap: 12, paddingTop: 8, paddingBottom: 12 }}>
|
||||
|
||||
@@ -3229,7 +3229,7 @@ describe('store appearance persistence', () => {
|
||||
expect(hydrated.useStore.getState().autoCheckForUpdatesIntervalMinutes).toBe(30);
|
||||
});
|
||||
|
||||
it('persists window state and bounds immediately so Windows reopen keeps maximise or size memory', async () => {
|
||||
it('persists window state and bounds immediately across store reloads', async () => {
|
||||
const { useStore } = await importStore();
|
||||
|
||||
useStore.getState().setWindowState('maximized');
|
||||
|
||||
@@ -1771,6 +1771,7 @@ interface AppState {
|
||||
appearance: AppearanceSettings;
|
||||
uiScale: number;
|
||||
fontSize: number;
|
||||
/** Legacy persisted name; true means maximise the startup window on every desktop platform. */
|
||||
startupFullscreen: boolean;
|
||||
/** 启动后与定时静默检查更新;默认开启 */
|
||||
autoCheckForUpdates: boolean;
|
||||
@@ -5338,7 +5339,7 @@ export const useStore = create<AppState>()(
|
||||
setWindowState: (state) => {
|
||||
const nextState = sanitizeWindowState(state);
|
||||
set({ windowState: nextState });
|
||||
// 最大化/普通态也要同步写盘,否则下次冷启动会落到默认 1024×768「半窗」
|
||||
// 与窗口尺寸一致即时落盘,避免退出阶段丢失最后一次观测状态。
|
||||
writePersistedStatePatch({ windowState: nextState });
|
||||
},
|
||||
|
||||
|
||||
@@ -5,9 +5,8 @@ import {
|
||||
isStartupWindowRestorePending,
|
||||
markStartupWindowRestorePending,
|
||||
resolveDefaultStartupWindowBounds,
|
||||
resolveStartupWindowRestoreMode,
|
||||
resolveWorkAreaFillWindowBounds,
|
||||
shouldPreferWindowsStartupMaximise,
|
||||
WINDOWS_STARTUP_MAXIMISE_AREA_RATIO,
|
||||
} from './windowStartupLayout';
|
||||
|
||||
describe('windowStartupLayout', () => {
|
||||
@@ -99,34 +98,11 @@ describe('windowStartupLayout', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('prefers maximise for missing, legacy 1024×768, and undersized default windows', () => {
|
||||
const viewport = {
|
||||
availWidth: 1920,
|
||||
availHeight: 1080,
|
||||
availLeft: 0,
|
||||
availTop: 0,
|
||||
};
|
||||
it('keeps startup normal when the explicit fullscreen preference is disabled', () => {
|
||||
expect(resolveStartupWindowRestoreMode(false)).toBe('normal');
|
||||
});
|
||||
|
||||
expect(shouldPreferWindowsStartupMaximise(null, viewport)).toBe(true);
|
||||
expect(shouldPreferWindowsStartupMaximise({
|
||||
width: 1024,
|
||||
height: 768,
|
||||
x: 0,
|
||||
y: 0,
|
||||
}, viewport)).toBe(true);
|
||||
|
||||
// 84%×84% default area ≈ 0.706 < 0.78 → 最大化
|
||||
const defaultBounds = resolveDefaultStartupWindowBounds(viewport);
|
||||
expect((defaultBounds.width * defaultBounds.height) / (1920 * 1080))
|
||||
.toBeLessThan(WINDOWS_STARTUP_MAXIMISE_AREA_RATIO);
|
||||
expect(shouldPreferWindowsStartupMaximise(defaultBounds, viewport)).toBe(true);
|
||||
|
||||
// 用户刻意拉大的普通窗应保留
|
||||
expect(shouldPreferWindowsStartupMaximise({
|
||||
width: 1760,
|
||||
height: 980,
|
||||
x: 80,
|
||||
y: 40,
|
||||
}, viewport)).toBe(false);
|
||||
it('maximises the startup window on every desktop platform when enabled', () => {
|
||||
expect(resolveStartupWindowRestoreMode(true)).toBe('maximised');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,20 +16,17 @@ export type StartupWindowBounds = {
|
||||
const MIN_STARTUP_WIDTH = 900;
|
||||
const MIN_STARTUP_HEIGHT = 600;
|
||||
|
||||
/**
|
||||
* 工作区覆盖率低于该阈值时,视为「半窗 / 默认小窗」记忆,Windows 启动改走最大化。
|
||||
* 84%×84% 居中默认窗的面积比约为 0.706,会被捕获;用户刻意拉大的普通窗通常更高。
|
||||
*/
|
||||
export const WINDOWS_STARTUP_MAXIMISE_AREA_RATIO = 0.78;
|
||||
|
||||
/** Align with historical main.go Width/Height defaults that look half-open on modern screens. */
|
||||
const LEGACY_DEFAULT_WIDTH = 1024;
|
||||
const LEGACY_DEFAULT_HEIGHT = 768;
|
||||
export type StartupWindowRestoreMode = 'normal' | 'maximised';
|
||||
|
||||
/**
|
||||
* Resolve a usable first-launch window when no persisted bounds exist.
|
||||
* Windows defaults to top-left 1024x768 which looks "half open" on modern screens.
|
||||
* The explicit startup preference is authoritative. A disabled preference must
|
||||
* not be overridden by a previously maximised window or a size heuristic.
|
||||
*/
|
||||
export const resolveStartupWindowRestoreMode = (
|
||||
startupMaximised: boolean,
|
||||
): StartupWindowRestoreMode => startupMaximised ? 'maximised' : 'normal';
|
||||
|
||||
/** Resolve a centered normal window when no persisted bounds exist. */
|
||||
export const resolveDefaultStartupWindowBounds = (
|
||||
viewport: StartupVisibleViewport,
|
||||
): StartupWindowBounds => {
|
||||
@@ -62,7 +59,7 @@ export const resolveDefaultStartupWindowBounds = (
|
||||
|
||||
/**
|
||||
* Fill the OS work area (taskbar excluded). Used when Maximise API fails on Windows
|
||||
* so the shell still looks "full" instead of lingering at 1024×768 / 84% floating.
|
||||
* so the shell still looks full instead of lingering in a normal window.
|
||||
*/
|
||||
export const resolveWorkAreaFillWindowBounds = (
|
||||
viewport: StartupVisibleViewport,
|
||||
@@ -84,41 +81,9 @@ export const resolveWorkAreaFillWindowBounds = (
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Decide whether Windows cold-start should prefer maximise over restoring bounds.
|
||||
* - 无记忆 / 非法尺寸 → 最大化
|
||||
* - 仍像旧默认 1024×768 → 最大化
|
||||
* - 覆盖工作区面积过低(含历史 84% 居中默认窗)→ 最大化
|
||||
*/
|
||||
export const shouldPreferWindowsStartupMaximise = (
|
||||
bounds: StartupWindowBounds | null | undefined,
|
||||
viewport: StartupVisibleViewport,
|
||||
): boolean => {
|
||||
if (!bounds) {
|
||||
return true;
|
||||
}
|
||||
const width = Math.trunc(Number(bounds.width) || 0);
|
||||
const height = Math.trunc(Number(bounds.height) || 0);
|
||||
if (width < 400 || height < 300) {
|
||||
return true;
|
||||
}
|
||||
if (width <= LEGACY_DEFAULT_WIDTH && height <= LEGACY_DEFAULT_HEIGHT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const availWidth = Math.max(0, Math.trunc(Number(viewport.availWidth) || 0));
|
||||
const availHeight = Math.max(0, Math.trunc(Number(viewport.availHeight) || 0));
|
||||
if (availWidth <= 0 || availHeight <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const areaRatio = (width * height) / (availWidth * availHeight);
|
||||
return areaRatio < WINDOWS_STARTUP_MAXIMISE_AREA_RATIO;
|
||||
};
|
||||
|
||||
let startupWindowRestorePendingUntil = 0;
|
||||
|
||||
/** Mark a short grace window while startup maximise/fullscreen is still settling. */
|
||||
/** Mark a short grace window while startup window restoration is still settling. */
|
||||
export const markStartupWindowRestorePending = (durationMs = 2800): void => {
|
||||
const duration = Math.max(0, Math.trunc(Number(durationMs) || 0));
|
||||
startupWindowRestorePendingUntil = Date.now() + duration;
|
||||
|
||||
Reference in New Issue
Block a user