fix(window): re-apply maximised after an external minimise (Win+D)

The earlier fix only covered the tray restore path, but Show desktop /
taskbar restore never runs our code at all — it is pure Windows + FMX, and
FMX brings the window back "normal" after forgetting it was maximised.

Track the state the user actually settled on (OnResize, ignoring wsMinimized
since that is the state we must survive) and re-apply it on OnActivate, which
fires however the user returns: taskbar, tray or Alt+Tab. Gated on
FWantMaximized so a window the user deliberately un-maximised is left alone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-25 19:58:27 +01:00
parent 6bed75f49e
commit 03a8a6f8a6
+34
View File
@@ -72,6 +72,13 @@ type
procedure btnToggleLogClick(Sender: TObject);
procedure btnReloadClick(Sender: TObject);
private
// Windows minimising the window from the outside (Win+D / Show desktop /
// taskbar) makes FMX forget it was maximised: the window comes back
// "normal". None of our restore paths run in that case — the taskbar
// restore is pure Windows + FMX — so the state is re-applied on activate,
// 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;
FServer: TPMHTTPServer;
FBridge: TPMBridge;
// Accumulates base64 chunks for large file saves (attachments too big
@@ -116,6 +123,8 @@ type
// needing two .fmx variants. Aligned to Client to fill the remaining
// space between PanelTop (top) and PanelLog/Splitter (bottom).
WebBrowser: TWebBrowserClass;
procedure FormActivateRestoreState(Sender: TObject);
procedure FormResizeTrackState(Sender: TObject);
procedure AutofillTimerTick(Sender: TObject);
// Shared by cmd://file/save (single-shot) and file/save-commit
// (chunked): decode the base64, show the Save dialog, write, and fire
@@ -162,6 +171,12 @@ function MaskAccessToken(const AUrl: string): string; forward;
procedure TMainForm.FormCreate(Sender: TObject);
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);
OnResize := FormResizeTrackState;
OnActivate := FormActivateRestoreState;
// Create the WebBrowser control programmatically so the {$IFDEF
// USE_EDGE_BROWSER} directive can swap the concrete class without a
// second .fmx variant. Align Client to fill the area below PanelTop
@@ -1488,6 +1503,25 @@ 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.
procedure TMainForm.FormResizeTrackState(Sender: TObject);
begin
if WindowState = TWindowState.wsMaximized then
FWantMaximized := True
else if WindowState = TWindowState.wsNormal then
FWantMaximized := False;
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.
procedure TMainForm.FormActivateRestoreState(Sender: TObject);
begin
if FWantMaximized and (WindowState = TWindowState.wsNormal) then
WindowState := TWindowState.wsMaximized;
end;
procedure TMainForm.AutofillTimerTick(Sender: TObject);
var
TargetHwnd: HWND;