fix(window): let an activate cancel the restore's own resize

The traces settled it: on a restore FMX fires its resize BEFORE the activate,
already reporting "not maximised" — so any snapshot taken at activate time was
too late. A genuine user resize, by contrast, is never followed by an activate
(the window is already active).

That is the discriminator. A resize no longer commits immediately: it queues,
and an activate arriving first cancels it, leaving the intent intact for the
correction to re-apply. Verified against every sequence in the log, including
the startup burst (three resizes then an activate), which must NOT be mistaken
for the user un-maximising.

Skips resizes that don't change the maximised state, so dragging the window
edge no longer floods the queue or the log.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-25 20:16:47 +01:00
parent ab12f6bf66
commit 28f0d1c5bc
+26 -8
View File
@@ -85,6 +85,8 @@ type
// Set from activate until the queued correction runs, so the restore's // Set from activate until the queued correction runs, so the restore's
// own resize is not mistaken for the user resizing the window. // own resize is not mistaken for the user resizing the window.
FRestoring: Boolean; FRestoring: Boolean;
// A resize awaiting confirmation that no activate follows it.
FResizePending, FPendingZoomed: 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
@@ -182,6 +184,7 @@ begin
FWantMaximized := (WindowState = TWindowState.wsMaximized); FWantMaximized := (WindowState = TWindowState.wsMaximized);
FWasMinimized := False; FWasMinimized := False;
FRestoring := False; FRestoring := False;
FResizePending := False;
OnResize := FormResizeTrackState; OnResize := FormResizeTrackState;
OnActivate := FormActivateRestoreState; OnActivate := FormActivateRestoreState;
@@ -1530,15 +1533,27 @@ begin
LogLine('[winstate] resize: minimised'); LogLine('[winstate] resize: minimised');
Exit; Exit;
end; end;
if FRestoring then // Observed order on a restore: FMX fires its resize BEFORE the activate, so
// committing here would destroy the intent we are about to need. A genuine
// user resize is never followed by an activate (the window is already
// active) — so defer the commit one message-pump turn and let an incoming
// activate cancel it.
// Nothing to learn when the maximised state is unchanged — and this is what
// keeps a mouse-drag resize (which fires continuously) from flooding the
// queue and the log.
if IsZoomed(LHwnd) = FWantMaximized then Exit;
FPendingZoomed := IsZoomed(LHwnd);
FResizePending := True;
LogLine('[winstate] resize pending: zoomed=' + BoolToStr(FPendingZoomed, True));
TThread.ForceQueue(nil,
procedure
begin begin
// The restore's own resize: reports whatever FMX decided, not intent. if not FResizePending then Exit; // an activate claimed it: a restore
LogLine('[winstate] resize during restore ignored (zoomed=' + FResizePending := False;
BoolToStr(IsZoomed(LHwnd), True) + ')'); FWantMaximized := FPendingZoomed;
Exit; LogLine('[winstate] resize committed: want_max=' +
end; BoolToStr(FWantMaximized, True));
FWantMaximized := IsZoomed(LHwnd); // genuine user-driven resize end);
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
@@ -1553,6 +1568,9 @@ begin
// Snapshot the intent NOW: FMX's restore-time resize may land before or // 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 // after this activate depending on how the window came back, and if it
// lands first it would already have overwritten FWantMaximized. // lands first it would already have overwritten FWantMaximized.
// Cancel a resize that has not committed yet: an activate right after one
// means this was a restore, not the user resizing the window.
FResizePending := False;
LWant := FWantMaximized; LWant := FWantMaximized;
if FRestoring then Exit; // one correction per return if FRestoring then Exit; // one correction per return
FRestoring := True; FRestoring := True;