diff --git a/delphi-backend/UMainForm.pas b/delphi-backend/UMainForm.pas index fc4d6a8..488d696 100644 --- a/delphi-backend/UMainForm.pas +++ b/delphi-backend/UMainForm.pas @@ -1520,24 +1520,27 @@ begin if LHwnd = 0 then Exit; if IsIconic(LHwnd) then Exit; // nothing to learn while minimised - // OnActivate turned out to be useless as a "the window came back" signal — - // it also fires on plain focus churn with the WebView2 child, and even right - // after the user clicks Maximize. Ask Windows instead: WPF_RESTORETOMAXIMIZED - // means "this window is on its way back from minimised and belongs - // maximised", which is exactly the resize we must not mistake for the user - // un-maximising. - FillChar(LPlace, SizeOf(LPlace), 0); + // Windows offers no usable "this resize is a restore" signal: the log showed + // WPF_RESTORETOMAXIMIZED (flags=2) is set for ANY maximised window, and + // showCmd is already 1/3 by then — testing those classified the user's own + // Maximize click as a restore and dropped the intent. + // + // What does separate them: a user gesture happens while the window is the + // FOREGROUND window. FMX's re-layout during a restore (and during startup) + // runs before the window owns the foreground. + LIsRestore := GetForegroundWindow <> LHwnd; + FillChar(LPlace, SizeOf(LPlace), 0); // diagnostics only LPlace.length := SizeOf(LPlace); - LIsRestore := GetWindowPlacement(LHwnd, @LPlace) and - (((LPlace.flags and WPF_RESTORETOMAXIMIZED) <> 0) or - (LPlace.showCmd = SW_SHOWMINIMIZED)); - LogLine(Format('[winstate] resize zoomed=%s showCmd=%d flags=%d restore=%s want=%s', + GetWindowPlacement(LHwnd, @LPlace); + LogLine(Format('[winstate] resize zoomed=%s showCmd=%d flags=%d fg=%s want=%s', [BoolToStr(IsZoomed(LHwnd), True), LPlace.showCmd, LPlace.flags, - BoolToStr(LIsRestore, True), BoolToStr(FWantMaximized, True)])); + BoolToStr(not LIsRestore, True), BoolToStr(FWantMaximized, True)])); if LIsRestore then begin - // Re-apply after FMX has finished sizing; doing it inline loses the race. + // Not a user gesture — never record it as intent. If it dropped a window + // the user had left maximised, put it back (queued: inline loses the race + // against FMX's own sizing). if FWantMaximized and (not IsZoomed(LHwnd)) and (not FRestoring) then begin FRestoring := True; @@ -1552,7 +1555,7 @@ begin ShowWindow(LH, SW_SHOWMAXIMIZED); end); end; - Exit; // never record a restore as intent + Exit; end; if FRestoring then Exit; // our own ShowWindow's resize FWantMaximized := IsZoomed(LHwnd); // genuine user gesture