From ee956ccbb6497b46a96efd20e64d1ef7cd5ce043 Mon Sep 17 00:00:00 2001 From: Jordan Russell Date: Mon, 16 Dec 2024 02:00:00 -0600 Subject: [PATCH] Unminimize before displaying message boxes and task dialogs. Now that MainFormOnTaskBar=True, Application.Restore doesn't steal the foreground anymore. --- Projects/Src/Setup.MainForm.pas | 26 -------------------------- Projects/Src/Setup.NewDiskForm.pas | 3 ++- Projects/Src/Shared.CommonFunc.Vcl.pas | 12 ++++++++++++ Projects/Src/Shared.TaskDialogFunc.pas | 1 + 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/Projects/Src/Setup.MainForm.pas b/Projects/Src/Setup.MainForm.pas index f3133f3e9..79e7098c2 100644 --- a/Projects/Src/Setup.MainForm.pas +++ b/Projects/Src/Setup.MainForm.pas @@ -26,7 +26,6 @@ TMainForm = class(TComponent) procedure Finish(const FromPreparingPage: Boolean); procedure InitializeWizard; function Install: Boolean; - procedure RestoreApp; procedure SetStep(const AStep: TSetupStep; const HandleExceptions: Boolean); class procedure ShowException(Sender: TObject; E: Exception); class procedure ShowExceptionMsg(const S: String); @@ -502,31 +501,6 @@ procedure TMainForm.Close; end; end; -procedure TMainForm.RestoreApp; -{ Restores the app if it is currently minimized, and tries to make its taskbar - button blink (by attempting to bring it to the foreground, which Windows - normally blocks). This should be called before displaying any dialogs that - aren't user-initiated (like NewDiskForm). } -begin - if IsIconic(Application.Handle) then begin - { If called alone, Application.Restore annoyingly brings WizardForm to the - foreground even if you're actively clicking/typing in the foreground - app. Evidently the SW_RESTORE command used by Application.Restore - bypasses Windows' usual foreground-stealing protections. However, if - we show WizardForm in advance (and leave the application window still - minimized), then SW_RESTORE doesn't bring WizardForm to the foreground - (not sure why). - Calling ShowWindow(Application.Handle, SW_SHOWNOACTIVATE) before - Application.Restore also works, but I worry that's relying on an - implementation detail: Application.Restore could be a no-op if it finds - the application window isn't minimized. (In fact, it used to be, until - the Forms unit added that fake IsIconic function.) } - //UpdateWizardFormVisibility(True); - Application.Restore; - end; - Application.BringToFront; -end; - class procedure TMainForm.AppOnGetActiveFormHandle(var AHandle: HWND); begin { IDE's TMainForm has this too; see comments there } diff --git a/Projects/Src/Setup.NewDiskForm.pas b/Projects/Src/Setup.NewDiskForm.pas index a979af0bc..79c473d8f 100644 --- a/Projects/Src/Setup.NewDiskForm.pas +++ b/Projects/Src/Setup.NewDiskForm.pas @@ -48,7 +48,8 @@ implementation function SelectDisk(const DiskNumber: Integer; const AFilename: String; var Path: String): Boolean; begin - MainForm.RestoreApp; + Application.Restore; { see comments in AppMessageBox } + Application.BringToFront; { usually just makes taskbar button blink } with TNewDiskForm.Create(Application) do try diff --git a/Projects/Src/Shared.CommonFunc.Vcl.pas b/Projects/Src/Shared.CommonFunc.Vcl.pas index e0f7a65d8..c01a41a95 100644 --- a/Projects/Src/Shared.CommonFunc.Vcl.pas +++ b/Projects/Src/Shared.CommonFunc.Vcl.pas @@ -212,6 +212,18 @@ function AppMessageBox(const Text, Caption: PChar; Flags: Longint): Integer; ActiveWindow: HWND; WindowList: Pointer; begin + { Always restore the app first if it's minimized. This makes sense from a + usability perspective (e.g., it may be unclear which app generated the + message box if it's shown by itself), but it's also a VCL bug mitigation + (seen on Delphi 11.3): + Without this, when Application.MainFormOnTaskBar=True, showing a window + like a message box causes a WM_ACTIVATEAPP message to be sent to + Application.Handle, and the VCL strangely responds by setting FAppIconic + to False -- even though the main form is still iconic (minimized). If we + later try to call Application.Restore, nothing happens because it sees + FAppIconic=False. } + Application.Restore; + { Always try to bring the message box to the foreground. Task dialogs appear to do that by default. Without this, if the main form is minimized and then closed via the diff --git a/Projects/Src/Shared.TaskDialogFunc.pas b/Projects/Src/Shared.TaskDialogFunc.pas index d4cf1b2e3..dd2cd1221 100644 --- a/Projects/Src/Shared.TaskDialogFunc.pas +++ b/Projects/Src/Shared.TaskDialogFunc.pas @@ -116,6 +116,7 @@ function TaskDialogMsgBox(const Icon, Instruction, Text, Caption: String; const NButtonLabelsAvailable: Integer; ButtonIDs: array of Integer; begin + Application.Restore; { See comments in AppMessageBox } if Icon <> '' then IconP := PChar(Icon) else begin