From 28f0d1c5bc09e5e00466404988ac15cf486ccfba Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:16:47 +0100 Subject: [PATCH] fix(window): let an activate cancel the restore's own resize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- delphi-backend/UMainForm.pas | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/delphi-backend/UMainForm.pas b/delphi-backend/UMainForm.pas index 016e758..53ed562 100644 --- a/delphi-backend/UMainForm.pas +++ b/delphi-backend/UMainForm.pas @@ -85,6 +85,8 @@ type // 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; + // A resize awaiting confirmation that no activate follows it. + FResizePending, FPendingZoomed: Boolean; FServer: TPMHTTPServer; FBridge: TPMBridge; // Accumulates base64 chunks for large file saves (attachments too big @@ -182,6 +184,7 @@ begin FWantMaximized := (WindowState = TWindowState.wsMaximized); FWasMinimized := False; FRestoring := False; + FResizePending := False; OnResize := FormResizeTrackState; OnActivate := FormActivateRestoreState; @@ -1530,15 +1533,27 @@ begin 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; - end; - FWantMaximized := IsZoomed(LHwnd); // genuine user-driven resize - LogLine('[winstate] resize: want_max=' + BoolToStr(FWantMaximized, True)); + // 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 + if not FResizePending then Exit; // an activate claimed it: a restore + FResizePending := False; + FWantMaximized := FPendingZoomed; + LogLine('[winstate] resize committed: want_max=' + + BoolToStr(FWantMaximized, True)); + end); end; // 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 // after this activate depending on how the window came back, and if it // 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; if FRestoring then Exit; // one correction per return FRestoring := True;