Fix window geometry save/restore: DPI scaling, position drift, maximized state

- LFM: Position = poDesigned (was poScreenCenter, overriding our restore)
- RestoreWindowBounds: remove position scaling (L,T invariant across DPI);
  scale size correctly per backend: LCLWin32 uses CurDPI/SavedDPI (physical px),
  Qt6/GTK/Cocoa use SavedDPI/CurDPI (logical px, inverse); fix min-size
  inflation on Qt6 (was MulDiv(900,DPI,96) which exceeds logical screen width
  at 150%+ scale, forcing window to 0,0)
- SaveWindowBounds moved from FormDestroy to FormClose while window is still
  stable; add guard for bogus RestoredLeft/Top values on Qt6 when maximized
- RestoreWindowBounds stores FRestoreL/T and re-applies position in AfterShowTick
  (200ms after show) when WM frame extents are already known to Qt, fixing the
  systematic right/down drift caused by unknown frame extents at FormCreate time
- Save and restore WindowState (maximized flag); skip AfterShowTick position
  re-apply when window is maximized

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-25 11:56:56 +03:00
co-authored by Claude Sonnet 4.6
parent 1eecd974fc
commit 9028a8dac0
3 changed files with 70 additions and 22 deletions
+17 -15
View File
@@ -282,8 +282,8 @@ type
class procedure DefaultGlobal(out G: TGlobalSettings);
class procedure DefaultTX(out T: TTXSettings);
// Размер окна — не привязан к MAC, хранится в корне JSON
procedure SaveWindowBounds(L, T, W, H, DPI: Integer);
procedure LoadWindowBounds(out L, T, W, H, DPI: Integer);
procedure SaveWindowBounds(L, T, W, H, DPI: Integer; Maximized: Boolean);
procedure LoadWindowBounds(out L, T, W, H, DPI: Integer; out Maximized: Boolean);
procedure SaveStartupPreview(VfoA, VfoB: Double; SampleRate: Integer);
function LoadStartupPreview(out VfoA, VfoB: Double; out SampleRate: Integer): Boolean;
procedure SaveAudioBufferSize(BufferSize: Integer);
@@ -757,28 +757,30 @@ begin
B.FMStepIdx := EnsureRange(JI(O,'fmstep_idx', B.FMStepIdx), 0, 3);
end;
procedure TSettingsManager.SaveWindowBounds(L, T, W, H, DPI: Integer);
procedure TSettingsManager.SaveWindowBounds(L, T, W, H, DPI: Integer; Maximized: Boolean);
var O: TJSONObject;
begin
O := EnsureObj(FRoot, 'window');
JW(O, 'left', L);
JW(O, 'top', T);
JW(O, 'width', W);
JW(O, 'height', H);
JW(O, 'dpi', DPI);
JW(O, 'left', L);
JW(O, 'top', T);
JW(O, 'width', W);
JW(O, 'height', H);
JW(O, 'dpi', DPI);
JW(O, 'maximized', Maximized);
end;
procedure TSettingsManager.LoadWindowBounds(out L, T, W, H, DPI: Integer);
procedure TSettingsManager.LoadWindowBounds(out L, T, W, H, DPI: Integer; out Maximized: Boolean);
var O: TJSONObject;
begin
L := 80; T := 80; W := 1400; H := 900; DPI := 0;
L := 80; T := 80; W := 1400; H := 900; DPI := 0; Maximized := False;
if FRoot.Find('window') = nil then Exit;
O := EnsureObj(FRoot, 'window');
L := JI(O, 'left', 80);
T := JI(O, 'top', 80);
W := JI(O, 'width', 1400);
H := JI(O, 'height', 900);
DPI := JI(O, 'dpi', 0);
L := JI(O, 'left', 80);
T := JI(O, 'top', 80);
W := JI(O, 'width', 1400);
H := JI(O, 'height', 900);
DPI := JI(O, 'dpi', 0);
Maximized := JB(O, 'maximized', False);
end;
procedure TSettingsManager.SaveStartupPreview(VfoA, VfoB: Double; SampleRate: Integer);