mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-26 21:57:32 +00:00
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:
+52
-6
@@ -343,6 +343,8 @@ type
|
||||
FMeterTimer: TTimer;
|
||||
FSpectrumTimer: TTimer;
|
||||
FAfterShowTimer:TTimer; // однократный таймер для пост-инициализации
|
||||
FRestoreL, FRestoreT: Integer; // позиция для отложенного восстановления
|
||||
FHasPendingRestore: Boolean;
|
||||
|
||||
// ---- Top panel (toolbar + VFO group + S-meter) ----
|
||||
PanelToolbar: TPanel;
|
||||
@@ -933,10 +935,11 @@ end;
|
||||
procedure TMainForm.RestoreWindowBounds;
|
||||
var
|
||||
L, T, Wd, Ht, SavedDPI, CurDPI: Integer;
|
||||
Maximized: Boolean;
|
||||
R, WA: TRect;
|
||||
Mon: TMonitor;
|
||||
begin
|
||||
FSettings.LoadWindowBounds(L, T, Wd, Ht, SavedDPI);
|
||||
FSettings.LoadWindowBounds(L, T, Wd, Ht, SavedDPI, Maximized);
|
||||
if (Wd > 400) and (Ht > 300) then
|
||||
begin
|
||||
CurDPI := CurrentScreenDPI;
|
||||
@@ -944,14 +947,32 @@ begin
|
||||
|
||||
if SavedDPI <> CurDPI then
|
||||
begin
|
||||
L := MulDiv(L, CurDPI, SavedDPI);
|
||||
T := MulDiv(T, CurDPI, SavedDPI);
|
||||
// Позицию не масштабируем: на LCLWin32 координаты физические и не зависят
|
||||
// от DPI-фактора; на Qt6/GTK/Cocoa — логические (device-independent), тоже
|
||||
// инвариантны. Выход за рабочую область поймает EnsureRange ниже.
|
||||
//
|
||||
// Размер масштабируем, чтобы окно выглядело одинаково при смене DPI:
|
||||
// LCLWin32: физические пиксели → Wd × CurDPI/SavedDPI
|
||||
// Qt6/GTK/Cocoa: логические пиксели → Wd × SavedDPI/CurDPI (обратное)
|
||||
{$IFDEF LCLWin32}
|
||||
Wd := MulDiv(Wd, CurDPI, SavedDPI);
|
||||
Ht := MulDiv(Ht, CurDPI, SavedDPI);
|
||||
{$ELSE}
|
||||
Wd := MulDiv(Wd, SavedDPI, CurDPI);
|
||||
Ht := MulDiv(Ht, SavedDPI, CurDPI);
|
||||
{$ENDIF}
|
||||
end;
|
||||
|
||||
// Минимальный размер:
|
||||
// LCLWin32 — в физических пикселях, масштабируется с DPI.
|
||||
// Qt6/GTK/Cocoa — в логических пикселях, device-independent, фиксированный.
|
||||
{$IFDEF LCLWin32}
|
||||
Wd := Max(MulDiv(900, CurDPI, 96), Wd);
|
||||
Ht := Max(MulDiv(560, CurDPI, 96), Ht);
|
||||
{$ELSE}
|
||||
Wd := Max(900, Wd);
|
||||
Ht := Max(560, Ht);
|
||||
{$ENDIF}
|
||||
R := Bounds(L, T, Wd, Ht);
|
||||
Mon := Screen.MonitorFromRect(R);
|
||||
if Mon <> nil then WA := Mon.WorkareaRect
|
||||
@@ -962,15 +983,27 @@ begin
|
||||
L := EnsureRange(L, WA.Left, Max(WA.Left, WA.Right - Wd));
|
||||
T := EnsureRange(T, WA.Top, Max(WA.Top, WA.Bottom - Ht));
|
||||
|
||||
Position := poDesigned;
|
||||
SetBounds(L, T, Wd, Ht);
|
||||
if Maximized then
|
||||
WindowState := wsMaximized
|
||||
else
|
||||
begin
|
||||
// На Qt6/X11 frame extents ещё не известны Qt во время FormCreate,
|
||||
// поэтому move() может поставить контент туда, куда мы хотели рамку.
|
||||
// Повторно применим позицию в AfterShowTick, когда WM уже декорировал окно.
|
||||
FRestoreL := L;
|
||||
FRestoreT := T;
|
||||
FHasPendingRestore := True;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.SaveWindowBounds;
|
||||
var
|
||||
L, T, Wd, Ht, DPI: Integer;
|
||||
Maximized: Boolean;
|
||||
begin
|
||||
Maximized := WindowState = wsMaximized;
|
||||
if WindowState = wsNormal then
|
||||
begin
|
||||
L := Left; T := Top; Wd := Width; Ht := Height;
|
||||
@@ -981,9 +1014,12 @@ begin
|
||||
Wd := RestoredWidth; Ht := RestoredHeight;
|
||||
end;
|
||||
|
||||
// RestoredLeft/Top могут вернуть мусор на Qt6 при максимизации;
|
||||
// ставим разумные запасные значения
|
||||
if (L < -32000) or (T < -32000) then begin L := 80; T := 80; end;
|
||||
if (Wd <= 400) or (Ht <= 300) then Exit;
|
||||
DPI := CurrentScreenDPI;
|
||||
FSettings.SaveWindowBounds(L, T, Wd, Ht, DPI);
|
||||
FSettings.SaveWindowBounds(L, T, Wd, Ht, DPI, Maximized);
|
||||
end;
|
||||
|
||||
function TMainForm.MakeBandSettings: TBandSettings;
|
||||
@@ -1497,7 +1533,6 @@ begin
|
||||
end;
|
||||
// Размер окна сохраняем всегда (не зависит от подключения)
|
||||
FSettings.SaveStartupPreview(FVfoA, FVfoB, FSampleRate);
|
||||
SaveWindowBounds;
|
||||
FSettings.Save;
|
||||
FSettings.Free;
|
||||
FWebServer.Stop;
|
||||
@@ -1509,6 +1544,9 @@ end;
|
||||
|
||||
procedure TMainForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
|
||||
begin
|
||||
// Сохраняем позицию здесь — окно ещё полностью видимо и стабильно.
|
||||
// В FormDestroy Qt-виджет может быть уже в нестабильном состоянии.
|
||||
SaveWindowBounds;
|
||||
// Останавливаем трансивер как при нажатии STOP
|
||||
if FNetwork.Connected then
|
||||
begin
|
||||
@@ -6968,6 +7006,14 @@ begin
|
||||
// Вызывается один раз через 200 мс после старта формы
|
||||
FAfterShowTimer.Enabled := False;
|
||||
|
||||
// Повторно применяем сохранённую позицию: к этому моменту WM уже декорировал
|
||||
// окно и Qt знает frame extents, поэтому move() ставит рамку точно.
|
||||
if FHasPendingRestore then
|
||||
begin
|
||||
SetBounds(FRestoreL, FRestoreT, Width, Height);
|
||||
FHasPendingRestore := False;
|
||||
end;
|
||||
|
||||
try
|
||||
FAudioOut.Open;
|
||||
except
|
||||
|
||||
Reference in New Issue
Block a user