Setup.SetupForm.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. unit Setup.SetupForm;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. TSetupForm
  8. Also used by UninstallProgressForm and UninstallSharedFileForm!
  9. Requires following globals to be set:
  10. -LangOptions.RightToLeft
  11. -LangOptions.DialogFontName
  12. -LangOptions.DialogFontSize
  13. -LangOptions.DialogFontBaseScaleWidth
  14. -LangOptions.DialogFontBaseScaleHeight
  15. -shWizardBorderStyled in SetupHeader.Options
  16. -shWizardKeepAspectRatio in SetupHeader.Options
  17. Also requires following globals to be set, but 0 is allowed:
  18. -SetupHeader.WizardSizePercentX
  19. -SetupHeader.WizardSizePercentY
  20. }
  21. interface
  22. uses
  23. Windows, SysUtils, Messages, Classes, Graphics, Controls, Forms, Dialogs,
  24. UIStateForm;
  25. type
  26. TSetupForm = class(TUIStateForm)
  27. private
  28. FOrigBaseUnitX, FOrigBaseUnitY: Integer;
  29. FBaseUnitX, FBaseUnitY: Integer;
  30. FRightToLeft: Boolean;
  31. FFlipControlsOnShow: Boolean;
  32. FCenterOnShow: Boolean;
  33. FControlsFlipped: Boolean;
  34. FKeepSizeX, FKeepSizeY: Boolean;
  35. FOrgClientWidthAfterScale, FOrgClientHeightAfterScale: Integer;
  36. FSetForeground: Boolean;
  37. procedure CMShowingChanged(var Message: TMessage); message CM_SHOWINGCHANGED;
  38. procedure WMQueryEndSession(var Message: TWMQueryEndSession); message WM_QUERYENDSESSION;
  39. protected
  40. procedure Center;
  41. procedure CenterInsideControl(const Ctl: TWinControl;
  42. const InsideClientArea: Boolean);
  43. procedure CenterInsideRect(const InsideRect: TRect);
  44. procedure CreateParams(var Params: TCreateParams); override;
  45. procedure CreateWnd; override;
  46. function GetExtraClientWidth: Integer;
  47. function GetExtraClientHeight: Integer;
  48. procedure FlipControlsIfNeeded;
  49. procedure CenterIfNeeded(const ACenterInsideControl: Boolean;
  50. const CenterInsideControlCtl: TWinControl;
  51. const CenterInsideControlInsideClientArea: Boolean);
  52. procedure VisibleChanging; override;
  53. procedure WndProc(var Message: TMessage); override;
  54. public
  55. constructor Create(AOwner: TComponent); override;
  56. constructor CreateNew(AOwner: TComponent; Dummy: Integer = 0); override;
  57. function CalculateButtonWidth(const ButtonCaptions: array of String): Integer;
  58. procedure InitializeFont(const KeepSizeX: Boolean = False; const KeepSizeY: Boolean = False);
  59. class function ScalePixelsX(const OrigBaseUnitX, BaseUnitX, N: Integer): Integer; overload;
  60. class function ScalePixelsY(const OrigBaseUnitY, BaseUnitY, N: Integer): Integer; overload;
  61. function ScalePixelsX(const N: Integer): Integer; overload;
  62. function ScalePixelsY(const N: Integer): Integer; overload;
  63. function ShouldSizeX: Boolean;
  64. function ShouldSizeY: Boolean;
  65. function ShowModal: Integer; override;
  66. procedure FlipAndCenterIfNeeded(const ACenterInsideControl: Boolean = False;
  67. const CenterInsideControlCtl: TWinControl = nil;
  68. const CenterInsideControlInsideClientArea: Boolean = False); virtual;
  69. property BaseUnitX: Integer read FBaseUnitX;
  70. published
  71. property CenterOnShow: Boolean read FCenterOnShow write FCenterOnShow;
  72. property ControlsFlipped: Boolean read FControlsFlipped;
  73. property ExtraClientWidth: Integer read GetExtraClientWidth;
  74. property ExtraClientHeight: Integer read GetExtraClientHeight;
  75. property FlipControlsOnShow: Boolean read FFlipControlsOnShow write FFlipControlsOnShow;
  76. property KeepSizeX: Boolean read FKeepSizeX;
  77. property KeepSizeY: Boolean read FKeepSizeY;
  78. property RightToLeft: Boolean read FRightToLeft;
  79. property SetForeground: Boolean read FSetForeground write FSetForeground;
  80. end;
  81. procedure CalculateBaseUnitsFromFont(const Font: TFont; var X, Y: Integer);
  82. function SetFontNameSize(const AFont: TFont; const AName: String;
  83. const ASize: Integer; const AFallbackName: String;
  84. const AFallbackSize: Integer): Boolean;
  85. implementation
  86. uses
  87. Generics.Collections, UITypes, WinXPanels,
  88. BidiUtils, NewNotebook,
  89. Shared.Struct, Shared.CommonFunc, Shared.CommonFunc.Vcl, Setup.MainFunc;
  90. var
  91. WM_QueryCancelAutoPlay: UINT;
  92. function GetRectOfPrimaryMonitor(const WorkArea: Boolean): TRect;
  93. begin
  94. if not WorkArea or
  95. not SystemParametersInfo(SPI_GETWORKAREA, 0, @Result, 0) then
  96. Result := Rect(0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
  97. end;
  98. function SetFontNameSize(const AFont: TFont; const AName: String;
  99. const ASize: Integer; const AFallbackName: String;
  100. const AFallbackSize: Integer): Boolean;
  101. { Returns True if AName <> '' and it used AName as the font name,
  102. False otherwise. }
  103. function SizeToHeight(const S: Integer): Integer;
  104. begin
  105. Result := MulDiv(-S, Screen.PixelsPerInch, 72);
  106. end;
  107. begin
  108. Result := False;
  109. if AName <> '' then begin
  110. if FontExists(AName) then begin
  111. AFont.Name := AName;
  112. AFont.Height := SizeToHeight(ASize);
  113. Result := True;
  114. Exit;
  115. end;
  116. { Note: AFallbackName is not used if the user specified an empty string for
  117. AName because in that case they want the default font used always }
  118. if (AFallbackName <> '') and FontExists(AFallbackName) then begin
  119. AFont.Name := AFallbackName;
  120. AFont.Height := SizeToHeight(AFallbackSize);
  121. Exit;
  122. end;
  123. end;
  124. AFont.Name := 'Segoe UI';
  125. AFont.Height := SizeToHeight(AFallbackSize);
  126. end;
  127. procedure CalculateBaseUnitsFromFont(const Font: TFont; var X, Y: Integer);
  128. var
  129. DC: HDC;
  130. Size: TSize;
  131. TM: TTextMetric;
  132. begin
  133. DC := GetDC(0);
  134. try
  135. SelectObject(DC, Font.Handle);
  136. { Based on code from Q145994: }
  137. GetTextExtentPoint(DC,
  138. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 52, Size);
  139. X := (Size.cx div 26 + 1) div 2;
  140. GetTextMetrics(DC, TM);
  141. Y := TM.tmHeight;
  142. finally
  143. ReleaseDC(0, DC);
  144. end;
  145. end;
  146. function IsParentSetupFormFlipped(AControl: TControl): Boolean;
  147. function GetParentSetupForm(AControl: TControl): TSetupForm;
  148. begin
  149. { Note: Unlike GetParentForm, this checks all levels, not just the top }
  150. repeat
  151. if AControl is TSetupForm then begin
  152. Result := TSetupForm(AControl);
  153. Exit;
  154. end;
  155. AControl := AControl.Parent;
  156. until AControl = nil;
  157. Result := nil;
  158. end;
  159. var
  160. ParentForm: TSetupForm;
  161. begin
  162. ParentForm := GetParentSetupForm(AControl);
  163. if Assigned(ParentForm) then
  164. Result := ParentForm.ControlsFlipped
  165. else
  166. Result := False;
  167. end;
  168. function GetPPI(const Wnd: HWND): Integer;
  169. begin
  170. { Based on TSysStyleHook.GetCurrentPPI }
  171. if CheckPerMonitorV2SupportForWindow(Wnd) then begin { Currently always False in Setup }
  172. { GetDPIForWindow requires Windows 10 version 1607. However, because it is delay-loaded and it's
  173. never executed on older versions of Windows, it does not cause entry point not found errors. }
  174. Result := GetDPIForWindow(Wnd)
  175. end else
  176. Result := Screen.PixelsPerInch;
  177. end;
  178. { TSetupForm }
  179. constructor TSetupForm.Create(AOwner: TComponent);
  180. begin
  181. { Must initialize FRightToLeft here in addition to CreateNew because
  182. CreateNew isn't virtual on Delphi 2 and 3 }
  183. FRightToLeft := LangOptions.RightToLeft;
  184. FFlipControlsOnShow := FRightToLeft;
  185. FCenterOnShow := True;
  186. inherited;
  187. { Setting BidiMode before inherited causes an AV when TControl tries to
  188. send CM_BIDIMODECHANGED. This is why we have additonal RTL code in
  189. CreateParams below. }
  190. if FRightToLeft then
  191. BiDiMode := bdRightToLeft;
  192. { In Delphi 2005 and later, Position defaults to poDefaultPosOnly, but we
  193. don't want the form to be changing positions whenever its handle is
  194. recreated, so change it to the D7 and earlier default of poDesigned. }
  195. if Position = poDefaultPosOnly then
  196. Position := poDesigned;
  197. end;
  198. constructor TSetupForm.CreateNew(AOwner: TComponent; Dummy: Integer = 0);
  199. begin
  200. { Note: On Delphi 2 and 3, CreateNew isn't virtual, so this is only reached
  201. when TSetupForm.CreateNew is called explicitly }
  202. FRightToLeft := LangOptions.RightToLeft;
  203. FFlipControlsOnShow := FRightToLeft;
  204. FCenterOnShow := True;
  205. inherited;
  206. if FRightToLeft then
  207. BiDiMode := bdRightToLeft;
  208. end;
  209. function TSetupForm.CalculateButtonWidth(const ButtonCaptions: array of String): Integer;
  210. var
  211. DC: HDC;
  212. I, W: Integer;
  213. begin
  214. Result := ScalePixelsX(75);
  215. { Increase the button size if there are unusually long button captions }
  216. DC := GetDC(0);
  217. try
  218. SelectObject(DC, Font.Handle);
  219. for I := Low(ButtonCaptions) to High(ButtonCaptions) do begin
  220. W := GetTextWidth(DC, ButtonCaptions[I], True) + ScalePixelsX(20);
  221. if Result < W then
  222. Result := W;
  223. end;
  224. finally
  225. ReleaseDC(0, DC);
  226. end;
  227. end;
  228. procedure TSetupForm.CenterInsideControl(const Ctl: TWinControl;
  229. const InsideClientArea: Boolean);
  230. var
  231. R: TRect;
  232. begin
  233. const CtlForm = GetParentForm(Ctl);
  234. if (CtlForm = nil) or not IsWindowVisible(CtlForm.Handle) or
  235. IsIconic(CtlForm.Handle) then begin
  236. Center;
  237. Exit;
  238. end;
  239. if not InsideClientArea then begin
  240. if GetWindowRect(Ctl.Handle, R) then
  241. CenterInsideRect(R);
  242. end
  243. else begin
  244. R := Ctl.ClientRect;
  245. MapWindowPoints(Ctl.Handle, 0, R, 2);
  246. CenterInsideRect(R);
  247. end;
  248. end;
  249. procedure TSetupForm.CenterInsideRect(const InsideRect: TRect);
  250. function GetRectOfMonitorContainingRect(const R: TRect): TRect;
  251. { Returns bounding rectangle of monitor containing or nearest to R }
  252. type
  253. HMONITOR = type THandle;
  254. TMonitorInfo = record
  255. cbSize: DWORD;
  256. rcMonitor: TRect;
  257. rcWork: TRect;
  258. dwFlags: DWORD;
  259. end;
  260. const
  261. MONITOR_DEFAULTTONEAREST = $00000002;
  262. var
  263. Module: HMODULE;
  264. MonitorFromRect: function(const lprc: TRect; dwFlags: DWORD): HMONITOR; stdcall;
  265. GetMonitorInfo: function(hMonitor: HMONITOR; var lpmi: TMonitorInfo): BOOL; stdcall;
  266. M: HMONITOR;
  267. Info: TMonitorInfo;
  268. begin
  269. Module := GetModuleHandle(user32);
  270. MonitorFromRect := GetProcAddress(Module, 'MonitorFromRect');
  271. GetMonitorInfo := GetProcAddress(Module, 'GetMonitorInfoA');
  272. if Assigned(MonitorFromRect) and Assigned(GetMonitorInfo) then begin
  273. M := MonitorFromRect(R, MONITOR_DEFAULTTONEAREST);
  274. Info.cbSize := SizeOf(Info);
  275. if GetMonitorInfo(M, Info) then begin
  276. Result := Info.rcWork;
  277. Exit;
  278. end;
  279. end;
  280. Result := GetRectOfPrimaryMonitor(True);
  281. end;
  282. var
  283. R, MR: TRect;
  284. begin
  285. R := Bounds(InsideRect.Left + ((InsideRect.Right - InsideRect.Left) - Width) div 2,
  286. InsideRect.Top + ((InsideRect.Bottom - InsideRect.Top) - Height) div 2,
  287. Width, Height);
  288. { Clip to nearest monitor }
  289. MR := GetRectOfMonitorContainingRect(R);
  290. if R.Right > MR.Right then
  291. OffsetRect(R, MR.Right - R.Right, 0);
  292. if R.Bottom > MR.Bottom then
  293. OffsetRect(R, 0, MR.Bottom - R.Bottom);
  294. if R.Left < MR.Left then
  295. OffsetRect(R, MR.Left - R.Left, 0);
  296. if R.Top < MR.Top then
  297. OffsetRect(R, 0, MR.Top - R.Top);
  298. BoundsRect := R;
  299. end;
  300. procedure TSetupForm.Center;
  301. begin
  302. CenterInsideRect(GetRectOfPrimaryMonitor(True));
  303. end;
  304. procedure TSetupForm.CreateParams(var Params: TCreateParams);
  305. begin
  306. inherited;
  307. { With Application.MainFormOnTaskBar=True, by default, a form won't get a
  308. taskbar button if the main form hasn't been created yet (due to the owner
  309. being an invisible Application.Handle), or if the main form exists but
  310. isn't visible (e.g., because it's a silent install). Force it to have a
  311. taskbar button in those cases by specifying no owner for the window.
  312. (Another method is to set WS_EX_APPWINDOW and leave WndParent set to
  313. Application.Handle, but it doesn't quite work correctly: if the form
  314. displays a message box, and you activate another app's window, clicking on
  315. the form's taskbar button activates the message box again, but the taskbar
  316. button doesn't change to a "selected" state.) }
  317. if (Params.WndParent <> 0) and
  318. (Application.MainFormOnTaskBar or (Params.WndParent <> Application.Handle)) and
  319. not IsWindowOnTaskbar(Params.WndParent) then
  320. Params.WndParent := 0;
  321. { See comment in Create. Also: The following does not make the title bar RTL.
  322. Achieving this requires adding WS_EX_LAYOUTRTL, which VCL does not support. }
  323. if FRightToLeft then
  324. Params.ExStyle := Params.ExStyle or (WS_EX_RTLREADING or WS_EX_LEFTSCROLLBAR or WS_EX_RIGHT);
  325. end;
  326. procedure TSetupForm.CreateWnd;
  327. procedure SetControlsCurrentPPI(const Ctl: TWinControl; const PPI: Integer);
  328. begin
  329. for var I := 0 to Ctl.ControlCount-1 do begin
  330. const C = Ctl.Controls[I];
  331. if C is TWinControl then begin
  332. SetControlsCurrentPPI(TWinControl(C), PPI);
  333. C.SetCurrentPPI(PPI);
  334. end else
  335. C.SetCurrentPPI(PPI)
  336. end;
  337. end;
  338. begin
  339. inherited;
  340. if WM_QueryCancelAutoPlay <> 0 then
  341. AddToWindowMessageFilterEx(Handle, WM_QueryCancelAutoPlay);
  342. if not (shWizardBorderStyled in SetupHeader.Options) then begin
  343. { SetDarkTitleBar also removes seBorder which disables styling of the titlebar and the border.
  344. Note that removing seBorder in Create causes a small bit of space to the right of bevels for
  345. some reason. Doing it here does not cause this problem. It's also here because SetDarkTitleBar
  346. requires the handle of the form. }
  347. SetDarkTitleBar(Self, IsDarkInstallMode);
  348. { SetDarkTitleBar is a noop on older versions of Windows }
  349. if seBorder in StyleElements then
  350. StyleElements := StyleElements - [seBorder];
  351. end;
  352. { We don't use the Scaled property for scaling and this means the CurrentPPI property will not be
  353. set correctly. This causes problems when VCL code inspects it, for example in THintWindow.CalcHintRect
  354. and FormStyleHook.GetBorderSize. So we should update it ourselves by directly writing to the
  355. FCurrentPPI private variable of the form and all controls on it, which we can do using a class
  356. helper. Note: Doing it later for the form causes issues with incorrect non-client vs. client size
  357. when styled title bars are enabled. }
  358. const PPI = GetPPI(Handle);
  359. SetCurrentPPI(PPI);
  360. SetControlsCurrentPPI(Self, PPI);
  361. { Now that CurrentPPI of the form is set you must make sure that any controls you later parent to
  362. the form already have the same CurrentPPI, otherwise VCL will scale the controls. Currently this
  363. is done in:
  364. -Setup.ScriptClasses's TControlParentW and TNewNotebookPageNotebook_W
  365. -Setup.ScriptDlg's SetCtlParent
  366. -TWizardForm.AddPage
  367. To debug/detect scaling add a breakpoint in Vcl.Controls' TWinControl.ChangeScale and set project
  368. option Building->Delphi Compiler->Compiling->Debugging->Use debug .dcus. }
  369. end;
  370. procedure TSetupForm.FlipControlsIfNeeded;
  371. begin
  372. if FFlipControlsOnShow then begin
  373. FFlipControlsOnShow := False;
  374. FControlsFlipped := not FControlsFlipped;
  375. FlipControls(Self);
  376. end;
  377. end;
  378. procedure TSetupForm.CenterIfNeeded(const ACenterInsideControl: Boolean; const CenterInsideControlCtl: TWinControl; const CenterInsideControlInsideClientArea: Boolean);
  379. begin
  380. if FCenterOnShow then begin
  381. FCenterOnShow := False;
  382. { Center }
  383. if ACenterInsideControl then
  384. CenterInsideControl(CenterInsideControlCtl, CenterInsideControlInsideClientArea)
  385. else
  386. Center;
  387. end;
  388. end;
  389. function TSetupForm.ShouldSizeX: Boolean;
  390. begin
  391. Result := not FKeepSizeX and (SetupHeader.WizardSizePercentX > 100);
  392. end;
  393. function TSetupForm.ShouldSizeY: Boolean;
  394. begin
  395. Result := not FKeepSizeY and (SetupHeader.WizardSizePercentY > 100);
  396. end;
  397. procedure TSetupForm.FlipAndCenterIfNeeded(const ACenterInsideControl: Boolean;
  398. const CenterInsideControlCtl: TWinControl; const CenterInsideControlInsideClientArea: Boolean);
  399. begin
  400. FlipControlsIfNeeded;
  401. CenterIfNeeded(ACenterInsideControl, CenterInsideControlCtl, CenterInsideControlInsideClientArea);
  402. end;
  403. type
  404. TControlAccess = class(TControl);
  405. procedure TSetupForm.InitializeFont(const KeepSizeX, KeepSizeY: Boolean);
  406. procedure NewChangeScale(const Ctl: TControl; const XM, XD, YM, YD: Integer);
  407. var
  408. X, Y, W, H: Integer;
  409. begin
  410. X := MulDiv(Ctl.Left, XM, XD);
  411. Y := MulDiv(Ctl.Top, YM, YD);
  412. if not(csFixedWidth in Ctl.ControlStyle) then
  413. W := MulDiv(Ctl.Width, XM, XD)
  414. else
  415. W := Ctl.Width;
  416. if not(csFixedHeight in Ctl.ControlStyle) then
  417. H := MulDiv(Ctl.Height, YM, YD)
  418. else
  419. H := Ctl.Height;
  420. Ctl.SetBounds(X, Y, W, H);
  421. end;
  422. procedure NewScaleControls(const Ctl: TWinControl; const XM, XD, YM, YD: Integer);
  423. { This is like TControl.ScaleControls, except it allows the width and height
  424. to be scaled independently }
  425. var
  426. I: Integer;
  427. C: TControl;
  428. begin
  429. for I := 0 to Ctl.ControlCount-1 do begin
  430. C := Ctl.Controls[I];
  431. if C is TWinControl then begin
  432. TWinControl(C).DisableAlign;
  433. try
  434. NewScaleControls(TWinControl(C), XM, XD, YM, YD);
  435. NewChangeScale(C, XM, XD, YM, YD);
  436. finally
  437. TWinControl(C).EnableAlign;
  438. end;
  439. end
  440. else
  441. NewChangeScale(C, XM, XD, YM, YD);
  442. end;
  443. end;
  444. type
  445. TControlAnchorsList = TDictionary<TControl, TAnchors>;
  446. procedure StripAndStoreChildControlCustomAnchors(const ParentCtl: TControl; const AnchorsList: TControlAnchorsList);
  447. begin
  448. if ParentCtl is TWinControl then begin
  449. const ParentWinCtl = TWinControl(ParentCtl);
  450. for var I := 0 to ParentWinCtl.ControlCount-1 do begin
  451. const Ctl = ParentWinCtl.Controls[I];
  452. if Ctl.Anchors <> [akLeft, akTop] then begin
  453. AnchorsList.Add(Ctl, Ctl.Anchors);
  454. { Before we can set Anchors to [akLeft, akTop] (which has a special
  455. 'no anchors' meaning to VCL), we first need to update the Explicit*
  456. properties so the control doesn't get moved back to an old position }
  457. TControlAccess(Ctl).UpdateExplicitBounds;
  458. Ctl.Anchors := [akLeft, akTop];
  459. end;
  460. StripAndStoreChildControlCustomAnchors(Ctl, AnchorsList);
  461. end;
  462. end;
  463. end;
  464. procedure RestoreAnchors(const AnchorsList: TControlAnchorsList);
  465. begin
  466. { The order in which we restore the anchors shouldn't matter, so just
  467. enumerate the list }
  468. for var Item in AnchorsList do
  469. Item.Key.Anchors := Item.Value;
  470. end;
  471. function ExcludeFromParentHandlesNeeded(const ParentCtl: TWinControl): Boolean;
  472. begin
  473. { Right-aligned TStackPanels are excluded. For example, calling
  474. HandleNeeded on TaskDialogForm's BottomStackPanel causes it to become
  475. left-aligned instead of right-aligned. This occurs regardless of the
  476. timing of the HandleNeeded call, such as before or after sizing. }
  477. Result := (ParentCtl is TStackPanel) and (TStackPanel(ParentCtl).Align = alRight);
  478. end;
  479. procedure ParentHandlesNeeded(const ParentCtl: TControl);
  480. begin
  481. if ParentCtl is TWinControl then begin
  482. const ParentWinCtl = TWinControl(ParentCtl);
  483. if (ParentWinCtl.ControlCount > 0) and not ExcludeFromParentHandlesNeeded(ParentWinCtl) then begin
  484. if not (ParentWinCtl is TNewNotebook) then { For notebooks: only need handles on pages }
  485. ParentWinCtl.HandleNeeded;
  486. for var I := 0 to ParentWinCtl.ControlCount-1 do
  487. ParentHandlesNeeded(ParentWinCtl.Controls[I]);
  488. end;
  489. end;
  490. end;
  491. begin
  492. { Create parent handles.
  493. Various things related to positioning and anchoring don't work without this:
  494. you get positions of child controls back as if there was no anchoring until
  495. handles are automatically created.
  496. Initially we did this only when sizing the form (for WizardForm it worked if
  497. done after sizing but for UninstallProgressForm it had be done before sizing,
  498. for unknown reasons).
  499. For WizardForm's BeveledLabel though, it needs it before the font name/size
  500. change (again for unknown reasons), otherwise the label will end up in the
  501. wrong position, even if all we do is changing the font. Setting AutoSize to
  502. False also causes it to stay in the correct position. (To see the bad
  503. positioning for WizardForm.BeveledLabel you would first have to disable next
  504. ParentHandlesNeeded call and then also the automatic vertical recentering in
  505. WizardForm.)
  506. Doing it always, instead of only before or after sizing, also helps
  507. TaskDialogForm which does its own sizing (so KeepSizeX and KeepSizeY are
  508. both True), but still needs parent handles to be created to avoid the issue.
  509. Note: Caller should make sure the created handles do not get lost again. For
  510. example, setting StyleElements to [] on a parent would cause the handle to
  511. get deallocated again, and would reintroduce the issue. So this must be done
  512. before calling us, and not after. }
  513. ParentHandlesNeeded(Self); { Also see ShowModal }
  514. { Set font. Note: Must keep the following lines in synch with Setup.ScriptFunc.pas's
  515. InitializeScaleBaseUnits }
  516. SetFontNameSize(Font, LangOptions.DialogFontName, LangOptions.DialogFontSize, '', 9);
  517. CalculateBaseUnitsFromFont(Font, FBaseUnitX, FBaseUnitY);
  518. FOrigBaseUnitX := LangOptions.DialogFontBaseScaleWidth;
  519. FOrigBaseUnitY := LangOptions.DialogFontBaseScaleHeight;
  520. if shWizardKeepAspectRatio in SetupHeader.Options then begin
  521. if FBaseUnitX * FOrigBaseUnitY > FBaseUnitY * FOrigBaseUnitX then begin
  522. FBaseUnitY := FBaseUnitX;
  523. FOrigBaseUnitY := FOrigBaseUnitX;
  524. end else begin
  525. FBaseUnitX := FBaseUnitY;
  526. FOrigBaseUnitX := FOrigBaseUnitY;
  527. end;
  528. end;
  529. { Scale }
  530. if (FBaseUnitX <> FOrigBaseUnitX) or (FBaseUnitY <> FOrigBaseUnitY) then begin
  531. const ControlAnchorsList = TControlAnchorsList.Create;
  532. try
  533. { Custom anchors interfere with our scaling code, so strip them and restore
  534. afterward }
  535. StripAndStoreChildControlCustomAnchors(Self, ControlAnchorsList);
  536. { Loosely based on scaling code from TForm.ReadState: }
  537. NewScaleControls(Self, FBaseUnitX, FOrigBaseUnitX, FBaseUnitY, FOrigBaseUnitY);
  538. const R = ClientRect;
  539. const W = MulDiv(R.Right, FBaseUnitX, FOrigBaseUnitX);
  540. const H = MulDiv(R.Bottom, FBaseUnitY, FOrigBaseUnitY);
  541. SetBounds(Left, Top, W + (Width - R.Right), H + (Height - R.Bottom));
  542. finally
  543. RestoreAnchors(ControlAnchorsList);
  544. ControlAnchorsList.Free;
  545. end;
  546. end;
  547. { Size }
  548. FKeepSizeX := KeepSizeX;
  549. FKeepSizeY := KeepSizeY;
  550. FOrgClientWidthAfterScale := ClientWidth;
  551. FOrgClientHeightAfterScale := ClientHeight;
  552. const LShouldSizeX = ShouldSizeX;
  553. const LShouldSizeY = ShouldSizeY;
  554. if LShouldSizeX then
  555. ClientWidth := MulDiv(ClientWidth, SetupHeader.WizardSizePercentX, 100);
  556. if LShouldSizeY then
  557. ClientHeight := MulDiv(ClientHeight, SetupHeader.WizardSizePercentY, 100);
  558. end;
  559. function TSetupForm.GetExtraClientWidth: Integer;
  560. begin
  561. Result := ClientWidth - FOrgClientWidthAfterScale;
  562. end;
  563. function TSetupForm.GetExtraClientHeight: Integer;
  564. begin
  565. Result := ClientHeight - FOrgClientHeightAfterScale;
  566. end;
  567. class function TSetupForm.ScalePixelsX(const OrigBaseUnitX, BaseUnitX, N: Integer): Integer;
  568. begin
  569. Result := MulDiv(N, BaseUnitX, OrigBaseUnitX);
  570. end;
  571. function TSetupForm.ScalePixelsX(const N: Integer): Integer;
  572. begin
  573. Result := ScalePixelsX(FOrigBaseUnitX, FBaseUnitX, N);
  574. end;
  575. class function TSetupForm.ScalePixelsY(const OrigBaseUnitY, BaseUnitY, N: Integer): Integer;
  576. begin
  577. Result := MulDiv(N, BaseUnitY, OrigBaseUnitY);
  578. end;
  579. function TSetupForm.ScalePixelsY(const N: Integer): Integer;
  580. begin
  581. Result := ScalePixelsY(FOrigBaseUnitY, FBaseUnitY, N);
  582. end;
  583. function TSetupForm.ShowModal: Integer;
  584. begin
  585. { Work around VCL issue (Delphi 11.3): ShowModal calls DisableTaskWindows
  586. without ensuring the form's handle has been created first. If the handle
  587. is created after DisableTaskWindows, PopupMode=pmAuto breaks;
  588. TCustomForm.CreateParams finds that the active window is disabled, and
  589. doesn't use it as the owner. It then falls back to pmNone behavior, which
  590. is to use the main form or application window as the owner. }
  591. HandleNeeded; { Also see InitializeFont }
  592. Result := inherited;
  593. end;
  594. procedure TSetupForm.VisibleChanging;
  595. begin
  596. inherited;
  597. { Note: Unlike DoShow, any exceptions raised in VisibleChanging will be
  598. propagated out, which is what we want }
  599. if not Visible then
  600. FlipAndCenterIfNeeded;
  601. end;
  602. procedure TSetupForm.CMShowingChanged(var Message: TMessage);
  603. begin
  604. inherited;
  605. { This usually just makes the taskbar button flash }
  606. if FSetForeground and Showing then
  607. SetForegroundWindow(Handle);
  608. end;
  609. procedure TSetupForm.WMQueryEndSession(var Message: TWMQueryEndSession);
  610. begin
  611. { TDummyClass.AntiShutdownHook in Setup.dpr already denies shutdown attempts
  612. but we also need to catch WM_QUERYENDSESSION here to suppress the VCL's
  613. default handling which calls CloseQuery. We do not want to let TMainForm &
  614. TNewDiskForm display any 'Exit Setup?' message boxes since we're already
  615. denying shutdown attempts, and also we can't allow them to potentially be
  616. displayed on top of another dialog box that's already displayed. }
  617. { Return zero, except if RestartInitiatedByThisProcess is set (which means
  618. we called RestartComputer previously) }
  619. if RestartInitiatedByThisProcess then
  620. Message.Result := 1;
  621. end;
  622. procedure TSetupForm.WndProc(var Message: TMessage);
  623. begin
  624. { When we receive a 'QueryCancelAutoPlay' message as a result of a new CD
  625. being inserted, return 1 to prevent it from being 'autoplayed'.
  626. Note: According to the docs, this message is only sent on Shell version
  627. 4.70 and later. }
  628. if (WM_QueryCancelAutoPlay <> 0) and (Message.Msg = WM_QueryCancelAutoPlay) then
  629. Message.Result := 1
  630. else
  631. inherited;
  632. end;
  633. initialization
  634. BidiUtils.IsParentFlippedFunc := IsParentSetupFormFlipped;
  635. WM_QueryCancelAutoPlay := RegisterWindowMessage('QueryCancelAutoPlay');
  636. end.