From 8204ccb316bad108186267fa711acc62a41a1bec Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:06:34 +0100 Subject: [PATCH] fix(window): stop forcing maximised on a window the user left normal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous version over-corrected: a normal window came back maximised after Win+D. Two causes, both fixed. - It read FMX's WindowState, which does not reliably follow a native un-maximise, so FWantMaximized never cleared. Now reads Win32 IsZoomed / IsIconic, which is the actual truth. - The restore fires its own resize, reporting whatever FMX decided — recording that erased the very intent we were preserving. FWasMinimized now gates the restore's resize out of the tracking. Activate then corrects both directions: re-maximise when FMX dropped a maximised window, restore when Windows brought a normal one back maximised. Degrades safely — if the minimise resize never fires, it simply does nothing. Co-Authored-By: Claude Opus 4.8 --- delphi-backend/UMainForm.pas | 47 +++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/delphi-backend/UMainForm.pas b/delphi-backend/UMainForm.pas index af5274c..ce19268 100644 --- a/delphi-backend/UMainForm.pas +++ b/delphi-backend/UMainForm.pas @@ -79,6 +79,9 @@ type // which happens however the user comes back. FWantMaximized mirrors the // user's LAST deliberate choice, so we never fight a normal-size window. FWantMaximized: Boolean; + // True between an external minimise and the following activate, so the + // restore's own resize is not mistaken for a user resize. + FWasMinimized: Boolean; FServer: TPMHTTPServer; FBridge: TPMBridge; // Accumulates base64 chunks for large file saves (attachments too big @@ -174,6 +177,7 @@ begin // The .fmx ships WindowState = wsMaximized, so that is the starting intent. // Wired here (not in the .fmx) to keep the designer file untouched. FWantMaximized := (WindowState = TWindowState.wsMaximized); + FWasMinimized := False; OnResize := FormResizeTrackState; OnActivate := FormActivateRestoreState; @@ -1503,23 +1507,44 @@ begin Application.Terminate; end; -// Remember the window state the USER settled on. Minimised is skipped on -// purpose: that is precisely the state we have to survive, not record. +// Remember the state the USER settled on. Reads Win32 (IsZoomed/IsIconic) +// rather than FMX's WindowState, which does not reliably follow a native +// un-maximise — that is why the first version never cleared the flag. +// +// The restore itself fires a resize too, and that one must NOT be recorded: +// it reports whatever FMX decided (often "normal"), which would erase the +// intent we are trying to preserve. FWasMinimized gates it out. procedure TMainForm.FormResizeTrackState(Sender: TObject); +var + LHwnd: HWND; begin - if WindowState = TWindowState.wsMaximized then - FWantMaximized := True - else if WindowState = TWindowState.wsNormal then - FWantMaximized := False; + LHwnd := WindowHandleToPlatform(Self.Handle).Wnd; + if LHwnd = 0 then Exit; + if IsIconic(LHwnd) then + begin + FWasMinimized := True; // the state we must survive, not record + Exit; + end; + if FWasMinimized then Exit; // this resize IS the restore + FWantMaximized := IsZoomed(LHwnd); // genuine user-driven resize end; -// Re-apply "maximised" when the window comes back from an external minimise. -// Runs however the user returns (taskbar, tray, Alt+Tab), because none of our -// own restore paths are involved in a plain taskbar click. +// Re-apply the remembered state once the window is back. Runs however the +// user returns (taskbar, tray, Alt+Tab) — a plain taskbar restore never goes +// through any of our own restore paths. Corrects BOTH directions: FMX drops +// a maximised window to normal, and Windows can restore a normal window +// maximised. procedure TMainForm.FormActivateRestoreState(Sender: TObject); +var + LHwnd: HWND; begin - if FWantMaximized and (WindowState = TWindowState.wsNormal) then - WindowState := TWindowState.wsMaximized; + if not FWasMinimized then Exit; + LHwnd := WindowHandleToPlatform(Self.Handle).Wnd; + if (LHwnd = 0) or IsIconic(LHwnd) then Exit; + FWasMinimized := False; + if FWantMaximized = IsZoomed(LHwnd) then Exit; // already right + if FWantMaximized then ShowWindow(LHwnd, SW_SHOWMAXIMIZED) + else ShowWindow(LHwnd, SW_RESTORE); end; procedure TMainForm.AutofillTimerTick(Sender: TObject);