fix(window): apply the restore correction after FMX finishes, with tracing

The FWasMinimized gate never armed — FMX evidently fires no resize while the
window is iconic — so activate bailed out and the maximised window still came
back normal.

Stop depending on that signal or on event ordering: snapshot the intent at
activate (before a restore-time resize can overwrite it) and apply the
correction from a queued call, which necessarily runs after FMX has finished
sizing the window. FRestoring keeps that resize out of the tracking, and the
intent is re-asserted afterwards since our own ShowWindow fires one too.

Adds [winstate] LogLine traces so the next iteration works from observed
event order instead of guesses (needs config.txt with debug=true).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-25 20:13:30 +01:00
parent 8204ccb316
commit ab12f6bf66
+40 -6
View File
@@ -82,6 +82,9 @@ type
// True between an external minimise and the following activate, so the // True between an external minimise and the following activate, so the
// restore's own resize is not mistaken for a user resize. // restore's own resize is not mistaken for a user resize.
FWasMinimized: Boolean; FWasMinimized: Boolean;
// Set from activate until the queued correction runs, so the restore's
// own resize is not mistaken for the user resizing the window.
FRestoring: Boolean;
FServer: TPMHTTPServer; FServer: TPMHTTPServer;
FBridge: TPMBridge; FBridge: TPMBridge;
// Accumulates base64 chunks for large file saves (attachments too big // Accumulates base64 chunks for large file saves (attachments too big
@@ -178,6 +181,7 @@ begin
// Wired here (not in the .fmx) to keep the designer file untouched. // Wired here (not in the .fmx) to keep the designer file untouched.
FWantMaximized := (WindowState = TWindowState.wsMaximized); FWantMaximized := (WindowState = TWindowState.wsMaximized);
FWasMinimized := False; FWasMinimized := False;
FRestoring := False;
OnResize := FormResizeTrackState; OnResize := FormResizeTrackState;
OnActivate := FormActivateRestoreState; OnActivate := FormActivateRestoreState;
@@ -1523,10 +1527,18 @@ begin
if IsIconic(LHwnd) then if IsIconic(LHwnd) then
begin begin
FWasMinimized := True; // the state we must survive, not record FWasMinimized := True; // the state we must survive, not record
LogLine('[winstate] resize: minimised');
Exit;
end;
if FRestoring then
begin
// The restore's own resize: reports whatever FMX decided, not intent.
LogLine('[winstate] resize during restore ignored (zoomed=' +
BoolToStr(IsZoomed(LHwnd), True) + ')');
Exit; Exit;
end; end;
if FWasMinimized then Exit; // this resize IS the restore
FWantMaximized := IsZoomed(LHwnd); // genuine user-driven resize FWantMaximized := IsZoomed(LHwnd); // genuine user-driven resize
LogLine('[winstate] resize: want_max=' + BoolToStr(FWantMaximized, True));
end; end;
// Re-apply the remembered state once the window is back. Runs however the // Re-apply the remembered state once the window is back. Runs however the
@@ -1536,15 +1548,37 @@ end;
// maximised. // maximised.
procedure TMainForm.FormActivateRestoreState(Sender: TObject); procedure TMainForm.FormActivateRestoreState(Sender: TObject);
var var
LHwnd: HWND; LWant: Boolean;
begin begin
if not FWasMinimized then Exit; // Snapshot the intent NOW: FMX's restore-time resize may land before or
// after this activate depending on how the window came back, and if it
// lands first it would already have overwritten FWantMaximized.
LWant := FWantMaximized;
if FRestoring then Exit; // one correction per return
FRestoring := True;
LogLine('[winstate] activate: want_max=' + BoolToStr(LWant, True) +
' was_min=' + BoolToStr(FWasMinimized, True));
// Queue the correction so it runs AFTER FMX has finished restoring the
// window — applying it inline loses the race against FMX's own sizing.
TThread.ForceQueue(nil,
procedure
var
LHwnd: HWND;
begin
FRestoring := False;
FWasMinimized := False;
LHwnd := WindowHandleToPlatform(Self.Handle).Wnd; LHwnd := WindowHandleToPlatform(Self.Handle).Wnd;
if (LHwnd = 0) or IsIconic(LHwnd) then Exit; if (LHwnd = 0) or IsIconic(LHwnd) then Exit;
FWasMinimized := False; if LWant <> IsZoomed(LHwnd) then
if FWantMaximized = IsZoomed(LHwnd) then Exit; // already right begin
if FWantMaximized then ShowWindow(LHwnd, SW_SHOWMAXIMIZED) LogLine('[winstate] correcting to ' +
BoolToStr(LWant, True) + ' (was zoomed=' +
BoolToStr(IsZoomed(LHwnd), True) + ')');
if LWant then ShowWindow(LHwnd, SW_SHOWMAXIMIZED)
else ShowWindow(LHwnd, SW_RESTORE); else ShowWindow(LHwnd, SW_RESTORE);
end;
FWantMaximized := LWant; // our own ShowWindow fired a resize too
end);
end; end;
procedure TMainForm.AutofillTimerTick(Sender: TObject); procedure TMainForm.AutofillTimerTick(Sender: TObject);