fix(window): stop forcing maximised on a window the user left normal

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 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-25 20:06:34 +01:00
parent 03a8a6f8a6
commit 8204ccb316
+36 -11
View File
@@ -79,6 +79,9 @@ type
// which happens however the user comes back. FWantMaximized mirrors the // which happens however the user comes back. FWantMaximized mirrors the
// user's LAST deliberate choice, so we never fight a normal-size window. // user's LAST deliberate choice, so we never fight a normal-size window.
FWantMaximized: Boolean; 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; 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
@@ -174,6 +177,7 @@ begin
// The .fmx ships WindowState = wsMaximized, so that is the starting intent. // The .fmx ships WindowState = wsMaximized, so that is the starting intent.
// 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;
OnResize := FormResizeTrackState; OnResize := FormResizeTrackState;
OnActivate := FormActivateRestoreState; OnActivate := FormActivateRestoreState;
@@ -1503,23 +1507,44 @@ begin
Application.Terminate; Application.Terminate;
end; end;
// Remember the window state the USER settled on. Minimised is skipped on // Remember the state the USER settled on. Reads Win32 (IsZoomed/IsIconic)
// purpose: that is precisely the state we have to survive, not record. // 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); procedure TMainForm.FormResizeTrackState(Sender: TObject);
var
LHwnd: HWND;
begin begin
if WindowState = TWindowState.wsMaximized then LHwnd := WindowHandleToPlatform(Self.Handle).Wnd;
FWantMaximized := True if LHwnd = 0 then Exit;
else if WindowState = TWindowState.wsNormal then if IsIconic(LHwnd) then
FWantMaximized := False; 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; end;
// Re-apply "maximised" when the window comes back from an external minimise. // Re-apply the remembered state once the window is back. Runs however the
// Runs however the user returns (taskbar, tray, Alt+Tab), because none of our // user returns (taskbar, tray, Alt+Tab) — a plain taskbar restore never goes
// own restore paths are involved in a plain taskbar click. // 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); procedure TMainForm.FormActivateRestoreState(Sender: TObject);
var
LHwnd: HWND;
begin begin
if FWantMaximized and (WindowState = TWindowState.wsNormal) then if not FWasMinimized then Exit;
WindowState := TWindowState.wsMaximized; 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; end;
procedure TMainForm.AutofillTimerTick(Sender: TObject); procedure TMainForm.AutofillTimerTick(Sender: TObject);